Merge "add settings for dock audio enabled" into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index 31ed755..e26d8f0 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -10093,8 +10093,10 @@
   public final class DisplayManager {
     method public android.view.Display getDisplay(int);
     method public android.view.Display[] getDisplays();
+    method public android.view.Display[] getDisplays(java.lang.String);
     method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -11768,6 +11770,7 @@
     method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
+    method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup);
@@ -11802,6 +11805,7 @@
     method public java.lang.CharSequence getName(android.content.Context);
     method public int getPlaybackStream();
     method public int getPlaybackType();
+    method public android.view.Display getPresentationDisplay();
     method public java.lang.CharSequence getStatus();
     method public int getSupportedTypes();
     method public java.lang.Object getTag();
@@ -16619,6 +16623,7 @@
 
   public class UserManager {
     method public long getSerialNumberForUser(android.os.UserHandle);
+    method public int getUserCount();
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public boolean isUserAGoat();
@@ -23742,6 +23747,7 @@
     method public deprecated int getWidth();
     method public boolean isValid();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_SECURE = 2; // 0x2
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
   }
 
@@ -24788,6 +24794,7 @@
     ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int);
     method public boolean gatherTransparentRegion(android.graphics.Region);
     method public android.view.SurfaceHolder getHolder();
+    method public void setSecure(boolean);
     method public void setZOrderMediaOverlay(boolean);
     method public void setZOrderOnTop(boolean);
   }
@@ -25362,7 +25369,6 @@
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
     field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
     field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
-    field public static int TEXT_ALIGNMENT_DEFAULT;
     field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
     field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
     field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
@@ -25370,7 +25376,6 @@
     field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
     field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
     field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
-    field public static int TEXT_DIRECTION_DEFAULT;
     field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
     field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
     field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
diff --git a/api/current.txt b/api/current.txt
index 31ed755..e26d8f0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10093,8 +10093,10 @@
   public final class DisplayManager {
     method public android.view.Display getDisplay(int);
     method public android.view.Display[] getDisplays();
+    method public android.view.Display[] getDisplays(java.lang.String);
     method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+    field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
   }
 
   public static abstract interface DisplayManager.DisplayListener {
@@ -11768,6 +11770,7 @@
     method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
+    method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup);
@@ -11802,6 +11805,7 @@
     method public java.lang.CharSequence getName(android.content.Context);
     method public int getPlaybackStream();
     method public int getPlaybackType();
+    method public android.view.Display getPresentationDisplay();
     method public java.lang.CharSequence getStatus();
     method public int getSupportedTypes();
     method public java.lang.Object getTag();
@@ -16619,6 +16623,7 @@
 
   public class UserManager {
     method public long getSerialNumberForUser(android.os.UserHandle);
+    method public int getUserCount();
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public boolean isUserAGoat();
@@ -23742,6 +23747,7 @@
     method public deprecated int getWidth();
     method public boolean isValid();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
+    field public static final int FLAG_SECURE = 2; // 0x2
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
   }
 
@@ -24788,6 +24794,7 @@
     ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int);
     method public boolean gatherTransparentRegion(android.graphics.Region);
     method public android.view.SurfaceHolder getHolder();
+    method public void setSecure(boolean);
     method public void setZOrderMediaOverlay(boolean);
     method public void setZOrderOnTop(boolean);
   }
@@ -25362,7 +25369,6 @@
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
     field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
     field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
-    field public static int TEXT_ALIGNMENT_DEFAULT;
     field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
     field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
     field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
@@ -25370,7 +25376,6 @@
     field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
     field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
     field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
-    field public static int TEXT_DIRECTION_DEFAULT;
     field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
     field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
     field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
index 20b27c5..3e8af60 100644
--- a/core/java/android/app/Presentation.java
+++ b/core/java/android/app/Presentation.java
@@ -50,6 +50,93 @@
  * whenever the activity itself is paused or resumed.
  * </p>
  *
+ * <h3>Choosing a presentation display</h3>
+ * <p>
+ * Before showing a {@link Presentation} it's important to choose the {@link Display}
+ * on which it will appear.  Choosing a presentation display is sometimes difficult
+ * because there may be multiple displays attached.  Rather than trying to guess
+ * which display is best, an application should let the system choose a suitable
+ * presentation display.
+ * </p><p>
+ * There are two main ways to choose a {@link Display}.
+ * </p>
+ *
+ * <h4>Using the media router to choose a presentation display</h4>
+ * <p>
+ * The easiest way to choose a presentation display is to use the
+ * {@link android.media.MediaRouter MediaRouter} API.  The media router service keeps
+ * track of which audio and video routes are available on the system.
+ * The media router sends notifications whenever routes are selected or unselected
+ * or when the preferred presentation display of a route changes.
+ * So an application can simply watch for these notifications and show or dismiss
+ * a presentation on the preferred presentation display automatically.
+ * </p><p>
+ * The preferred presentation display is the display that the media router recommends
+ * that the application should use if it wants to show content on the secondary display.
+ * Sometimes there may not be a preferred presentation display in which
+ * case the application should show its content locally without using a presentation.
+ * </p><p>
+ * Here's how to use the media router to create and show a presentation on the preferred
+ * presentation display using {@link android.media.MediaRouter.RouteInfo#getPresentationDisplay()}.
+ * </p>
+ * {@samplecode
+ * MediaRouter mediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
+ * if (route != null) $&#123;
+ *     Display presentationDisplay = route.getPresentationDisplay();
+ *     if (presentationDisplay != null) $&#123;
+ *         Presentation presentation = new MyPresentation(context, presentationDisplay);
+ *         presentation.show();
+ *     $&#125;
+ * $&#125;
+ * }
+ * <p>
+ * The following sample code from <code>ApiDemos</code> demonstrates how to use the media
+ * router to automatically switch between showing content in the main activity and showing
+ * the content in a presentation when a presentation display is available.
+ * </p>
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java
+ *      activity}
+ *
+ * <h4>Using the display manager to choose a presentation display</h4>
+ * <p>
+ * Another way to choose a presentation display is to use the {@link DisplayManager} API
+ * directly.  The display manager service provides functions to enumerate and describe all
+ * displays that are attached to the system including displays that may be used
+ * for presentations.
+ * </p><p>
+ * The display manager keeps track of all displays in the system.  However, not all
+ * displays are appropriate for showing presentations.  For example, if an activity
+ * attempted to show a presentation on the main display it might obscure its own content
+ * (it's like opening a dialog on top of your activity).
+ * </p><p>
+ * Here's how to identify suitable displays for showing presentations using
+ * {@link DisplayManager#getDisplays(String)} and the
+ * {@link DisplayManager#DISPLAY_CATEGORY_PRESENTATION} category.
+ * </p>
+ * {@samplecode
+ * DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ * Display[] presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ * if (presentationDisplays.length > 0) $&#123;
+ *     // If there is more than one suitable presentation display, then we could consider
+ *     // giving the user a choice.  For this example, we simply choose the first display
+ *     // which is the one the system recommends as the preferred presentation display.
+ *     Display display = presentationDisplays[0];
+ *     Presentation presentation = new MyPresentation(context, presentationDisplay);
+ *     presentation.show();
+ * $&#125;
+ * }
+ * <p>
+ * The following sample code from <code>ApiDemos</code> demonstrates how to use the display
+ * manager to enumerate displays and show content on multiple presentation displays
+ * simultaneously.
+ * </p>
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java
+ *      activity}
+ *
+ * @see android.media.MediaRouter#ROUTE_TYPE_LIVE_VIDEO for information on about live
+ * video routes and how to obtain the preferred presentation display for the
+ * current media route.
  * @see DisplayManager for information on how to enumerate displays and receive
  * notifications when displays are added or removed.
  */
@@ -121,7 +208,7 @@
     @Override
     protected void onStart() {
         super.onStart();
-        mDisplayManager.registerDisplayListener(mDisplayListener, null);
+        mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
 
         // Since we were not watching for display changes until just now, there is a
         // chance that the display metrics have changed.  If so, we will need to
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 8cbf5b1..26bde19 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -1,5 +1,17 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package android.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
index 163e4e2..d5e64f6 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl
@@ -1,5 +1,17 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package android.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothHealth.aidl b/core/java/android/bluetooth/IBluetoothHealth.aidl
index e741da4..a84a42c 100644
--- a/core/java/android/bluetooth/IBluetoothHealth.aidl
+++ b/core/java/android/bluetooth/IBluetoothHealth.aidl
@@ -1,5 +1,17 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package android.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothInputDevice.aidl b/core/java/android/bluetooth/IBluetoothInputDevice.aidl
index 23e6d50..1ebb9ca 100755
--- a/core/java/android/bluetooth/IBluetoothInputDevice.aidl
+++ b/core/java/android/bluetooth/IBluetoothInputDevice.aidl
@@ -1,6 +1,19 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
+
 package android.bluetooth;
 
 import android.bluetooth.BluetoothDevice;
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index de8fe91..ed8777c 100755
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -1,5 +1,17 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package android.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
index 3e795ea..9551086 100644
--- a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
@@ -1,5 +1,17 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package android.bluetooth;
diff --git a/core/java/android/bluetooth/IBluetoothPan.aidl b/core/java/android/bluetooth/IBluetoothPan.aidl
index b91bd7d..5a32347 100644
--- a/core/java/android/bluetooth/IBluetoothPan.aidl
+++ b/core/java/android/bluetooth/IBluetoothPan.aidl
@@ -1,6 +1,19 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
+
 package android.bluetooth;
 
 import android.bluetooth.BluetoothDevice;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 28e320b..0a7a2e7 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -21,6 +21,8 @@
 import android.util.SparseArray;
 import android.view.Display;
 
+import java.util.ArrayList;
+
 /**
  * Manages the properties of attached displays.
  * <p>
@@ -40,6 +42,8 @@
     private final Object mLock = new Object();
     private final SparseArray<Display> mDisplays = new SparseArray<Display>();
 
+    private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
+
     /**
      * Broadcast receiver that indicates when the Wifi display status changes.
      * <p>
@@ -60,6 +64,20 @@
     public static final String EXTRA_WIFI_DISPLAY_STATUS =
             "android.hardware.display.extra.WIFI_DISPLAY_STATUS";
 
+    /**
+     * Display category: Presentation displays.
+     * <p>
+     * This category can be used to identify secondary displays that are suitable for
+     * use as presentation displays.
+     * </p>
+     *
+     * @see android.app.Presentation for information about presenting content
+     * on secondary displays.
+     * @see #getDisplays(String)
+     */
+    public static final String DISPLAY_CATEGORY_PRESENTATION =
+            "android.hardware.display.category.PRESENTATION";
+
     /** @hide */
     public DisplayManager(Context context) {
         mContext = context;
@@ -87,24 +105,52 @@
      * @return An array containing all displays.
      */
     public Display[] getDisplays() {
-        int[] displayIds = mGlobal.getDisplayIds();
-        int expectedCount = displayIds.length;
-        Display[] displays = new Display[expectedCount];
+        return getDisplays(null);
+    }
+
+    /**
+     * Gets all currently valid logical displays of the specified category.
+     * <p>
+     * When there are multiple displays in a category the returned displays are sorted
+     * of preference.  For example, if the requested category is
+     * {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays
+     * then the displays are sorted so that the first display in the returned array
+     * is the most preferred presentation display.  The application may simply
+     * use the first display or allow the user to choose.
+     * </p>
+     *
+     * @param category The requested display category or null to return all displays.
+     * @return An array containing all displays sorted by order of preference.
+     *
+     * @see #DISPLAY_CATEGORY_PRESENTATION
+     */
+    public Display[] getDisplays(String category) {
+        final int[] displayIds = mGlobal.getDisplayIds();
         synchronized (mLock) {
-            int actualCount = 0;
-            for (int i = 0; i < expectedCount; i++) {
-                Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
-                if (display != null) {
-                    displays[actualCount++] = display;
+            try {
+                if (category == null) {
+                    addMatchingDisplaysLocked(mTempDisplays, displayIds, -1);
+                } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+                    addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
+                    addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
+                    addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
                 }
-            }
-            if (actualCount != expectedCount) {
-                Display[] oldDisplays = displays;
-                displays = new Display[actualCount];
-                System.arraycopy(oldDisplays, 0, displays, 0, actualCount);
+                return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
+            } finally {
+                mTempDisplays.clear();
             }
         }
-        return displays;
+    }
+
+    private void addMatchingDisplaysLocked(
+            ArrayList<Display> displays, int[] displayIds, int matchType) {
+        for (int i = 0; i < displayIds.length; i++) {
+            Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
+            if (display != null
+                    && (matchType < 0 || display.getType() == matchType)) {
+                displays.add(display);
+            }
+        }
     }
 
     private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 898c766..d73f99a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -122,7 +122,7 @@
      * @param userHandle the user handle of the user whose information is being requested.
      * @return the UserInfo object for a specific user.
      * @hide
-     * */
+     */
     public UserInfo getUserInfo(int userHandle) {
         try {
             return mService.getUserInfo(userHandle);
@@ -134,10 +134,11 @@
 
     /**
      * Return the serial number for a user.  This is a device-unique
-     * number assigned to that user; if the user is deleted and new users
-     * created, the new users will not be given the same serial number.
+     * number assigned to that user; if the user is deleted and then a new
+     * user created, the new users will not be given the same serial number.
      * @param user The user whose serial number is to be retrieved.
-     * @return The serial number of the given user.
+     * @return The serial number of the given user; returns -1 if the
+     * given UserHandle does not exist.
      * @see #getUserForSerialNumber(long)
      */
     public long getSerialNumberForUser(UserHandle user) {
@@ -179,6 +180,14 @@
     }
 
     /**
+     * Return the number of users currently created on the device.
+     */
+    public int getUserCount() {
+        List<UserInfo> users = getUsers();
+        return users != null ? users.size() : 1;
+    }
+
+    /**
      * Returns information for all users on this device.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @return the list of users that were created.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e26c51a..23c12f6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3222,18 +3222,11 @@
         public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
 
         /**
-         * Id of the time appwidget on the lockscreen, or -1 if none
-         * @hide
-         */
-        public static final String LOCK_SCREEN_STATUS_APPWIDGET_ID =
-            "lock_screen_status_appwidget_id";
-
-        /**
          * Id of the user-selected appwidget on the lockscreen, or -1 if none
          * @hide
          */
-        public static final String LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID =
-            "lock_screen_user_selected_appwidget_id";
+        public static final String LOCK_SCREEN_APPWIDGET_IDS =
+            "lock_screen_appwidget_ids";
 
         /**
          * This preference enables showing the owner info on LockScren.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 662dc45..758abb5 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -54,7 +54,9 @@
     private final DisplayManagerGlobal mGlobal;
     private final int mDisplayId;
     private final int mLayerStack;
-    private final String mName;
+    private final int mFlags;
+    private final int mType;
+    private final String mAddress;
     private final CompatibilityInfoHolder mCompatibilityInfo;
 
     private DisplayInfo mDisplayInfo; // never null
@@ -82,22 +84,95 @@
      * Display flag: Indicates that the display supports compositing content
      * that is stored in protected graphics buffers.
      * <p>
+     * If this flag is set then the display device supports compositing protected buffers.
+     * </p><p>
+     * If this flag is not set then the display device may not support compositing
+     * protected buffers; the user may see a blank region on the screen instead of
+     * the protected content.
+     * </p><p>
      * Secure (DRM) video decoders may allocate protected graphics buffers to request that
      * a hardware-protected path be provided between the video decoder and the external
      * display sink.  If a hardware-protected path is not available, then content stored
      * in protected graphics buffers may not be composited.
      * </p><p>
-     * If this flag is not set, then the display device does not support compositing
-     * protected buffers; the user may see a blank region on the screen instead of
-     * the protected content.  An application can use this flag as a hint that it should
-     * select an alternate content stream or adopt a different strategy for decoding
-     * content that does not rely on protected buffers so as to ensure that the user
-     * can view the content on the display as expected.
+     * An application can use the absence of this flag as a hint that it should not use protected
+     * buffers for this display because the content may not be visible.  For example,
+     * if the flag is not set then the application may choose not to show content on this
+     * display, show an informative error message, select an alternate content stream
+     * or adopt a different strategy for decoding content that does not rely on
+     * protected buffers.
      * </p>
+     *
+     * @see #getFlags
      */
     public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1 << 0;
 
     /**
+     * Display flag: Indicates that the display has a secure video output and
+     * supports compositing secure surfaces.
+     * <p>
+     * If this flag is set then the display device has a secure video output
+     * and is capable of showing secure surfaces.  It may also be capable of
+     * showing {@link #FLAG_SUPPORTS_PROTECTED_BUFFERS protected buffers}.
+     * </p><p>
+     * If this flag is not set then the display device may not have a secure video
+     * output; the user may see a blank region on the screen instead of
+     * the contents of secure surfaces or protected buffers.
+     * </p><p>
+     * Secure surfaces are used to prevent content rendered into those surfaces
+     * by applications from appearing in screenshots or from being viewed
+     * on non-secure displays.  Protected buffers are used by secure video decoders
+     * for a similar purpose.
+     * </p><p>
+     * An application creates a window with a secure surface by specifying the
+     * {@link WindowManager.LayoutParams#FLAG_SECURE} window flag.
+     * Likewise, an application creates a {@link SurfaceView} with a secure surface
+     * by calling {@link SurfaceView#setSecure} before attaching the secure view to
+     * its containing window.
+     * </p><p>
+     * An application can use the absence of this flag as a hint that it should not create
+     * secure surfaces or protected buffers on this display because the content may
+     * not be visible.  For example, if the flag is not set then the application may
+     * choose not to show content on this display, show an informative error message,
+     * select an alternate content stream or adopt a different strategy for decoding
+     * content that does not rely on secure surfaces or protected buffers.
+     * </p>
+     *
+     * @see #getFlags
+     */
+    public static final int FLAG_SECURE = 1 << 1;
+
+    /**
+     * Display type: Unknown display type.
+     * @hide
+     */
+    public static final int TYPE_UNKNOWN = 0;
+
+    /**
+     * Display type: Built-in display.
+     * @hide
+     */
+    public static final int TYPE_BUILT_IN = 1;
+
+    /**
+     * Display type: HDMI display.
+     * @hide
+     */
+    public static final int TYPE_HDMI = 2;
+
+    /**
+     * Display type: WiFi display.
+     * @hide
+     */
+    public static final int TYPE_WIFI = 3;
+
+    /**
+     * Display type: Overlay display.
+     * @hide
+     */
+    public static final int TYPE_OVERLAY = 4;
+
+    /**
      * Internal method to create a display.
      * Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
      * or {@link android.hardware.display.DisplayManager#getDisplay}
@@ -111,10 +186,14 @@
         mGlobal = global;
         mDisplayId = displayId;
         mDisplayInfo = displayInfo;
-        mLayerStack = displayInfo.layerStack; // can never change as long as the display is valid
-        mName = displayInfo.name; // cannot change as long as the display is valid
         mCompatibilityInfo = compatibilityInfo;
         mIsValid = true;
+
+        // Cache properties that cannot change as long as the display is valid.
+        mLayerStack = displayInfo.layerStack;
+        mFlags = displayInfo.flags;
+        mType = displayInfo.type;
+        mAddress = displayInfo.address;
     }
 
     /**
@@ -182,12 +261,37 @@
      * @return The display flags.
      *
      * @see #FLAG_SUPPORTS_PROTECTED_BUFFERS
+     * @see #FLAG_SECURE
      */
     public int getFlags() {
-        synchronized (this) {
-            updateDisplayInfoLocked();
-            return mDisplayInfo.flags;
-        }
+        return mFlags;
+    }
+
+    /**
+     * Gets the display type.
+     *
+     * @return The display type.
+     *
+     * @see #TYPE_UNKNOWN
+     * @see #TYPE_BUILT_IN
+     * @see #TYPE_HDMI
+     * @see #TYPE_WIFI
+     * @see #TYPE_OVERLAY
+     * @hide
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Gets the display address, or null if none.
+     * Interpretation varies by display type.
+     *
+     * @return The display address.
+     * @hide
+     */
+    public String getAddress() {
+        return mAddress;
     }
 
     /**
@@ -202,10 +306,17 @@
 
     /**
      * Gets the name of the display.
+     * <p>
+     * Note that some displays may be renamed by the user.
+     * </p>
+     *
      * @return The display's name.
      */
     public String getName() {
-        return mName;
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.name;
+        }
     }
 
     /**
@@ -483,5 +594,25 @@
                     + ", " + mTempMetrics + ", isValid=" + mIsValid;
         }
     }
+
+    /**
+     * @hide
+     */
+    public static String typeToString(int type) {
+        switch (type) {
+            case TYPE_UNKNOWN:
+                return "UNKNOWN";
+            case TYPE_BUILT_IN:
+                return "BUILT_IN";
+            case TYPE_HDMI:
+                return "HDMI";
+            case TYPE_WIFI:
+                return "WIFI";
+            case TYPE_OVERLAY:
+                return "OVERLAY";
+            default:
+                return Integer.toString(type);
+        }
+    }
 }
 
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index fb04150..f3841d5 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -39,6 +39,17 @@
     public int flags;
 
     /**
+     * Display type.
+     */
+    public int type;
+
+    /**
+     * Display address, or null if none.
+     * Interpretation varies by display type.
+     */
+    public String address;
+
+    /**
      * The human-readable name of the display.
      */
     public String name;
@@ -143,10 +154,12 @@
     public float physicalYDpi;
 
     public static final Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
+        @Override
         public DisplayInfo createFromParcel(Parcel source) {
             return new DisplayInfo(source);
         }
 
+        @Override
         public DisplayInfo[] newArray(int size) {
             return new DisplayInfo[size];
         }
@@ -171,6 +184,9 @@
     public boolean equals(DisplayInfo other) {
         return other != null
                 && layerStack == other.layerStack
+                && flags == other.flags
+                && type == other.type
+                && Objects.equal(address, other.address)
                 && Objects.equal(name, other.name)
                 && appWidth == other.appWidth
                 && appHeight == other.appHeight
@@ -195,6 +211,8 @@
     public void copyFrom(DisplayInfo other) {
         layerStack = other.layerStack;
         flags = other.flags;
+        type = other.type;
+        address = other.address;
         name = other.name;
         appWidth = other.appWidth;
         appHeight = other.appHeight;
@@ -214,6 +232,8 @@
     public void readFromParcel(Parcel source) {
         layerStack = source.readInt();
         flags = source.readInt();
+        type = source.readInt();
+        address = source.readString();
         name = source.readString();
         appWidth = source.readInt();
         appHeight = source.readInt();
@@ -234,6 +254,8 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(layerStack);
         dest.writeInt(this.flags);
+        dest.writeInt(type);
+        dest.writeString(address);
         dest.writeString(name);
         dest.writeInt(appWidth);
         dest.writeInt(appHeight);
@@ -294,11 +316,17 @@
                 + ", rotation " + rotation
                 + ", density " + logicalDensityDpi
                 + ", " + physicalXDpi + " x " + physicalYDpi + " dpi"
-                + ", layerStack " + layerStack + flagsToString(flags) + "}";
+                + ", layerStack " + layerStack
+                + ", type " + Display.typeToString(type)
+                + ", address " + address
+                + flagsToString(flags) + "}";
     }
 
     private static String flagsToString(int flags) {
         StringBuilder result = new StringBuilder();
+        if ((flags & Display.FLAG_SECURE) != 0) {
+            result.append(", FLAG_SECURE");
+        }
         if ((flags & Display.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
             result.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
         }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 59f941d..1c613245 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1526,30 +1526,6 @@
         }
 
         @Override
-        void destroyLayers(View view) {
-            if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) {
-                if (mCanvas != null) {
-                    mCanvas.clearLayerUpdates();
-                }
-                destroyHardwareLayer(view);
-                GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
-            }
-        }
-
-        private static void destroyHardwareLayer(View view) {
-            view.destroyLayer(true);
-
-            if (view instanceof ViewGroup) {
-                ViewGroup group = (ViewGroup) view;
-
-                int count = group.getChildCount();
-                for (int i = 0; i < count; i++) {
-                    destroyHardwareLayer(group.getChildAt(i));
-                }
-            }
-        }
-
-        @Override
         boolean safelyRun(Runnable action) {
             boolean needsContext = true;
             if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
@@ -1574,6 +1550,35 @@
         }
 
         @Override
+        void destroyLayers(final View view) {
+            if (view != null) {
+                safelyRun(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (mCanvas != null) {
+                            mCanvas.clearLayerUpdates();
+                        }
+                        destroyHardwareLayer(view);
+                        GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+                    }
+                });
+            }
+        }
+
+        private static void destroyHardwareLayer(View view) {
+            view.destroyLayer(true);
+
+            if (view instanceof ViewGroup) {
+                ViewGroup group = (ViewGroup) view;
+
+                int count = group.getChildCount();
+                for (int i = 0; i < count; i++) {
+                    destroyHardwareLayer(group.getChildAt(i));
+                }
+            }
+        }
+
+        @Override
         void destroyHardwareResources(final View view) {
             if (view != null) {
                 safelyRun(new Runnable() {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0d16dd3..9008521 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -385,7 +385,27 @@
             mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         }
     }
-    
+
+    /**
+     * Control whether the surface view's content should be treated as secure,
+     * preventing it from appearing in screenshots or from being viewed on
+     * non-secure displays.
+     *
+     * <p>Note that this must be set before the surface view's containing
+     * window is attached to the window manager.
+     *
+     * <p>See {@link android.view.Display#FLAG_SECURE} for details.
+     *
+     * @param isSecure True if the surface view is secure.
+     */
+    public void setSecure(boolean isSecure) {
+        if (isSecure) {
+            mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+        } else {
+            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
+        }
+    }
+
     /**
      * Hack to allow special layering of windows.  The type is one of the
      * types in WindowManager.LayoutParams.  This is a hack so:
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5e259e..9d0d4f0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1864,7 +1864,6 @@
 
     /**
      * Default horizontal layout direction.
-     * @hide
      */
     private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
 
@@ -1914,7 +1913,7 @@
     /**
      * Default text direction is inherited
      */
-    public static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+    private static final int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
 
     /**
      * Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
@@ -2024,7 +2023,7 @@
     /**
      * Default text alignment is inherited
      */
-    public static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+    private static final int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
 
     /**
       * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
@@ -3224,7 +3223,7 @@
         mContext = context;
         mResources = context != null ? context.getResources() : null;
         mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
-        // Set layout and text direction defaults
+        // Set some flags defaults
         mPrivateFlags2 =
                 (LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) |
                 (TEXT_DIRECTION_DEFAULT << PFLAG2_TEXT_DIRECTION_MASK_SHIFT) |
@@ -14198,11 +14197,13 @@
      * @hide
      */
     protected void resolveDrawables() {
-        if (mBackground != null) {
-            mBackground.setLayoutDirection(getLayoutDirection());
+        if (canResolveLayoutDirection()) {
+            if (mBackground != null) {
+                mBackground.setLayoutDirection(getLayoutDirection());
+            }
+            mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
+            onResolveDrawables(getLayoutDirection());
         }
-        mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
-        onResolveDrawables(getLayoutDirection());
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c252c77..00723f3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3384,7 +3384,6 @@
 
         if (child.isLayoutDirectionInherited()) {
             child.resetRtlProperties();
-            child.resolveRtlPropertiesIfNeeded();
         }
 
         onViewAdded(child);
@@ -4817,8 +4816,6 @@
             int parentWidthMeasureSpec, int widthUsed,
             int parentHeightMeasureSpec, int heightUsed) {
         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-        final int layoutDirection = getLayoutDirection();
-        lp.resolveLayoutDirection(layoutDirection);
 
         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 01923e2..3b31ff6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -628,8 +628,13 @@
         @Deprecated
         public static final int FLAG_DITHER             = 0x00001000;
         
-        /** Window flag: don't allow screen shots while this window is
-         * displayed. Maps to Surface.SECURE. */
+        /** Window flag: Treat the content of the window as secure, preventing
+         * it from appearing in screenshots or from being viewed on non-secure
+         * displays.
+         *
+         * <p>See {@link android.view.Display#FLAG_SECURE} for more details about
+         * secure surfaces and secure displays.
+         */
         public static final int FLAG_SECURE             = 0x00002000;
         
         /** Window flag: a special mode where the layout parameters are used
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 19b825c..495e46b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -144,6 +144,8 @@
     CharSequence mError;
     boolean mErrorWasChanged;
     ErrorPopup mErrorPopup;
+    private int mLastLayoutDirection = -1;
+
     /**
      * This flag is set if the TextView tries to display an error before it
      * is attached to the window (so its position is still unknown).
@@ -288,23 +290,30 @@
     public void setError(CharSequence error, Drawable icon) {
         mError = TextUtils.stringOrSpannedString(error);
         mErrorWasChanged = true;
-        final Drawables dr = mTextView.mDrawables;
-        if (dr != null) {
-            switch (mTextView.getLayoutDirection()) {
+        final int layoutDirection = mTextView.getLayoutDirection();
+        if (mLastLayoutDirection != layoutDirection) {
+            final Drawables dr = mTextView.mDrawables;
+            switch (layoutDirection) {
                 default:
                 case View.LAYOUT_DIRECTION_LTR:
-                    mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
-                            dr.mDrawableBottom);
+                    if (dr != null) {
+                        mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
+                                dr.mDrawableBottom);
+                    } else {
+                        mTextView.setCompoundDrawables(null, null, icon, null);
+                    }
                     break;
                 case View.LAYOUT_DIRECTION_RTL:
-                    mTextView.setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight,
-                            dr.mDrawableBottom);
+                    if (dr != null) {
+                        mTextView.setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight,
+                                dr.mDrawableBottom);
+                    } else {
+                        mTextView.setCompoundDrawables(icon, null, null, null);
+                    }
                     break;
             }
-        } else {
-            mTextView.setCompoundDrawables(null, null, icon, null);
+            mLastLayoutDirection = layoutDirection;
         }
-
         if (mError == null) {
             if (mErrorPopup != null) {
                 if (mErrorPopup.isShowing()) {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e158776..738f63b 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -304,16 +304,11 @@
         int maxWidth = 0;
         int childState = 0;
 
-        final int layoutDirection = getLayoutDirection();
-
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
             if (mMeasureAllChildren || child.getVisibility() != GONE) {
                 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
-                // measureChildWithMargins() has triggered layout params resolution, so no need
-                // to do it now
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
                 maxWidth = Math.max(maxWidth,
                         child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                 maxHeight = Math.max(maxHeight,
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 83c15bb..b6f0862 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -648,8 +648,6 @@
 
         int largestChildHeight = Integer.MIN_VALUE;
 
-        final int layoutDirection = getLayoutDirection();
-
         // See how tall everyone is. Also remember max width.
         for (int i = 0; i < count; ++i) {
             final View child = getVirtualChildAt(i);
@@ -669,7 +667,6 @@
             }
 
             LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
-            lp.resolveLayoutDirection(layoutDirection);
 
             totalWeight += lp.weight;
             
@@ -992,8 +989,6 @@
 
         int largestChildWidth = Integer.MIN_VALUE;
 
-        final int layoutDirection = getLayoutDirection();
-
         // See how wide everyone is. Also remember max height.
         for (int i = 0; i < count; ++i) {
             final View child = getVirtualChildAt(i);
@@ -1014,7 +1009,6 @@
 
             final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                     child.getLayoutParams();
-            lp.resolveLayoutDirection(layoutDirection);
 
             totalWeight += lp.weight;
             
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index ace26f3..e52e84d 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -414,15 +414,12 @@
         final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
         final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
 
-        final int layoutDirection = getLayoutDirection();
-
         View[] views = mSortedHorizontalChildren;
         int count = views.length;
         for (int i = 0; i < count; i++) {
             View child = views[i];
             if (child.getVisibility() != GONE) {
                 LayoutParams params = (LayoutParams) child.getLayoutParams();
-                params.resolveLayoutDirection(layoutDirection);
 
                 applyHorizontalSizeRules(params, myWidth);
                 measureChildHorizontal(child, params, myWidth, myHeight);
@@ -486,6 +483,8 @@
             }
         }
 
+        final int layoutDirection = getLayoutDirection();
+
         if (isWrapContentWidth) {
             // Width already has left padding in it since it was calculated by looking at
             // the right of each child view
@@ -730,7 +729,6 @@
 
         final int layoutDirection = getLayoutDirection();
         int[] rules = params.getRules(layoutDirection);
-        params.resolveLayoutDirection(layoutDirection);
 
         if (params.mLeft < 0 && params.mRight >= 0) {
             // Right is fixed, but left varies
@@ -984,7 +982,6 @@
             if (child.getVisibility() != GONE) {
                 RelativeLayout.LayoutParams st =
                         (RelativeLayout.LayoutParams) child.getLayoutParams();
-                st.resolveLayoutDirection(getLayoutDirection());
                 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
             }
         }
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 1def89f..bc41931 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -329,13 +329,11 @@
             return;
         }
 
-        final int layoutDirection = getLayoutDirection();
         if (getChildCount() > 0) {
             final View child = getChildAt(0);
             int height = getMeasuredHeight();
             if (child.getMeasuredHeight() < height) {
                 final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                lp.resolveLayoutDirection(layoutDirection);
 
                 int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                         mPaddingLeft + mPaddingRight, lp.width);
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 68ffd73..35927e0 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -192,9 +192,7 @@
             int widthMeasureSpec, int totalWidth,
             int heightMeasureSpec, int totalHeight) {
         if (mConstrainedColumnWidths != null) {
-            final int layoutDirection = getLayoutDirection();
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.resolveLayoutDirection(layoutDirection);
 
             int measureMode = MeasureSpec.EXACTLY;
             int columnWidth = 0;
@@ -228,6 +226,7 @@
                 final int childWidth = child.getMeasuredWidth();
                 lp.mOffset[LayoutParams.LOCATION_NEXT] = columnWidth - childWidth;
 
+                final int layoutDirection = getLayoutDirection();
                 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.LEFT:
@@ -293,13 +292,11 @@
         }
 
         final int[] columnWidths = mColumnWidths;
-        final int layoutDirection = getLayoutDirection();
 
         for (int i = 0; i < numColumns; i++) {
             final View child = getVirtualChildAt(i);
             if (child != null && child.getVisibility() != GONE) {
                 final LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
-                layoutParams.resolveLayoutDirection(layoutDirection);
                 if (layoutParams.span == 1) {
                     int spec;
                     switch (layoutParams.width) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8e5ff40..a46481c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -306,7 +306,7 @@
     private Layout.Alignment mLayoutAlignment;
     private int mResolvedTextAlignment;
 
-    private boolean mResolvedDrawables;
+    private int mLastLayoutDirection = -1;
 
     /**
      * On some devices the fading edges add a performance penalty if used
@@ -8260,16 +8260,16 @@
     @Override
     public void onResolveDrawables(int layoutDirection) {
         // No need to resolve twice
-        if (mResolvedDrawables) {
+        if (mLastLayoutDirection == layoutDirection) {
             return;
         }
+        mLastLayoutDirection = layoutDirection;
         // No drawable to resolve
         if (mDrawables == null) {
             return;
         }
         // No relative drawable to resolve
         if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
-            mResolvedDrawables = true;
             return;
         }
 
@@ -8307,7 +8307,6 @@
                 break;
         }
         updateDrawablesLayoutDirection(dr, layoutDirection);
-        mResolvedDrawables = true;
     }
 
     private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) {
@@ -8329,7 +8328,7 @@
      * @hide
      */
     protected void resetResolvedDrawables() {
-        mResolvedDrawables = false;
+        mLastLayoutDirection = -1;
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 43c63b6..8bc1081 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -343,11 +343,9 @@
         final int height = maxHeight - verticalPadding;
         final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
         
-        final int layoutDirection = getLayoutDirection();
         if (mClose != null) {
             availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
             MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
-            lp.resolveLayoutDirection(layoutDirection);
             availableWidth -= lp.leftMargin + lp.rightMargin;
         }
 
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d8b3d2f..6fb459c 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -946,9 +946,6 @@
             final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
                     (ActionBar.LayoutParams) lp : null;
 
-            final int layoutDirection = getLayoutDirection();
-            lp.resolveLayoutDirection(layoutDirection);
-
             int horizontalMargin = 0;
             int verticalMargin = 0;
             if (ablp != null) {
@@ -1099,9 +1096,8 @@
             customView = mCustomNavView;
         }
         if (customView != null) {
-            ViewGroup.LayoutParams lp = customView.getLayoutParams();
             final int layoutDirection = getLayoutDirection();
-            lp.resolveLayoutDirection(layoutDirection);
+            ViewGroup.LayoutParams lp = customView.getLayoutParams();
             final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
                     (ActionBar.LayoutParams) lp : null;
             final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
@@ -1339,15 +1335,11 @@
         @Override
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
-            // measureChildWithMargins() has triggered layout params resolution, so no need
-            // to do it now
             final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
             mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
             int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth;
             int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
             measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
-            // measureChildWithMargins() has triggered layout params resolution, so no need
-            // to do it now
             final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
             width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
             height = Math.max(height,
@@ -1387,12 +1379,10 @@
         protected void onLayout(boolean changed, int l, int t, int r, int b) {
             final int vCenter = (b - t) / 2;
             final boolean isLayoutRtl = isLayoutRtl();
-            final int layoutDirection = getLayoutDirection();
             final int width = getWidth();
             int upOffset = 0;
             if (mUpView.getVisibility() != GONE) {
                 final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
-                upLp.resolveLayoutDirection(layoutDirection);
                 final int upHeight = mUpView.getMeasuredHeight();
                 final int upWidth = mUpView.getMeasuredWidth();
                 upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
@@ -1413,7 +1403,6 @@
             }
 
             final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
-            iconLp.resolveLayoutDirection(layoutDirection);
             final int iconHeight = mIconView.getMeasuredHeight();
             final int iconWidth = mIconView.getMeasuredWidth();
             final int hCenter = (r - l) / 2;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3f40f20..f6ae83c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,10 +16,6 @@
 
 package com.android.internal.widget;
 
-import com.android.internal.R;
-import com.android.internal.telephony.ITelephony;
-import com.google.android.collect.Lists;
-
 import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
 import android.content.ContentResolver;
@@ -29,7 +25,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -43,6 +38,10 @@
 import android.view.View;
 import android.widget.Button;
 
+import com.android.internal.R;
+import com.android.internal.telephony.ITelephony;
+import com.google.android.collect.Lists;
+
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -125,6 +124,11 @@
      */
     public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1;
 
+    /**
+     * Pseudo-appwidget id we use to represent the default clock status widget
+     */
+    public static final int ID_DEFAULT_STATUS_WIDGET = -2;
+
     protected final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
     protected final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
     protected final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
@@ -146,6 +150,7 @@
     private final ContentResolver mContentResolver;
     private DevicePolicyManager mDevicePolicyManager;
     private ILockSettings mLockSettingsService;
+    private int mStickyWidgetIndex = -1;
 
     // The current user is set by KeyguardViewMediator and shared by all LockPatternUtils.
     private static volatile int sCurrentUserId = UserHandle.USER_NULL;
@@ -1045,28 +1050,116 @@
         }
     }
 
-    public int[] getUserDefinedWidgets() {
-        int appWidgetId = -1;
+    public int[] getAppWidgets() {
         String appWidgetIdString = Settings.Secure.getStringForUser(
-                mContentResolver, Settings.Secure.LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID,
+                mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
                 UserHandle.USER_CURRENT);
-        if (appWidgetIdString != null) {
-            appWidgetId = (int) Integer.decode(appWidgetIdString);
+        String delims = ",";
+        if (appWidgetIdString != null && appWidgetIdString.length() > 0) {
+            String[] appWidgetStringIds = appWidgetIdString.split(delims);
+            int[] appWidgetIds = new int[appWidgetStringIds.length];
+            for (int i = 0; i < appWidgetStringIds.length; i++) {
+                String appWidget = appWidgetStringIds[i];
+                try {
+                    appWidgetIds[i] = Integer.decode(appWidget);
+                } catch (NumberFormatException e) {
+                    Log.d(TAG, "Error when parsing widget id " + appWidget);
+                    return null;
+                }
+            }
+            return appWidgetIds;
         }
-
-        return new int[] { appWidgetId };
+        if (appWidgetIdString == null) {
+            return new int[] { LockPatternUtils.ID_DEFAULT_STATUS_WIDGET };
+        } else {
+            return new int[0];
+        }
     }
 
-    public int getStatusWidget() {
-        int appWidgetId = -1;
-        String appWidgetIdString = Settings.Secure.getStringForUser(
-                mContentResolver, Settings.Secure.LOCK_SCREEN_STATUS_APPWIDGET_ID,
-                UserHandle.USER_CURRENT);
-        if (appWidgetIdString != null) {
-            appWidgetId = (int) Integer.decode(appWidgetIdString);
+    private static String combineStrings(int[] list, String separator) {
+        int listLength = list.length;
+
+        switch (listLength) {
+            case 0: {
+                return "";
+            }
+            case 1: {
+                return Integer.toString(list[0]);
+            }
         }
 
-        return appWidgetId;
+        int strLength = 0;
+        int separatorLength = separator.length();
+
+        String[] stringList = new String[list.length];
+        for (int i = 0; i < listLength; i++) {
+            stringList[i] = Integer.toString(list[i]);
+            strLength += stringList[i].length();
+            if (i < listLength - 1) {
+                strLength += separatorLength;
+            }
+        }
+
+        StringBuilder sb = new StringBuilder(strLength);
+
+        for (int i = 0; i < listLength; i++) {
+            sb.append(list[i]);
+            if (i < listLength - 1) {
+                sb.append(separator);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private void writeAppWidgets(int[] appWidgetIds) {
+        Settings.Secure.putStringForUser(mContentResolver,
+                        Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
+                        combineStrings(appWidgetIds, ","),
+                        UserHandle.USER_CURRENT);
+    }
+
+    // TODO: log an error if this returns false
+    public boolean addAppWidget(int widgetId, int index) {
+        int[] widgets = getAppWidgets();
+        if (widgets == null) {
+            return false;
+        }
+        if (index < 0 || index > widgets.length) {
+            return false;
+        }
+        int[] newWidgets = new int[widgets.length + 1];
+        for (int i = 0, j = 0; i < newWidgets.length; i++) {
+            if (index == i) {
+                newWidgets[i] = widgetId;
+                i++;
+            }
+            if (i < newWidgets.length) {
+                newWidgets[i] = widgets[j];
+                j++;
+            }
+        }
+        writeAppWidgets(newWidgets);
+        return true;
+    }
+
+    public boolean removeAppWidget(int widgetId) {
+        int[] widgets = getAppWidgets();
+
+        int[] newWidgets = new int[widgets.length - 1];
+        for (int i = 0, j = 0; i < widgets.length; i++) {
+            if (widgets[i] == widgetId) {
+                // continue...
+            } else if (j >= newWidgets.length) {
+                // we couldn't find the widget
+                return false;
+            } else {
+                newWidgets[j] = widgets[i];
+                j++;
+            }
+        }
+        writeAppWidgets(newWidgets);
+        return true;
     }
 
     private long getLong(String secureSettingKey, long defaultValue) {
@@ -1218,4 +1311,12 @@
         return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true);
     }
 
+    public int getStickyWidgetIndex() {
+        return mStickyWidgetIndex;
+    }
+
+    public void setStickyWidgetIndex(int stickyWidgetIndex) {
+        mStickyWidgetIndex = stickyWidgetIndex;
+    }
+
 }
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
index 0f49776..b7f64ec 100644
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
@@ -256,11 +256,8 @@
             setDirectionDescriptionsResourceId(resourceId);
         }
 
-        a.recycle();
+        mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP);
 
-        // Use gravity attribute from LinearLayout
-        a = context.obtainStyledAttributes(attrs, android.R.styleable.LinearLayout);
-        mGravity = a.getInt(android.R.styleable.LinearLayout_gravity, Gravity.TOP);
         a.recycle();
 
         setVibrateEnabled(mVibrationDuration > 0);
diff --git a/core/res/res/anim/keyguard_security_fade_in.xml b/core/res/res/anim/keyguard_security_fade_in.xml
index 7d5516a..6293432 100644
--- a/core/res/res/anim/keyguard_security_fade_in.xml
+++ b/core/res/res/anim/keyguard_security_fade_in.xml
@@ -15,8 +15,8 @@
 -->
 
 <alpha xmlns:android="http://schemas.android.com/apk/res/android"
-    android:interpolator="@interpolator/decelerate_quad"
+    android:interpolator="@android:interpolator/decelerate_quad"
     android:fromAlpha="0.0" android:toAlpha="1.0"
-    android:duration="@integer/kg_security_fade_duration" />
+    android:duration="@*android:integer/kg_security_fade_duration" />
 
 
diff --git a/core/res/res/anim/keyguard_security_fade_out.xml b/core/res/res/anim/keyguard_security_fade_out.xml
index 08c8b2b..4ab0229 100644
--- a/core/res/res/anim/keyguard_security_fade_out.xml
+++ b/core/res/res/anim/keyguard_security_fade_out.xml
@@ -13,9 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<alpha xmlns:android="http://schemas.android.com/apk/res/android" 
-    android:interpolator="@interpolator/accelerate_quad"
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:interpolator/accelerate_quad"
     android:fromAlpha="1.0"
     android:toAlpha="0.0"
-    android:duration="@integer/kg_security_fade_duration"
+    android:duration="@*android:integer/kg_security_fade_duration"
 />
diff --git a/core/res/res/drawable-hdpi/add_widget.png b/core/res/res/drawable-hdpi/add_widget.png
new file mode 100644
index 0000000..fb64a52
--- /dev/null
+++ b/core/res/res/drawable-hdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 0000000..c34fe20
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_grip.9.png b/core/res/res/drawable-hdpi/kg_security_grip.9.png
new file mode 100644
index 0000000..fb1c866
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_lock.png b/core/res/res/drawable-hdpi/kg_security_lock.png
new file mode 100644
index 0000000..136d3ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/security_frame.9.png b/core/res/res/drawable-hdpi/security_frame.9.png
new file mode 100644
index 0000000..9eeadc4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/security_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/security_handle.png b/core/res/res/drawable-hdpi/security_handle.png
new file mode 100644
index 0000000..bd4640f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/security_handle.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..f1bcf48
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/add_widget.png b/core/res/res/drawable-mdpi/add_widget.png
new file mode 100644
index 0000000..ae26787
--- /dev/null
+++ b/core/res/res/drawable-mdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 0000000..f636524
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_grip.9.png b/core/res/res/drawable-mdpi/kg_security_grip.9.png
new file mode 100644
index 0000000..25beb2b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_lock.png b/core/res/res/drawable-mdpi/kg_security_lock.png
new file mode 100644
index 0000000..861760d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/security_frame.9.png b/core/res/res/drawable-mdpi/security_frame.9.png
new file mode 100644
index 0000000..9eeadc4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/security_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/security_handle.png b/core/res/res/drawable-mdpi/security_handle.png
new file mode 100644
index 0000000..bd4640f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/security_handle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..d5a7708
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..f1bcf48
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..d5a7708
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..55174e0
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/add_widget.png b/core/res/res/drawable-xhdpi/add_widget.png
new file mode 100644
index 0000000..c02700c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/default_wallpaper.jpg b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
index 5b8d1d5..da9fa91 100644
--- a/core/res/res/drawable-xhdpi/default_wallpaper.jpg
+++ b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 0000000..9c4a603
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_grip.9.png b/core/res/res/drawable-xhdpi/kg_security_grip.9.png
new file mode 100644
index 0000000..b5cd134
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_lock.png b/core/res/res/drawable-xhdpi/kg_security_lock.png
new file mode 100644
index 0000000..4544584
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/security_frame.9.png b/core/res/res/drawable-xhdpi/security_frame.9.png
new file mode 100644
index 0000000..9eeadc4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/security_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/security_handle.png b/core/res/res/drawable-xhdpi/security_handle.png
new file mode 100644
index 0000000..bd4640f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/security_handle.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png
new file mode 100644
index 0000000..55174e0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
index 521853f..bb455bd 100644
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -21,30 +21,53 @@
     and the security view. -->
 <com.android.internal.policy.impl.keyguard.KeyguardHostView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res/android"
     android:id="@+id/keyguard_host_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center_vertical"
     android:orientation="horizontal">
 
-    <include layout="@layout/keyguard_widget_region"
-        android:layout_width="0dp"
+    <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
+        android:id="@+id/multi_pane_challenge"
+        android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_weight="@integer/kg_widget_region_weight" />
+        android:clipChildren="false">
 
-    <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
-        android:id="@+id/view_flipper"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="@integer/kg_security_flipper_weight"
-        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:paddingBottom="@dimen/keyguard_security_view_margin"
-        android:gravity="center">
+        <include layout="@layout/keyguard_widget_pager"
+            android:id="@+id/app_widget_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            androidprv:layout_centerWithinArea="0.55"
+            androidprv:layout_childType="widget"
+            androidprv:layout_maxWidth="480dp"
+            androidprv:layout_maxHeight="480dp" />
+        <include layout="@layout/keyguard_multi_user_selector"/>
 
-    </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+        <View android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              androidprv:layout_childType="scrim"
+              android:background="#99000000" />
 
+        <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
+            android:id="@+id/keyguard_security_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            androidprv:layout_childType="challenge"
+            androidprv:layout_centerWithinArea="0.55">
+            <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
+                android:id="@+id/view_flipper"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                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:paddingBottom="@dimen/keyguard_security_view_margin"
+                android:gravity="center">
+            </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+        </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
+
+    </com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout>
 </com.android.internal.policy.impl.keyguard.KeyguardHostView>
+
diff --git a/core/res/res/layout-land/keyguard_widget_pager.xml b/core/res/res/layout-land/keyguard_widget_pager.xml
new file mode 100644
index 0000000..8b7b9a3
--- /dev/null
+++ b/core/res/res/layout-land/keyguard_widget_pager.xml
@@ -0,0 +1,31 @@
+<?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.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetCarousel
+    xmlns:androidprv="http://schemas.android.com/apk/res/android"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:paddingLeft="25dp"
+    android:paddingRight="25dp"
+    android:paddingTop="25dp"
+    android:paddingBottom="25dp"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    androidprv:pageSpacing="10dp">
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetCarousel>
diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml
index 981fe6d..15e9844 100644
--- a/core/res/res/layout-port/keyguard_host_view.xml
+++ b/core/res/res/layout-port/keyguard_host_view.xml
@@ -21,28 +21,57 @@
     and the security view. -->
 <com.android.internal.policy.impl.keyguard.KeyguardHostView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res/android"
     android:id="@+id/keyguard_host_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center_horizontal"
     android:orientation="vertical">
 
-    <include layout="@layout/keyguard_widget_region"
+    <com.android.internal.policy.impl.keyguard.SlidingChallengeLayout
+        android:id="@+id/sliding_layout"
         android:layout_width="match_parent"
-        android:layout_height="153dp" />
+        android:layout_height="match_parent"
+        androidprv:dragHandle="@drawable/kg_security_grip"
+        androidprv:dragIcon="@drawable/kg_security_lock">
 
-    <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
-        android:id="@+id/view_flipper"
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:layout_weight="1"
-        android:paddingLeft="@dimen/keyguard_security_view_margin"
-        android:paddingTop="@dimen/keyguard_security_view_margin"
-        android:paddingRight="@dimen/keyguard_security_view_margin"
-        android:paddingBottom="@dimen/keyguard_security_view_margin"
-        android:gravity="center">
-    </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <include layout="@layout/keyguard_widget_pager"
+                android:id="@+id/app_widget_container"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_gravity="center"/>
+        </FrameLayout>
+
+        <View android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              androidprv:layout_childType="scrim"
+              android:background="#99000000" />
+
+        <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
+            android:id="@+id/keyguard_security_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            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:gravity="bottom|center_horizontal">
+            <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
+                android:id="@+id/view_flipper"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                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:paddingBottom="@dimen/keyguard_security_view_margin"
+                android:gravity="center">
+            </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+        </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
+    </com.android.internal.policy.impl.keyguard.SlidingChallengeLayout>
 </com.android.internal.policy.impl.keyguard.KeyguardHostView>
 
diff --git a/core/res/res/layout-port/keyguard_widget_pager.xml b/core/res/res/layout-port/keyguard_widget_pager.xml
new file mode 100644
index 0000000..6662f83
--- /dev/null
+++ b/core/res/res/layout-port/keyguard_widget_pager.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.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
+    xmlns:androidprv="http://schemas.android.com/apk/res/android"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/app_widget_container"
+    android:paddingLeft="25dp"
+    android:paddingRight="25dp"
+    android:paddingTop="25dp"
+    android:paddingBottom="25dp"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    androidprv:pageSpacing="10dp">
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
diff --git a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
index 23c1e9c..3953c95 100644
--- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
+++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -21,33 +21,54 @@
     and the security view. -->
 <com.android.internal.policy.impl.keyguard.KeyguardHostView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res/android"
     android:id="@+id/keyguard_host_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center_horizontal"
-    android:orientation="vertical">
+    android:orientation="horizontal">
 
-    <include layout="@layout/keyguard_widget_region"
+    <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
+        android:id="@+id/multi_pane_challenge"
         android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="@integer/kg_widget_region_weight" />
-
-    <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
-        android:id="@+id/view_flipper"
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="@integer/kg_security_flipper_weight"
+        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:paddingBottom="@dimen/keyguard_security_view_margin"
-        android:layout_gravity="center">
+        android:orientation="vertical">
 
+        <include layout="@layout/keyguard_widget_pager"
+            android:id="@+id/app_widget_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            androidprv:layout_centerWithinArea="0.55"
+            androidprv:layout_childType="widget"
+            androidprv:layout_maxWidth="480dp"
+            androidprv:layout_maxHeight="480dp" />
 
+        <include layout="@layout/keyguard_multi_user_selector"/>
 
-    </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+        <View android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              androidprv:layout_childType="scrim"
+              android:background="#99000000" />
 
+        <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
+            android:id="@+id/keyguard_security_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            androidprv:layout_childType="challenge"
+            android:layout_gravity="center_horizontal|bottom">
+            <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
+                android:id="@+id/view_flipper"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                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:paddingBottom="@dimen/keyguard_security_view_margin"
+                android:gravity="center">
+            </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+        </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
+
+    </com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout>
 </com.android.internal.policy.impl.keyguard.KeyguardHostView>
-
diff --git a/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml b/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
index 1eef099..930b14e 100644
--- a/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
+++ b/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
@@ -18,7 +18,7 @@
 -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     <include layout="@layout/keyguard_glow_pad_view"
-        android:layout_width="@dimen/kg_glow_pad_size"
-        android:layout_height="@dimen/kg_glow_pad_size"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_gravity="center" />
 </merge>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_add_widget.xml b/core/res/res/layout/keyguard_add_widget.xml
new file mode 100644
index 0000000..fa811d7
--- /dev/null
+++ b/core/res/res/layout/keyguard_add_widget.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyguard_add_widget"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+    <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            >
+        <ImageView
+            android:id="@+id/keyguard_add_widget_view"
+            android:clickable="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:padding="24dp"
+            android:src="@drawable/add_widget" />
+    </FrameLayout>
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml b/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
index 68840ab..eeb4178 100644
--- a/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
+++ b/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
@@ -61,7 +61,7 @@
             android:layout_width="0dip"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:drawableLeft="@drawable/lockscreen_forgot_password_button"
+            android:drawableLeft="@*android:drawable/lockscreen_forgot_password_button"
             style="?android:attr/buttonBarButtonStyle"
             android:textSize="@dimen/kg_status_line_font_size"
             android:textColor="?android:attr/textColorSecondary"
diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml
index 845c265..00f14f5 100644
--- a/core/res/res/layout/keyguard_face_unlock_view.xml
+++ b/core/res/res/layout/keyguard_face_unlock_view.xml
@@ -39,7 +39,7 @@
             android:id="@+id/spotlightMask"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:background="@color/facelock_spotlight_mask"
+            android:background="@*android:color/facelock_spotlight_mask"
         />
 
         <ImageButton
@@ -50,7 +50,7 @@
             android:layout_alignParentTop="true"
             android:layout_alignParentEnd="true"
             android:background="#00000000"
-            android:src="@drawable/ic_facial_backup"
+            android:src="@*android:drawable/ic_facial_backup"
         />
 
     </RelativeLayout>
diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml
index 0e851e3..2d8f02d 100644
--- a/core/res/res/layout/keyguard_multi_user_avatar.xml
+++ b/core/res/res/layout/keyguard_multi_user_avatar.xml
@@ -20,35 +20,26 @@
 <!-- This is a view that shows general status information in Keyguard. -->
 <com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="125dp"
-    android:layout_height="125dp"
-    android:background="#000000"
+    android:layout_width="@dimen/keyguard_avatar_size"
+    android:layout_height="@dimen/keyguard_avatar_size"
+    android:background="#00000000"
     android:gravity="center_horizontal">
     <ImageView
         android:id="@+id/keyguard_user_avatar"
-        android:scaleType="centerCrop"
+        android:scaleType="center"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center"/>
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
-        <Space
-            android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="0.78" />
-        <TextView
-            android:id="@+id/keyguard_user_name"
-            android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="0.22"
-            android:paddingLeft="6dp"
-            android:layout_gravity="center_vertical|left"
-            android:textSize="16sp"
-            android:textColor="#ffffff"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:background="#808080" />
-    </LinearLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
\ No newline at end of file
+    <TextView
+       android:id="@+id/keyguard_user_name"
+       android:layout_width="match_parent"
+       android:layout_height="wrap_content"
+       android:layout_gravity="bottom"
+       android:gravity="center"
+       android:textSize="@dimen/keyguard_avatar_name_size"
+       android:textColor="#ffffff"
+       android:singleLine="true"
+       android:ellipsize="end"
+       android:paddingLeft="2dp"
+       android:paddingRight="2dp" />
+</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
diff --git a/core/res/res/layout/keyguard_multi_user_selector.xml b/core/res/res/layout/keyguard_multi_user_selector.xml
index 5a6e998..ee01285 100644
--- a/core/res/res/layout/keyguard_multi_user_selector.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector.xml
@@ -17,18 +17,23 @@
 */
 -->
 <com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
+    xmlns:androidprv="http://schemas.android.com/apk/res/android"
     xmlns:android="http://schemas.android.com/apk/res/android"
+    androidprv:layout_childType="userSwitcher"
+    android:id="@+id/keyguard_user_selector"
     android:orientation="horizontal"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_gravity="center"
-    android:contentDescription="@string/keyguard_accessibility_user_selector">
+    android:layout_height="wrap_content"
+    android:layout_gravity="bottom"
+    android:contentDescription="@*android:string/keyguard_accessibility_user_selector"
+    android:visibility="gone">
 
-    <com.android.internal.policy.impl.keyguard.KeyguardSubdivisionLayout
+    <com.android.internal.policy.impl.keyguard.KeyguardLinearLayout
         android:id="@+id/keyguard_users_grid"
         android:orientation="horizontal"
-        android:layout_width="300dp"
-        android:layout_height="300dp"
-        android:layout_gravity="center" />
+        android:layout_width="wrap_content"
+        android:layout_marginBottom="@dimen/keyguard_muliuser_selector_margin"
+        android:layout_height="@dimen/keyguard_avatar_size"
+        android:layout_gravity="center|bottom" />
 
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView>
\ No newline at end of file
+</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView>
diff --git a/core/res/res/layout/keyguard_multi_user_selector_widget.xml b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
index ad9fdfe..fc126fe 100644
--- a/core/res/res/layout/keyguard_multi_user_selector_widget.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
@@ -23,7 +23,4 @@
     android:id="@+id/keyguard_multi_user_selector"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-
-    <include layout="@layout/keyguard_multi_user_selector"/>
-
 </com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_password_view.xml b/core/res/res/layout/keyguard_password_view.xml
index 81916f7..e28f2ac 100644
--- a/core/res/res/layout/keyguard_password_view.xml
+++ b/core/res/res/layout/keyguard_password_view.xml
@@ -77,18 +77,6 @@
                         android:imeOptions="flagForceAscii|actionDone"
                         />
 
-                    <!-- This delete button is only visible for numeric PIN entry -->
-                    <ImageButton android:id="@+id/delete_button"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="center_vertical"
-                        android:src="@*android:drawable/ic_input_delete"
-                        android:clickable="true"
-                        android:padding="8dip"
-                        android:background="?android:attr/selectableItemBackground"
-                        android:visibility="gone"
-                        />
-
                     <ImageView android:id="@+id/switch_ime_button"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
@@ -101,20 +89,6 @@
                         />
 
                 </LinearLayout>
-
-                <!-- Numeric keyboard -->
-                <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginStart="4dip"
-                    android:layout_marginEnd="4dip"
-                    android:paddingTop="4dip"
-                    android:paddingBottom="4dip"
-                    android:background="#40000000"
-                    android:keyBackground="@*android:drawable/btn_keyboard_key_ics"
-                    android:visibility="gone"
-                    android:clickable="true"
-                />
             </LinearLayout>
         </LinearLayout>
     </FrameLayout>
diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..9b883af
--- /dev/null
+++ b/core/res/res/layout/keyguard_pin_view.xml
@@ -0,0 +1,206 @@
+<?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.
+*/
+-->
+
+<com.android.internal.policy.impl.keyguard.KeyguardPINView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyguard_pin_view"
+    android:layout_width="350dp"
+    android:layout_height="350dp"
+    android:orientation="vertical"
+    >
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:orientation="horizontal"
+        android:layout_weight="1"
+        >
+        <TextView android:id="@+id/passwordEntry"
+            android:editable="true"
+            android:layout_width="0dip"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left"
+            android:singleLine="true"
+            android:cursorVisible="false"
+            android:background="@null"
+            android:textAppearance="@android:style/TextAppearance.NumPadKey"
+            android:imeOptions="flagForceAscii|actionDone"
+            />
+        <ImageButton android:id="@+id/delete_button"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            android:src="@*android:drawable/ic_input_delete"
+            android:clickable="true"
+            android:paddingTop="8dip"
+            android:paddingBottom="8dip"
+            android:paddingLeft="24dp"
+            android:paddingRight="24dp"
+            android:background="?android:attr/selectableItemBackground"
+            />
+    </LinearLayout>
+    <View
+        android:layout_width="wrap_content"
+        android:layout_height="1dp"
+        android:background="#55FFFFFF"
+        />
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        >
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key1"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="1"
+            />
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key2"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="2"
+            />
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key3"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="3"
+            />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        >
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key4"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="4"
+            />
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key5"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="5"
+            />
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key6"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="6"
+            />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:orientation="horizontal"
+        android:layout_weight="1"
+        >
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key7"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="7"
+            />
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key8"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="8"
+            />
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key9"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="9"
+            />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        >
+        <Space
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            />
+        <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+            android:id="@+id/key0"
+            style="@style/Widget.Button.NumPadKey"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            androidprv:textView="@+id/passwordEntry"
+            androidprv:digit="0"
+            />
+        <ImageButton
+            android:id="@+id/key_enter"
+            style="@android:style/Widget.Button.NumPadKey"
+            android:gravity="center"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:src="@drawable/sym_keyboard_return_holo"
+            />
+    </LinearLayout>
+
+    <include layout="@layout/keyguard_emergency_carrier_area_and_recovery"
+                   android:id="@+id/keyguard_selector_fade_container"
+                   android:layout_width="match_parent"
+                   android:layout_height="wrap_content"
+                   android:orientation="vertical"
+                   android:layout_gravity="bottom|center_horizontal"
+                   android:gravity="center_horizontal" />
+
+</com.android.internal.policy.impl.keyguard.KeyguardPINView>
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index 1de0f6c..9532a88 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -35,7 +35,7 @@
 
         <LinearLayout android:layout_width="match_parent"
                       android:layout_height="wrap_content"
-                      android:layout_gravity="center_vertical"
+                      android:layout_gravity="center_horizontal|top"
                       android:orientation="vertical">
             <com.android.internal.policy.impl.keyguard.ClockView
                 android:id="@+id/clock_view"
diff --git a/core/res/res/layout/keyguard_transport_control_view.xml b/core/res/res/layout/keyguard_transport_control_view.xml
index 5a6083a..532322c 100644
--- a/core/res/res/layout/keyguard_transport_control_view.xml
+++ b/core/res/res/layout/keyguard_transport_control_view.xml
@@ -26,8 +26,8 @@
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:foreground="@drawable/ic_lockscreen_player_background"
-        android:contentDescription="@string/keygaurd_accessibility_media_controls">
+        android:foreground="@*android:drawable/ic_lockscreen_player_background"
+        android:contentDescription="@*android:string/keygaurd_accessibility_media_controls">
         <!-- Use ImageView for its cropping features; otherwise could be android:background -->
         <ImageView
             android:id="@+id/albumart"
@@ -70,11 +70,11 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="center"
-                    android:src="@drawable/ic_media_previous"
+                    android:src="@*android:drawable/ic_media_previous"
                     android:clickable="true"
                     android:background="?android:attr/selectableItemBackground"
                     android:padding="10dip"
-                    android:contentDescription="@string/lockscreen_transport_prev_description"/>
+                    android:contentDescription="@*android:string/lockscreen_transport_prev_description"/>
             </FrameLayout>
             <FrameLayout
                 android:layout_width="wrap_content"
@@ -86,10 +86,10 @@
                     android:layout_height="wrap_content"
                     android:layout_gravity="center"
                     android:clickable="true"
-                    android:src="@drawable/ic_media_play"
+                    android:src="@*android:drawable/ic_media_play"
                     android:background="?android:attr/selectableItemBackground"
                     android:padding="10dip"
-                    android:contentDescription="@string/lockscreen_transport_play_description"/>
+                    android:contentDescription="@*android:string/lockscreen_transport_play_description"/>
             </FrameLayout>
             <FrameLayout
                 android:layout_width="wrap_content"
@@ -101,10 +101,10 @@
                     android:layout_height="wrap_content"
                     android:layout_gravity="center"
                     android:clickable="true"
-                    android:src="@drawable/ic_media_next"
+                    android:src="@*android:drawable/ic_media_next"
                     android:background="?android:attr/selectableItemBackground"
                     android:padding="10dip"
-                    android:contentDescription="@string/lockscreen_transport_next_description"/>
+                    android:contentDescription="@*android:string/lockscreen_transport_next_description"/>
             </FrameLayout>
         </LinearLayout>
     </LinearLayout>
diff --git a/core/res/res/layout/keyguard_widget_region.xml b/core/res/res/layout/keyguard_widget_region.xml
deleted file mode 100644
index ed10c2b..0000000
--- a/core/res/res/layout/keyguard_widget_region.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?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.
-*/
--->
-
-<!-- This is the selector widget that allows the user to select an action. -->
-<com.android.internal.policy.impl.keyguard.KeyguardWidgetRegion
-    xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/kg_widget_region"
-        android:visibility="gone"
-        android:gravity="center"
-        android:orientation="vertical">
-    <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
-        android:id="@+id/app_widget_container"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:clipChildren="false"
-        android:clipToPadding="false">
-    </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="10dp"
-        android:orientation="horizontal"
-        android:paddingLeft="@dimen/kg_widget_pager_horizontal_padding"
-        android:paddingRight="@dimen/kg_widget_pager_horizontal_padding"
-        android:layout_marginTop="@dimen/kg_runway_lights_top_margin"
-        android:visibility="gone">
-        <com.android.internal.policy.impl.keyguard.KeyguardGlowStripView
-            android:id="@+id/left_strip"
-            android:paddingTop="@dimen/kg_runway_lights_vertical_padding"
-            android:paddingBottom="@dimen/kg_runway_lights_vertical_padding"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            prvandroid:numDots="5"
-            prvandroid:dotSize="@dimen/kg_runway_lights_height"
-            prvandroid:leftToRight="false"
-            prvandroid:glowDot="@*android:drawable/ic_lockscreen_glowdot" />
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1.6"/>
-        <com.android.internal.policy.impl.keyguard.KeyguardGlowStripView
-            android:id="@+id/right_strip"
-            android:paddingTop="@dimen/kg_runway_lights_vertical_padding"
-            android:paddingBottom="@dimen/kg_runway_lights_vertical_padding"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            prvandroid:numDots="5"
-            prvandroid:dotSize="@dimen/kg_runway_lights_height"
-            prvandroid:leftToRight="true"
-            prvandroid:glowDot="@*android:drawable/ic_lockscreen_glowdot" />
-    </LinearLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetRegion>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index fdd35a1..d63f85d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -585,7 +585,7 @@
     <string name="permdesc_writeDictionary" msgid="8185385716255065291">"Allows the app to write new words into the user dictionary."</string>
     <string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"test access to protected storage"</string>
     <string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"test access to protected storage"</string>
-    <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Allows the app to test a permission for USB storage that will be availabe on future devices."</string>
+    <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Allows the app to test a permission for USB storage that will be available on future devices."</string>
     <string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Allows the app to test a permission for the SD card that will be available on future devices."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
     <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a333187..9389579 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1381,12 +1381,9 @@
     <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Pantalla HDMI"</string>
     <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposición #<xliff:g id="ID">%1$d</xliff:g>"</string>
     <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> x <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> ppp"</string>
-    <!-- no translation found for wifi_display_notification_title (2223050649240326557) -->
-    <skip />
-    <!-- no translation found for wifi_display_notification_message (4498802012464170685) -->
-    <skip />
-    <!-- no translation found for wifi_display_notification_disconnect (6183754463561153372) -->
-    <skip />
+    <string name="wifi_display_notification_title" msgid="2223050649240326557">"Se conectó la pantalla inalámbrica"</string>
+    <string name="wifi_display_notification_message" msgid="4498802012464170685">"Esta pantalla se muestra en otro dispositivo."</string>
+    <string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"Desconectar"</string>
     <string name="kg_emergency_call_label" msgid="684946192523830531">"Llamada de emergencia"</string>
     <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"¿Olvidaste el patrón?"</string>
     <string name="kg_wrong_pattern" msgid="1850806070801358830">"Patrón incorrecto"</string>
diff --git a/core/res/res/values-land/bools.xml b/core/res/res/values-land/bools.xml
index b0630ad..85c64d9 100644
--- a/core/res/res/values-land/bools.xml
+++ b/core/res/res/values-land/bools.xml
@@ -15,6 +15,7 @@
 -->
 
 <resources>
+    <bool name="kg_enable_camera_default_widget">false</bool>
     <bool name="kg_share_status_area">false</bool>
     <bool name="kg_sim_puk_account_full_screen">false</bool>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 4d24fcb..855f461 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1382,7 +1382,7 @@
     <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Наложение № <xliff:g id="ID">%1$d</xliff:g>"</string>
     <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> х <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> тчк/дюйм"</string>
     <string name="wifi_display_notification_title" msgid="2223050649240326557">"Беспроводной проектор подключен"</string>
-    <string name="wifi_display_notification_message" msgid="4498802012464170685">"Изображение проецируется на другое устройство"</string>
+    <string name="wifi_display_notification_message" msgid="4498802012464170685">"Изображение передается на другое устройство"</string>
     <string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"Отключить"</string>
     <string name="kg_emergency_call_label" msgid="684946192523830531">"Экстренный вызов"</string>
     <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забыли графический ключ?"</string>
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index 3753aba..eae4f87 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -19,4 +19,6 @@
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="kg_share_status_area">false</bool>
     <bool name="kg_sim_puk_account_full_screen">false</bool>
+    <!-- No camera for you, tablet user -->
+    <bool name="kg_enable_camera_default_widget">false</bool>
 </resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 564545a..0d01df4 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -108,5 +108,9 @@
     <dimen name="kg_runway_lights_top_margin">-10dp</dimen>
 
     <!-- Margin around the various security views -->
-    <dimen name="keyguard_security_view_margin">24dp</dimen>
+    <dimen name="keyguard_security_view_margin">12dp</dimen>
+
+    <!-- Margin around the various security views -->
+    <dimen name="keyguard_muliuser_selector_margin">12dp</dimen>
+
 </resources>
diff --git a/core/res/res/values-sw600dp/integers.xml b/core/res/res/values-sw600dp/integers.xml
new file mode 100644
index 0000000..de9829c
--- /dev/null
+++ b/core/res/res/values-sw600dp/integers.xml
@@ -0,0 +1,21 @@
+<?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.
+*/
+-->
+<resources>
+    <integer name="kg_carousel_angle">60</integer>
+</resources>
diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index 6144961..d6d2b66 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -101,5 +101,15 @@
     <dimen name="kg_runway_lights_top_margin">-30dp</dimen>
 
     <!-- Margin around the various security views -->
-    <dimen name="keyguard_security_view_margin">100dp</dimen>
+    <dimen name="keyguard_muliuser_selector_margin">24dp</dimen>
+
+    <!-- Stroke width of the frame for the circular avatars. -->
+    <dimen name="keyguard_avatar_frame_stroke_width">3dp</dimen>
+
+    <!-- Size of the avator on the multiuser lockscreen. -->
+    <dimen name="keyguard_avatar_size">88dp</dimen>
+
+    <!-- Size of the text under the avator on the multiuser lockscreen. -->
+    <dimen name="keyguard_avatar_name_size">12sp</dimen>
+
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 8783b0b..b0c363bc 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1381,8 +1381,8 @@
     <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI 屏幕"</string>
     <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"叠加视图 #<xliff:g id="ID">%1$d</xliff:g>"</string>
     <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>:<xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>,<xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
-    <string name="wifi_display_notification_title" msgid="2223050649240326557">"无线显示设备已连接"</string>
-    <string name="wifi_display_notification_message" msgid="4498802012464170685">"此屏幕显示在另一台设备上"</string>
+    <string name="wifi_display_notification_title" msgid="2223050649240326557">"已连接到无线显示设备"</string>
+    <string name="wifi_display_notification_message" msgid="4498802012464170685">"此屏幕的内容正显示在另一台设备上"</string>
     <string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"断开连接"</string>
     <string name="kg_emergency_call_label" msgid="684946192523830531">"紧急呼救"</string>
     <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"忘记了图案"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 8615476..8744bfe 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -398,4 +398,17 @@
         <item>@null</item>
     </array>
 
+    <!-- list of 3- or 4-letter mnemonics for a 10-key numeric keypad -->
+    <string-array translatable="false" name="lockscreen_num_pad_klondike">
+        <item></item><!-- 0 -->
+        <item></item><!-- 1 -->
+        <item>ABC</item><!-- 2 -->
+        <item>DEF</item><!-- 3 -->
+        <item>GHI</item><!-- 4 -->
+        <item>JKL</item><!-- 5 -->
+        <item>MNO</item><!-- 6 -->
+        <item>PQRS</item><!-- 7 -->
+        <item>TUV</item><!-- 8 -->
+        <item>WXYZ</item><!-- 9 -->
+    </string-array>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3550df9..48d4745 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5464,6 +5464,8 @@
 
         <!-- Used when the handle shouldn't wait to be hit before following the finger -->
         <attr name="alwaysTrackFinger"/>
+
+        <attr name="gravity" />
     </declare-styleable>
 
     <!-- =============================== -->
@@ -5759,14 +5761,6 @@
     <!-- PagedView specific attributes. These attributes are used to customize
          a PagedView view in XML files. -->
     <declare-styleable name="PagedView">
-        <!-- A spacing override for the icons within a page -->
-        <attr name="pageLayoutWidthGap" format="dimension" />
-        <attr name="pageLayoutHeightGap" format="dimension" />
-        <!-- The padding of the pages that are dynamically created per page -->
-        <attr name="pageLayoutPaddingTop" format="dimension" />
-        <attr name="pageLayoutPaddingBottom" format="dimension" />
-        <attr name="pageLayoutPaddingLeft" format="dimension" />
-        <attr name="pageLayoutPaddingRight" format="dimension" />
         <!-- The space between adjacent pages of the PagedView. -->
         <attr name="pageSpacing" format="dimension" />
         <!-- The padding for the scroll indicator area -->
@@ -5781,10 +5775,58 @@
         <attr name="leftToRight" format="boolean" />
     </declare-styleable>
 
+    <!-- Some child types have special behavior. -->
+    <attr name="layout_childType">
+        <!-- No special behavior. Layout will proceed as normal. -->
+        <enum name="none" value="0" />
+        <!-- Widget container.
+             This will be resized in response to certain events. -->
+        <enum name="widget" value="1" />
+        <!-- Security challenge container.
+             This will be dismissed/shown in response to certain events,
+             possibly obscuring widget elements. -->
+        <enum name="challenge" value="2" />
+        <!-- User switcher.
+             This will consume space from the total layout area. -->
+        <enum name="userSwitcher" value="3" />
+        <!-- Scrim. This will block access to child views that
+             come before it in the child list in bouncer mode. -->
+        <enum name="scrim" value="4" />
+    </attr>
+
+    <declare-styleable name="SlidingChallengeLayout">
+        <attr name="dragHandle" format="reference" />
+        <attr name="dragIcon" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="SlidingChallengeLayout_Layout">
+        <attr name="layout_childType" />
+    </declare-styleable>
+
     <!-- Attributes that can be used with <code>&lt;FragmentBreadCrumbs&gt;</code>
     tags. -->
     <declare-styleable name="FragmentBreadCrumbs">
         <attr name="gravity" />
     </declare-styleable>
 
+    <declare-styleable name="MultiPaneChallengeLayout">
+        <!-- Influences how layout_centerWithinArea behaves -->
+        <attr name="orientation" />
+    </declare-styleable>
+
+    <declare-styleable name="MultiPaneChallengeLayout_Layout">
+        <!-- Percentage of the screen this child should consume or center within.
+             If 0/default, the view will be measured by standard rules
+             as if this were a FrameLayout. -->
+        <attr name="layout_centerWithinArea" format="float" />
+        <attr name="layout_childType" />
+        <attr name="layout_gravity" />
+        <attr name="layout_maxWidth" format="dimension" />
+        <attr name="layout_maxHeight" />
+    </declare-styleable>
+
+    <declare-styleable name="NumPadKey">
+        <attr name="digit" format="integer" />
+        <attr name="textView" format="reference" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index f9762b1..d4ead01 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -15,6 +15,7 @@
 -->
 
 <resources>
+    <bool name="kg_enable_camera_default_widget">true</bool>
     <bool name="action_bar_embed_tabs">true</bool>
     <bool name="action_bar_embed_tabs_pre_jb">false</bool>
     <bool name="split_action_bar_is_narrow">true</bool>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 6a93f30..b19e23d 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -190,5 +190,10 @@
     <drawable name="notification_template_icon_bg">#3333B5E5</drawable>
     <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
 
+    <!-- Keyguard colors -->
+    <color name="keyguard_avatar_frame_color">#ffffffff</color>
+    <color name="keyguard_avatar_frame_shadow_color">#80000000</color>
+    <color name="keyguard_avatar_nick_color">#ffffffff</color>
+    <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
 </resources>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 948a3d3..8a7632c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -242,8 +242,8 @@
     <dimen name="notification_subtext_size">12dp</dimen>
 
     <!-- Keyguard dimensions -->
-    <!-- Width of security view in keyguard. -->
-    <dimen name="kg_glow_pad_size">500dp</dimen>
+    <!-- TEMP -->
+    <dimen name="kg_security_panel_height">600dp</dimen>
 
     <!-- Height of security view in keyguard. -->
     <dimen name="kg_security_view_height">0dp</dimen>
@@ -305,4 +305,23 @@
 
     <!-- Margin around the various security views -->
     <dimen name="keyguard_security_view_margin">8dp</dimen>
+
+    <!-- Margin around the various security views -->
+    <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
+
+    <!-- Stroke width of the frame for the circular avatars. -->
+    <dimen name="keyguard_avatar_frame_stroke_width">2dp</dimen>
+
+    <!-- Shadow radius under the frame for the circular avatars. -->
+    <dimen name="keyguard_avatar_frame_shadow_radius">1dp</dimen>
+
+    <!-- Size of the avator on hte multiuser lockscreen. -->
+    <dimen name="keyguard_avatar_size">66dp</dimen>
+
+    <!-- Size of the text under the avator on the multiuser lockscreen. -->
+    <dimen name="keyguard_avatar_name_size">10sp</dimen>
+
+    <!-- Size of the region along the edge of the screen that will accept
+         swipes to scroll the widget area. -->
+    <dimen name="kg_edge_swipe_region_size">24dp</dimen>
 </resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index 6d49a91..91ad5d8 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -17,6 +17,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>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f8dbd84..47a8fc5 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1721,7 +1721,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_sdcardRead" product="default">test access to protected storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to test a permission for USB storage that will be availabe on future devices. </string>
+    <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to test a permission for USB storage that will be available on future devices. </string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_sdcardRead" product="default">Allows the app to test a permission for the SD card that will be available on future devices.</string>
 
@@ -3950,5 +3950,7 @@
     <string name="enable_accessibility_canceled">Accessibility canceled.</string>
     <!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
     <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
+    <!-- Default name of the owner user [CHAR LIMIT=20] -->
+    <string name="owner_name" msgid="3879126011135546571">Owner</string>
 
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4371aec..da26e6a 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -2478,4 +2478,29 @@
         <item name="android:contentDescription">@android:string/media_route_button_content_description</item>
     </style>
 
+    <!-- Keyguard PIN pad styles -->
+    <style name="Widget.Button.NumPadKey"
+            parent="@android:style/Widget.Button">
+        <item name="android:singleLine">true</item>
+        <item name="android:padding">6dip</item>
+        <item name="android:gravity">left|center_vertical</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+        <item name="android:textSize">34dp</item>
+        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#ffffff</item>
+    </style>
+    <style name="TextAppearance.NumPadKey"
+            parent="@android:style/TextAppearance">
+        <item name="android:textSize">34dp</item>
+        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#ffffff</item>
+    </style>
+    <style name="TextAppearance.NumPadKey.Klondike">
+        <item name="android:textSize">20dp</item>
+        <item name="android:fontFamily">sans-serif-condensed</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#80ffffff</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8ef91df..60a2e91 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -842,6 +842,7 @@
   <java-symbol type="string" name="media_route_status_connecting" />
   <java-symbol type="string" name="media_route_status_available" />
   <java-symbol type="string" name="media_route_status_not_available" />
+  <java-symbol type="string" name="owner_name" />
 
   <java-symbol type="plurals" name="abbrev_in_num_days" />
   <java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -962,6 +963,7 @@
   <java-symbol type="drawable" name="status_bar_background" />
   <java-symbol type="drawable" name="sym_keyboard_shift" />
   <java-symbol type="drawable" name="sym_keyboard_shift_locked" />
+  <java-symbol type="drawable" name="sym_keyboard_return_holo" />
   <java-symbol type="drawable" name="tab_bottom_left" />
   <java-symbol type="drawable" name="tab_bottom_left_v4" />
   <java-symbol type="drawable" name="tab_bottom_right" />
@@ -1086,8 +1088,8 @@
   <java-symbol type="layout" name="notification_template_inbox" />
   <java-symbol type="layout" name="keyguard_multi_user_avatar" />
   <java-symbol type="layout" name="keyguard_multi_user_selector_widget" />
-  <java-symbol type="layout" name="keyguard_widget_region" />
   <java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
+  <java-symbol type="layout" name="keyguard_add_widget" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
@@ -1177,6 +1179,7 @@
   <java-symbol type="array" name="lockscreen_targets_when_silent" />
   <java-symbol type="array" name="lockscreen_targets_when_soundon" />
   <java-symbol type="array" name="lockscreen_targets_with_camera" />
+  <java-symbol type="array" name="lockscreen_num_pad_klondike" />
   <java-symbol type="attr" name="actionModePopupWindowStyle" />
   <java-symbol type="attr" name="dialogCustomTitleDecorLayout" />
   <java-symbol type="attr" name="dialogTitleDecorLayout" />
@@ -1191,12 +1194,17 @@
   <java-symbol type="bool" name="config_lidControlsSleep" />
   <java-symbol type="bool" name="config_reverseDefaultRotation" />
   <java-symbol type="bool" name="config_showNavigationBar" />
+  <java-symbol type="bool" name="kg_enable_camera_default_widget" />
   <java-symbol type="bool" name="kg_share_status_area" />
-  <java-symbol type="bool" name="kg_sim_puk_account_full_screen" />  
+  <java-symbol type="bool" name="kg_sim_puk_account_full_screen" />
   <java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
   <java-symbol type="color" name="kg_multi_user_text_active" />
   <java-symbol type="color" name="kg_multi_user_text_inactive" />
   <java-symbol type="color" name="kg_widget_pager_gradient" />
+  <java-symbol type="color" name="keyguard_avatar_frame_color" />
+  <java-symbol type="color" name="keyguard_avatar_frame_pressed_color" />
+  <java-symbol type="color" name="keyguard_avatar_frame_shadow_color" />
+  <java-symbol type="color" name="keyguard_avatar_nick_color" />
   <java-symbol type="dimen" name="navigation_bar_height" />
   <java-symbol type="dimen" name="navigation_bar_height_landscape" />
   <java-symbol type="dimen" name="navigation_bar_width" />
@@ -1204,6 +1212,10 @@
   <java-symbol type="dimen" name="kg_widget_pager_horizontal_padding" />
   <java-symbol type="dimen" name="kg_widget_pager_top_padding" />
   <java-symbol type="dimen" name="kg_widget_pager_bottom_padding" />
+  <java-symbol type="dimen" name="keyguard_avatar_size" />
+  <java-symbol type="dimen" name="keyguard_avatar_frame_stroke_width" />
+  <java-symbol type="dimen" name="keyguard_avatar_frame_shadow_radius" />
+  <java-symbol type="dimen" name="kg_edge_swipe_region_size" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_off" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_on" />
   <java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -1222,6 +1234,7 @@
   <java-symbol type="drawable" name="magnified_region_frame" />
   <java-symbol type="drawable" name="menu_background" />
   <java-symbol type="drawable" name="stat_sys_secure" />
+  <java-symbol type="drawable" name="security_frame" />
   <java-symbol type="id" name="action_mode_bar_stub" />
   <java-symbol type="id" name="alarm_status" />
   <java-symbol type="id" name="backspace" />
@@ -1280,6 +1293,7 @@
   <java-symbol type="id" name="keyguard_selector_view" />
   <java-symbol type="id" name="keyguard_pattern_view" />
   <java-symbol type="id" name="keyguard_password_view" />
+  <java-symbol type="id" name="keyguard_pin_view" />
   <java-symbol type="id" name="keyguard_face_unlock_view" />
   <java-symbol type="id" name="keyguard_sim_pin_view" />
   <java-symbol type="id" name="keyguard_sim_puk_view" />
@@ -1304,12 +1318,15 @@
   <java-symbol type="id" name="keyguard_users_grid" />
   <java-symbol type="id" name="clock_text" />
   <java-symbol type="id" name="clock_view" />
-  <java-symbol type="id" name="kg_widget_region" />
-  <java-symbol type="id" name="left_strip" />
-  <java-symbol type="id" name="right_strip" />
   <java-symbol type="id" name="keyguard_multi_user_selector" />
   <java-symbol type="id" name="status_security_message" />
-
+  <java-symbol type="id" name="sliding_layout" />
+  <java-symbol type="id" name="keyguard_add_widget" />
+  <java-symbol type="id" name="keyguard_add_widget_view" />
+  <java-symbol type="id" name="sliding_layout" />
+  <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="integer" name="config_carDockRotation" />
   <java-symbol type="integer" name="config_defaultUiModeType" />
   <java-symbol type="integer" name="config_deskDockRotation" />
@@ -1318,6 +1335,7 @@
   <java-symbol type="integer" name="config_lidOpenRotation" />
   <java-symbol type="integer" name="config_longPressOnHomeBehavior" />
   <java-symbol type="integer" name="kg_security_flip_duration" />
+  <java-symbol type="integer" name="kg_carousel_angle" />
   <java-symbol type="layout" name="global_actions_item" />
   <java-symbol type="layout" name="global_actions_silent_mode" />
   <java-symbol type="layout" name="keyguard_screen_glogin_unlock" />
@@ -1334,6 +1352,7 @@
   <java-symbol type="layout" name="keyguard_selector_view" />
   <java-symbol type="layout" name="keyguard_pattern_view" />
   <java-symbol type="layout" name="keyguard_password_view" />
+  <java-symbol type="layout" name="keyguard_pin_view" />
   <java-symbol type="layout" name="keyguard_face_unlock_view" />
   <java-symbol type="layout" name="keyguard_sim_pin_view" />
   <java-symbol type="layout" name="keyguard_sim_puk_view" />
@@ -1403,6 +1422,9 @@
   <java-symbol type="style" name="Animation.LockScreen" />
   <java-symbol type="style" name="Theme.Dialog.RecentApplications" />
   <java-symbol type="style" name="Theme.ExpandedMenu" />
+  <java-symbol type="style" name="Widget.Button.NumPadKey" />
+  <java-symbol type="style" name="TextAppearance.NumPadKey" />
+  <java-symbol type="style" name="TextAppearance.NumPadKey.Klondike" />
   <java-symbol type="string" name="kg_emergency_call_label" />
   <java-symbol type="string" name="kg_forgot_pattern_button_text" />
   <java-symbol type="string" name="kg_wrong_pattern" />
diff --git a/docs/downloads/training/Animations.zip b/docs/downloads/training/Animations.zip
new file mode 100644
index 0000000..5063dd1
--- /dev/null
+++ b/docs/downloads/training/Animations.zip
Binary files differ
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index 14ab5d5..9c21b3a 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -23,8 +23,6 @@
     <a class="close" onclick="$('#player-wrapper').hide()">close video</a>
   </div>
 </div>
-
-
 <div class="wrap">
    <!-- Slideshow -->
    <div class="slideshow-container slideshow-develop col-16">
@@ -32,6 +30,45 @@
        <a href="" class="slideshow-next">Next</a>
        <div class="frame">
            <ul>
+              <li class="item carousel-home">
+                 <div class="col-8">
+                   <img
+src="http://4.bp.blogspot.com/-lfjzgG5Dqrk/UHMThRtpRwI/AAAAAAAABpk/h4d3nsmkgPM/s400/mint.png"
+class="play no-shadow no-transform" />
+                 </div>
+                <div class="content-right col-6">
+                  <h2>Building Great Apps for Tablets</h2>
+                  <p>Tablets are a growing part of the Android installed base and they offer new opportunities for user engagement and monetization. If you are targeting tablets, check out the <strong>Tablet App Quality Checklist</strong> for tips and techniques on how to deliver a great app experience for tablet users.  </p>
+                  <p><a
+href="/distribute/googleplay/quality/tablet.html" class="button">Read
+more</a></p>
+                </div>            
+              </li>
+               <li class="item carousel-home">
+                   <div class="col-8">
+                     <img
+src="http://1.bp.blogspot.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
+                   <div class="content-right col-6">
+                   <h2>In-app Subscriptions with Trials</h2>
+                   <p>You can now set up a <strong>free trial period</strong> for any Google Play in-app subscription, making it easy for users try your subscriber content before automatically converting to a full subscription. Free trials give you a new way to bring users into your products and engage them effectively. </p>
+                   <p><a class="button"
+href="http://dirkbd.mtv:8809/guide/google/play/billing/billing_subscriptions.html#trials">Read
+more</a></p>
+                   </div>                
+                </li>
+               <li class="item carousel-home">
+                   <div class="col-8">
+                     <img
+src="http://2.bp.blogspot.com/-MgN5DnoO5XU/UHYGYzTcAOI/AAAAAAAABs4/jTS7sKkfBcM/s1600/pubsites.png" class="play"></div>
+                   <div class="content-right col-6">
+                   <p class="title-intro">From the blog:</p>
+                   <h2>New Google Play Developer Console</h2>
+                   <p>All developers can now try the <strong>new Google Play Developer Console</strong>. With a streamlined publishing flow, new language options, and new user ratings statistics, you’ll have better tools for delivering great Android apps that delight users.</p>
+                  <p><a
+href="http://android-developers.blogspot.com/2012/10/new-google-play-developer-console.html" class="button">Read
+more</a></p>
+                   </div>                
+                </li>
                <li class="item carousel-home">
                  <div class="col-8">
                    <img
@@ -42,60 +79,14 @@
                   <p class="title-intro">From the blog:</p>
                   <h2>Getting Your App Ready for Jelly Bean and Nexus 7</h2>
                   <p>For many people, their first taste of Jelly Bean will be on the beautiful 
-                    Nexus 7. While most applications will run just fine on Nexus 7, who wants 
-                    their app to be just fine? Here are some tips for optimizing your application 
+                    <strong>Nexus 7 tablet</strong>. Most applications will run just fine on Nexus 7, but who wants 
+                    their app to be just fine? Here are some tips for optimizing your app 
                     to make the most of this device.</p>
                   <p><a
 href="http://android-developers.blogspot.com/2012/07/getting-your-app-ready-for-jelly-bean.html" class="button">Read
 more</a></p>
                 </div>            
               </li>
-               <li class="item carousel-home">
-                 <div class="col-8">
-                   <img
-src="http://1.bp.blogspot.com/-6qyjPxTuzv0/T6lde-Oq_fI/AAAAAAAABXc/zle7OFEGP44/s400/fddns%2Bcopy.png"
-class="play no-shadow no-transform" />
-                 </div>
-                <div class="content-right col-6">
-                  <p class="title-intro">From the blog:</p>
-                  <h2>Using DialogFragments</h2>
-                  <p>In this post, I'll show how to use DialogFragments with the <a
-href="http://developer.android.com/reference/android/support/v4/app/DialogFragment.html">v4 support
-library</a> (for backward compatibility on pre-Honeycomb devices) to show a simple edit dialog and
-return a result to the calling Activity using an interface.</p>
-                  <p><a
-href="http://android-developers.blogspot.com/2012/05/using-dialogfragments.html" class="button">Read
-more</a></p>
-                </div>            
-              </li>
-               <li class="item carousel-home">
-                   <div class="col-8">
-                     <img
-src="http://1.bp.blogspot.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
-                   <div class="content-right col-6">
-                   <p class="title-intro">From the blog:</p>
-                   <h2>In-app Subscriptions in Google Play</h2>
-                   <p>Starting today, developers can use In-app Billing to sell monthly or annual
-subscriptions from inside of their apps. All subscriptions are auto-renewing, for every app and game
-and every type of subscription product.</p>
-                   <p><a class="button"
-href="http://android-developers.blogspot.com/2012/05/in-app-subscriptions-in-google-play.html">Read
-more</a></p>
-                   </div>                
-                </li>
-               <li class="item carousel-home">
-                   <div class="col-8">
-                     <img
-src="{@docRoot}images/home/developers_live.png" class="play"></div>
-                   <div class="content-right col-6">
-                   <h2>Learn what great apps are made of</h2>
-                   <p>Every week we host a live broadcast in which we review a collection of apps and games
-                     nominated by the creators. It's no-holds-barred and we tell you exactly what is flawed or
-                     fantastic in each app and how to make improvements.</p>
-                   <p><a href="" class="button" onclick="$('ul#DevelopersLive li:first
-a').click();return false;">Watch the latest review</a></p>
-                   </div>                
-                </li>
            </ul>
        </div>
    </div>
@@ -111,53 +102,47 @@
 		<div class="feed-container">
 			<div class="feed-frame">
                                 <!-- DEVELOPER NEWS -->
-				<ul>
-					<li><a href="http://android-developers.blogspot.com/2012/06/android-sdk-tools-revision-20.html">
-                                          <div class="feed-image" style="background:url('http://1.bp.blogspot.com/-Kp1qE5du6l8/T-xurIjfPgI/AAAAAAAABAM/kuWQwPgi0rw/s640/newactivity+(1).png') no-repeat 0 0">
-                                          </div>
-                                          <h4>Android SDK Tools, Revision 20</h4>
-                                          <p>Along with the preview of the Android 4.1 (Jelly Bean) platform, we launched Android SDK Tools R20 and ADT 20.0.0. Here are a few things...</p>
-					</a></li>
-					<li><a href="http://android-developers.blogspot.com/2012/04/faster-emulator-with-better-hardware.html">
-                                          <div class="feed-image" style="background:url('../images/emulator-wvga800l.png') no-repeat 0 0">
-                                          </div>
-                                          <h4>A Faster Emulator with Better...</h4>
-                                          <p>Today we’re thrilled to announce several significant improvements to the emulator, including a dramatic...</p>
-					</a></li>
-					<li><a href="http://android-developers.blogspot.com/2012/04/android-c2dm-client-login-key.html">
-                                          <div class="feed-image" style="background:url('../images/develop/auth-code.png') no-repeat 0 0">
-                                          </div>
-                                          <h4>Android C2DM — Client Login key...</h4>
-                                          <p>In the upcoming weeks, some of the older Client
-					Login authentication keys will expire. If you generated the token you’re...</p>
-					</a></li>
-					<li><a href="http://android-developers.blogspot.com/2012/04/accessibility-are-you-serving-all-your.html">
-                                          <div class="feed-image">
-                                          </div>
-                                          <h4>Accessibility</h4>
-                                          <p>We recently published some new resources to help developers make their Android applications more accessible... </p>
-					</a></li>
-                                      
-				</ul>
+          <ul>
+            <li><a href="http://android-developers.blogspot.com/2012/10/google-play-seller-support-in-india.html">
+              <div class="feed-image" style="background:url('http://4.bp.blogspot.com/-ekT-9XQi0YY/UH7WT2XjSdI/AAAAAAAABwc/fI5QaPi7QEk/s320/india-apps1.png') no-repeat 0 0"></div>
+              <h4>Google Play Seller Support in India</h4>
+              <p>Developers in India can sell paid applications, in-app products, and subscriptions in Google Play, with monthly payouts to their local bank accounts...</p>
+              </a></li>
+            <li><a href="http://android-developers.blogspot.com/2012/09/google-play-services-and-oauth-identity.html">
+              <div class="feed-image" style="background:url('https://lh4.ggpht.com/7z4NItEg-X21zvFGAarKonk-VaysBYthJ30u1JjaQ0-5fjyHNawnmoNeG--4FCACog=w124') no-repeat 0 0"></div>
+              <h4>Google Play services and OAuth Tools</h4>
+              <p>The rollout of Google Play services to all Android 2.2+ devices worldwide is now complete, and all of those devices now have new tools for working with OAuth 2.0 tokens...</p>
+              </a></li>
+            <li><a href="http://android-developers.blogspot.com/2012/08/creating-your-own-spelling-checker.html">
+              <div class="feed-image" style="background:url('http://2.bp.blogspot.com/-QKlztEdM1aA/UC1bH6Kv4UI/AAAAAAAAADo/fQS8-EfBYIQ/s320/spell-check-framed-new.png') no-repeat 0 0"></div>
+              <h4>Creating A Spelling Checker Service</h4>
+              <p>If you are an IME developer, the Spelling Checker framework gives you a great way to provide an even better experience for your users...</p>
+              </a></li>
+            <li><a href="http://android-developers.blogspot.com/2012/04/accessibility-are-you-serving-all-your.html">
+              <div class="feed-image"></div>
+              <h4>Accessibility</h4>
+              <p>We recently published some new resources to help developers make their Android applications more accessible... </p>
+              </a></li>                         
+          </ul>
                                 <!-- FEATURED DOCS -->
-				<ul>
-					<li><a href="{@docRoot}guide/google/play/billing/index.html">
-						<h4>Google Play In-app Billing</h4>
-						<p>In-app Billing is a Google Play service that lets you sell digital content from inside your applications. You can sell products as standard in-app products (one-time purchase) or with subscriptions (recurring...</p>
-					</a></li>
-					<li><a href="{@docRoot}guide/topics/providers/contacts-provider.html">
-						<h4>Contacts Provider</h4>
-						<p>The Contacts Provider is a powerful and flexible Android component that manages the device's central repository of data about people. You can use it to build powerful social features...</p>
-					</a></li>
-					<li><a href="{@docRoot}training/efficient-downloads/index.html">
-						<h4>Transferring Data Without Draining the Battery</h4>
-						<p>This training class demonstrates the best practices for scheduling and executing downloads using techniques such as caching, polling, and prefetching.</p>
-					</a></li>
-					<li><a href="{@docRoot}training/backward-compatible-ui/index.html">
-						<h4>Creating Backward-Compatible UIs</h4>
-						<p>This training class demonstrates how to use UI components and APIs available in newer versions of Android in a backward-compatible way, ensuring that your application still runs on previous versions...</p>
-					</a></li>
-				</ul>
+          <ul>
+            <li><a href="{@docRoot}distribute/googleplay/spotlight/tablets.html">
+              <h4>Tablet Stories</h4>
+              <p>More developers are investing in a full tablet experience for their apps. Here are some stories from developers who are seeing real results as they expand their offering to include Android tablets. </p>
+              </a></li>
+            <li><a href="{@docRoot}distribute/googleplay/quality/core.html">
+              <h4>Core App Quality Guidelines</h4>
+              <p>This document helps you assess basic aspects of quality in your app through a compact set of core app quality criteria and associated tests. All Android apps should meet these criteria.</p>
+              </a></li>
+            <li><a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">
+              <h4>Updated Notifications API Guide</h4>
+              <p>The Notifications API Guide is updated to include information about building Jelly Bean rich notifications using the Support Library APIs for backwards-compatibility.</p>
+              </a></li>
+            <li><a href="{@docRoot}guide/topics/ui/dialogs.html">
+              <h4>Updated Dialogs API Guide</h4>
+              <p>The Dialogs API Guide now shows to use DialogFragment class, a simpler way to manage your dialogs and embed them in alternative layouts.</p>
+              </a></li>                                      
+          </ul>
 			</div>
 		</div>
 	</div>	<!-- /news and feature feed -->
@@ -169,10 +154,10 @@
 		</ul>
 		<div class="feed-container">
 			<div class="feed-frame">
-				<ul id="DevelopersLive">
-				</ul>
-				<ul id="VideoPlaylists">
-				</ul>
+              <ul id="DevelopersLive">
+              </ul>
+              <ul id="VideoPlaylists">
+              </ul>
 			</div>
 		</div>
 	</div>
diff --git a/docs/html/training/animation/anim_card_flip.mp4 b/docs/html/training/animation/anim_card_flip.mp4
new file mode 100755
index 0000000..e885f98
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.ogv b/docs/html/training/animation/anim_card_flip.ogv
new file mode 100755
index 0000000..33cd86c
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.webm b/docs/html/training/animation/anim_card_flip.webm
new file mode 100755
index 0000000..a670d78
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.mp4 b/docs/html/training/animation/anim_crossfade.mp4
new file mode 100644
index 0000000..ced7cc9
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.ogv b/docs/html/training/animation/anim_crossfade.ogv
new file mode 100644
index 0000000..7ec417a
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.webm b/docs/html/training/animation/anim_crossfade.webm
new file mode 100644
index 0000000..21e7228
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.mp4 b/docs/html/training/animation/anim_layout_changes.mp4
new file mode 100644
index 0000000..0709601
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.ogv b/docs/html/training/animation/anim_layout_changes.ogv
new file mode 100644
index 0000000..75f5250
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.webm b/docs/html/training/animation/anim_layout_changes.webm
new file mode 100644
index 0000000..a99a566
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.mp4 b/docs/html/training/animation/anim_screenslide.mp4
new file mode 100755
index 0000000..3e65026
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.ogv b/docs/html/training/animation/anim_screenslide.ogv
new file mode 100755
index 0000000..c45ebd4
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.webm b/docs/html/training/animation/anim_screenslide.webm
new file mode 100755
index 0000000..c72adbc
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.mp4 b/docs/html/training/animation/anim_zoom.mp4
new file mode 100644
index 0000000..4326c35
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.ogv b/docs/html/training/animation/anim_zoom.ogv
new file mode 100644
index 0000000..e5793f3
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.webm b/docs/html/training/animation/anim_zoom.webm
new file mode 100644
index 0000000..b3b7566
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.webm
Binary files differ
diff --git a/docs/html/training/animation/cardflip.jd b/docs/html/training/animation/cardflip.jd
new file mode 100644
index 0000000..ab3eb3a
--- /dev/null
+++ b/docs/html/training/animation/cardflip.jd
@@ -0,0 +1,365 @@
+page.title=Displaying Card Flip Animations
+trainingnavtop=true
+
+@jd:body
+    <div id="tb-wrapper">
+      <div id="tb">
+        <h2>
+          This lesson teaches you to
+        </h2>
+        <ol>
+          <li>
+            <a href="#animators">Create the Animators</a>
+          </li>
+          <li>
+            <a href="#views">Create the Views</a>
+          </li>
+          <li>
+            <a href="#fragment">Create the Fragment</a>
+          </li>
+          <li>
+            <a href="#animate">Animate the Card Flip</a>
+          </li>
+        </ol>
+      </div>
+    </div>
+    <p> This lesson shows you how to do a card flip
+      animation with custom fragment animations.
+      Card flips animate between views of content by showing an animation that emulates
+      a card flipping over. 
+    </p>
+    <p>Here's what a card flip looks like:
+    </p>
+
+    <div class="framed-galaxynexus-land-span-8">
+      <video class="play-on-hover" autoplay>
+        <source src="anim_card_flip.mp4" type="video/mp4">
+        <source src="anim_card_flip.webm" type="video/webm">
+        <source src="anim_card_flip.ogv" type="video/ogg">
+      </video>
+    </div>
+    <div class="figure-caption">
+      Card flip animation
+      <div class="video-instructions">&nbsp;</div>
+    </div>
+
+    <p>
+      If you want to jump ahead and see a full working example,
+      <a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+      run the sample app and select the Card Flip example. See the following
+      files for the code implementation:
+    </p>
+    <ul>
+      <li>
+        <code>src/CardFlipActivity.java</code>
+      </li>
+      <li>
+        <code>animator/card_flip_right_in.xml</code>
+      </li>
+      <li>
+        <code>animator/card_flip_right_out.xml</code>
+      </li>
+      <li>
+        <code>animator/card_flip_right_in.xml</code>
+      </li>
+      <li>
+        <code>animator/card_flip_left_out.xml</code>
+      </li>
+      <li>
+        <code>layout/fragment_card_back.xml</code>
+      </li>
+      <li>
+        <code>layout/fragment_card_front.xml</code>
+      </li>
+    </ul>
+
+    <h2 id="animate">
+      Create the Animators
+    </h2>
+    <p>
+      Create the animations for the card flips. You'll need two animators for when the front
+      of the card animates out and to the left and in and from the left. You'll also need two animators
+      for when the back of the card animates in and from the right and out and to the right.
+    </p>
+    <h4>
+      card_flip_left_in.xml
+    </h4>
+<pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;!-- Before rotating, immediately set the alpha to 0. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
+        android:propertyName="alpha"
+        android:duration="0" /&gt;
+
+    &lt;!-- Rotate. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="-180"
+        android:valueTo="0"
+        android:propertyName="rotationY"
+        android:interpolator="@android:interpolator/accelerate_decelerate"
+        android:duration="@integer/card_flip_time_full" /&gt;
+
+    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:propertyName="alpha"
+        android:startOffset="@integer/card_flip_time_half"
+        android:duration="1" /&gt;
+&lt;/set&gt;
+</pre>
+    <h4>
+      card_flip_left_out.xml
+    </h4>
+    <pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;!-- Rotate. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="0"
+        android:valueTo="180"
+        android:propertyName="rotationY"
+        android:interpolator="@android:interpolator/accelerate_decelerate"
+        android:duration="@integer/card_flip_time_full" /&gt;
+
+    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
+        android:propertyName="alpha"
+        android:startOffset="@integer/card_flip_time_half"
+        android:duration="1" /&gt;
+&lt;/set&gt;
+    </pre>
+    <h4>
+      card_flip_right_in.xml
+    </h4>
+    <pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;!-- Before rotating, immediately set the alpha to 0. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
+        android:propertyName="alpha"
+        android:duration="0" /&gt;
+
+    &lt;!-- Rotate. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="180"
+        android:valueTo="0"
+        android:propertyName="rotationY"
+        android:interpolator="@android:interpolator/accelerate_decelerate"
+        android:duration="@integer/card_flip_time_full" /&gt;
+
+    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="0.0"
+        android:valueTo="1.0"
+        android:propertyName="alpha"
+        android:startOffset="@integer/card_flip_time_half"
+        android:duration="1" /&gt;
+</set>
+
+</pre>
+    <h4>
+      card_flip_right_out.xml
+    </h4>
+    <pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;!-- Rotate. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="0"
+        android:valueTo="-180"
+        android:propertyName="rotationY"
+        android:interpolator="@android:interpolator/accelerate_decelerate"
+        android:duration="@integer/card_flip_time_full" /&gt;
+
+    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;
+    &lt;objectAnimator
+        android:valueFrom="1.0"
+        android:valueTo="0.0"
+        android:propertyName="alpha"
+        android:startOffset="@integer/card_flip_time_half"
+        android:duration="1" /&gt;
+&lt;/set&gt;
+</pre>
+    <h2 id="views">
+      Create the Views
+    </h2>
+    <p>
+      Each side of the "card" is a separate layout that can contain any content you want,
+      such as two screens of text, two images, or any combination of views to flip between. You'll then
+      use the two layouts in the fragments that you'll later animate. The following layouts
+      create one side of a card that shows text:
+    </p>
+
+    <pre>
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="#a6c"
+    android:padding="16dp"
+    android:gravity="bottom"&gt;
+
+    &lt;TextView android:id="@android:id/text1"
+        style="?android:textAppearanceLarge"
+        android:textStyle="bold"
+        android:textColor="#fff"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/card_back_title" /&gt;
+
+    &lt;TextView style="?android:textAppearanceSmall"
+        android:textAllCaps="true"
+        android:textColor="#80ffffff"
+        android:textStyle="bold"
+        android:lineSpacingMultiplier="1.2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/card_back_description" /&gt;
+
+&lt;/LinearLayout&gt;
+</pre>
+<p>
+and the other side of the card that displays an {@link android.widget.ImageView}:
+</p>
+<pre>
+&lt;ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:src="@drawable/image1"
+    android:scaleType="centerCrop"
+    android:contentDescription="@string/description_image_1" /&gt;
+</pre>
+    <h2 id="fragment">
+      Create the Fragment
+    </h2>
+    <p>
+      Create fragment classes for the front and back of the card. These classes return the layouts
+      that you created previously in the {@link android.app.Fragment#onCreateView onCreateView()} method
+      of each fragment. You can then create instances of this fragment in the parent activity
+      where you want to show the card. The following example shows nested fragment classes inside
+      of the parent activity that uses them:
+    </p>
+    <pre>
+public class CardFlipActivity extends Activity {
+    ...
+    /**
+     * A fragment representing the front of the card.
+     */
+    public class CardFrontFragment extends Fragment {
+        &#64;Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            return inflater.inflate(R.layout.fragment_card_front, container, false);
+        }
+    }
+
+    /**
+     * A fragment representing the back of the card.
+     */
+    public class CardBackFragment extends Fragment {
+        &#64;Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            return inflater.inflate(R.layout.fragment_card_back, container, false);
+        }
+    }
+}
+</pre>
+    <h2 id="animate">
+      Animate the Card Flip
+    </h2>
+
+    <p> Now, you'll need to display the fragments inside of a parent activity.
+    To do this, first create the layout for your activity. The following example creates a
+    {@link android.widget.FrameLayout} that you
+    can add fragments to at runtime:</p>
+
+    <pre>
+&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" /&gt;
+</pre>
+
+    <p>In the activity code, set the content view to be the layout that you just created. It's also
+      good idea to show a default fragment when the activity is created, so the following example
+      activity shows you how to display the front of the card by default:
+    </p>
+
+
+
+<pre>
+public class CardFlipActivity extends Activity {
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_activity_card_flip);
+
+        if (savedInstanceState == null) {
+            getFragmentManager()
+                    .beginTransaction()
+                    .add(R.id.container, new CardFrontFragment())
+                    .commit();
+        }
+    }
+    ...
+}
+</pre>
+    <p>
+      Now that you have the front of the card showing, you can show the back of the card
+      with the flip animation at an appropriate time. Create a method to show the other
+      side of the card that does the following things:
+    </p>
+    <ul>
+      <li>Sets the custom animations that you created earlier for the fragment transitions.
+      </li>
+      <li>Replaces the currently displayed fragment with a new fragment and animates this event
+      with the custom animations that you created.
+      </li>
+      <li>Adds the previously displayed fragment to the fragment back stack
+      so when the user presses the <em>Back</em> button, the card flips back over.
+      </li>
+    </ul>
+    <pre>
+private void flipCard() {
+    if (mShowingBack) {
+        getFragmentManager().popBackStack();
+        return;
+    }
+
+    // Flip to the back.
+
+    mShowingBack = true;
+
+    // Create and commit a new fragment transaction that adds the fragment for the back of
+    // the card, uses custom animations, and is part of the fragment manager's back stack.
+
+    getFragmentManager()
+            .beginTransaction()
+
+            // Replace the default fragment animations with animator resources representing
+            // rotations when switching to the back of the card, as well as animator
+            // resources representing rotations when flipping back to the front (e.g. when
+            // the system Back button is pressed).
+            .setCustomAnimations(
+                    R.animator.card_flip_right_in, R.animator.card_flip_right_out,
+                    R.animator.card_flip_left_in, R.animator.card_flip_left_out)
+
+            // Replace any fragments currently in the container view with a fragment
+            // representing the next page (indicated by the just-incremented currentPage
+            // variable).
+            .replace(R.id.container, new CardBackFragment())
+
+            // Add this transaction to the back stack, allowing users to press Back
+            // to get to the front of the card.
+            .addToBackStack(null)
+
+            // Commit the transaction.
+            .commit();
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/animation/crossfade.jd b/docs/html/training/animation/crossfade.jd
new file mode 100644
index 0000000..99e879b
--- /dev/null
+++ b/docs/html/training/animation/crossfade.jd
@@ -0,0 +1,208 @@
+page.title=Crossfading Two Views
+trainingnavtop=true
+
+
+@jd:body
+
+    <div id="tb-wrapper">
+      <div id="tb">
+        <h2>
+          This lesson teaches you to:
+        </h2>
+        <ol>
+          <li>
+            <a href="#views">Create the Views</a>
+          </li>
+          <li>
+            <a href="#setup">Set up the Animation</a>
+          </li>
+          <li>
+            <a href="#animate">Crossfade the Views</a>
+          </li>
+        </ol>
+    </div>
+    </div>
+
+    <p>
+      Crossfade animations (also know as dissolve) gradually fade out one UI component while simultaneously fading in
+      another. This animation is useful for situations where you want to switch content or views
+      in your app. Crossfades are very subtle and short but offer a fluid transition from one screen to the
+      next. When you don't use them, however, transitions often feel abrupt or hurried.
+    </p>
+    <p>Here's an example of a crossfade from a progress indicator to some text content.
+    </p>
+
+<div class="framed-galaxynexus-land-span-8">
+  <video class="play-on-hover" autoplay>
+    <source src="anim_crossfade.mp4" type="video/mp4">
+    <source src="anim_crossfade.webm" type="video/webm">
+    <source src="anim_crossfade.ogv" type="video/ogg">
+  </video>
+</div>
+<div class="figure-caption">
+Crossfade animation
+  <div class="video-instructions">&nbsp;</div>
+</div>
+
+    <p>
+      If you want to jump ahead and see a full working example,
+      <a href="{@docRoot}shareables/training/Animations.zip">download</a>
+      and run the sample app and select the Crossfade example.
+      See the following files for the code implementation:
+    </p>
+    <ul>
+      <li>
+        <code>src/CrossfadeActivity.java</code>
+      </li>
+      <li>
+        <code>layout/activity_crossfade.xml</code>
+      </li>
+      <li>
+        <code>menu/activity_crossfade.xml</code>
+      </li>
+    </ul>
+    <h2 id="views">
+      Create the Views
+    </h2>
+    <p>
+      Create the two views that you want to crossfade. The following example creates a progress
+      indicator and a scrollable text view:
+    </p>
+    <pre>
+&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"&gt;
+
+    &lt;ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"&gt;
+
+        &lt;TextView style="?android:textAppearanceMedium"
+            android:lineSpacingMultiplier="1.2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/lorem_ipsum"
+            android:padding="16dp" /&gt;
+
+    &lt;/ScrollView&gt;
+
+    &lt;ProgressBar android:id="@+id/loading_spinner"
+        style="?android:progressBarStyleLarge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center" /&gt;
+
+&lt;/FrameLayout&gt;
+</pre>
+    <h2 id="setup">
+      Set up the Animation
+    </h2>
+    <p>
+      To set up the animation:
+    </p>
+    <ol>
+      <li>Create member variables for the views that you want to crossfade. You need
+      these references later when modifying the views during the animation.
+      </li>
+      <li>For the view that is being faded in, set its visibility to {@link
+      android.view.View#GONE}. This prevents the view from taking up layout space and omits it
+      from layout calculations, speeding up processing.
+      </li>
+      <li>Cache the <code>{@link android.R.integer#config_shortAnimTime}</code>
+      system property in a member variable. This property defines a standard
+      "short" duration for the animation. This duration is ideal for subtle animations or
+      animations that occur very frequently. {@link android.R.integer#config_longAnimTime} and
+      {@link android.R.integer#config_mediumAnimTime} are also available if you wish to use them.
+      </li>
+    </ol>
+    <p>
+      Here's an example using the layout from the previous code snippet as the activity content
+      view:
+    </p>
+    <pre>
+public class CrossfadeActivity extends Activity {
+
+    private View mContentView;
+    private View mLoadingView;
+    private int mShortAnimationDuration;
+
+    ...
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_crossfade);
+
+        mContentView = findViewById(R.id.content);
+        mLoadingView = findViewById(R.id.loading_spinner);
+
+        // Initially hide the content view.
+        mContentView.setVisibility(View.GONE);
+
+        // Retrieve and cache the system's default "short" animation time.
+        mShortAnimationDuration = getResources().getInteger(
+                android.R.integer.config_shortAnimTime);
+    }
+
+</pre>
+    <h2 id="animate">
+      Crossfade the Views
+    </h2>
+    <p>
+      Now that the views are properly set up, crossfade them by doing the following:
+    </p>
+    <ol>
+      <li>For the view that is fading in, set the alpha value to <code>0</code> and the visibility
+      to {@link android.view.View#VISIBLE}. (Remember that it was initially set to {@link
+      android.view.View#GONE}.) This makes the view visible but completely transparent.
+      </li>
+      <li>For the view that is fading in, animate its alpha value from <code>0</code> to
+      <code>1</code>. At the same time, for the view that is fading out, animate the alpha value
+      from <code>1</code> to <code>0</code>.
+      </li>
+      <li>Using {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()}
+      in an {@link android.animation.Animator.AnimatorListener}, set the visibility of the view
+      that was fading out to {@link android.view.View#GONE}. Even though the alpha value is <code>0</code>,
+      setting the view's visibility to {@link android.view.View#GONE} prevents the view from taking
+      up layout space and omits it from layout calculations, speeding up processing.
+      </li>
+    </ol>
+    <p>
+      The following method shows an example of how to do this:
+    </p>
+    <pre>
+private View mContentView;
+private View mLoadingView;
+private int mShortAnimationDuration;
+
+...
+
+private void crossfade() {
+
+    // Set the content view to 0% opacity but visible, so that it is visible
+    // (but fully transparent) during the animation.
+    mContentView.setAlpha(0f);
+    mContentView.setVisibility(View.VISIBLE);
+
+    // Animate the content view to 100% opacity, and clear any animation
+    // listener set on the view.
+    mContentView.animate()
+            .alpha(1f)
+            .setDuration(mShortAnimationDuration)
+            .setListener(null);
+
+    // Animate the loading view to 0% opacity. After the animation ends,
+    // set its visibility to GONE as an optimization step (it won't
+    // participate in layout passes, etc.)
+    mHideView.animate()
+            .alpha(0f)
+            .setDuration(mShortAnimationDuration)
+            .setListener(new AnimatorListenerAdapter() {
+                &#64;Override
+                public void onAnimationEnd(Animator animation) {
+                    mHideView.setVisibility(View.GONE);
+                }
+            });
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/animation/index.jd b/docs/html/training/animation/index.jd
new file mode 100644
index 0000000..9cc7e6c
--- /dev/null
+++ b/docs/html/training/animation/index.jd
@@ -0,0 +1,86 @@
+page.title=Adding Animations
+trainingnavtop=true
+startpage=true
+
+@jd:body
+    <div id="tb-wrapper">
+      <div id="tb">
+        <h2>
+          Dependencies and prerequisites
+        </h2>
+        <ul>
+          <li>Android 4.0 or later
+          </li>
+          <li>Experience building an Android <a href="{@docRoot}guide/topics/ui/index.html">User
+          Interface</a>
+          </li>
+        </ul>
+        <h2>
+          You should also read
+        </h2>
+        <ul>
+          <li>
+            <a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>
+          </li>
+        </ul>
+        <h2>
+          Try it out
+        </h2>
+        <div class="download-box">
+          <a href="{@docRoot}shareables/training/Animations.zip" class=
+          "button">Download the sample app</a>
+          <p class="filename">
+            Animations.zip
+          </p>
+        </div>
+      </div>
+    </div>
+    <p>
+      Animations can add subtle visual cues that notify users about what's going on in your app and
+      improve their mental model of your app's interface. Animations are especially useful when the
+      screen changes state, such as when content loads or new actions become available. Animations
+      can also add a polished look to your app, which gives your app a higher quality feel.
+    </p>
+    <p>
+      Keep in mind though, that overusing animations or using them at the wrong time can be
+      detrimental, such as when they cause delays. This training class shows you how to
+      implement some common types of animations that can increase usability and add flair without
+      annoying your users.
+    </p>
+
+    <h2>
+      Lessons
+    </h2>
+    <dl>
+      <dt>
+        <b><a href="crossfade.html">Crossfading Two Views</a></b>
+      </dt>
+      <dd>
+        Learn how to crossfade between two overlapping views. This lesson shows you how to crossfade a progress
+        indicator to a view that contains text content.
+      </dd>
+      <dt>
+        <b><a href="screen-slide.html">Using ViewPager for Screen Slides</a></b>
+      </dt>
+      <dd>
+        Learn how to animate between horizontally adjacent screens with a sliding transition.
+      </dd>
+      <dt>
+        <b><a href="cardflip.html">Displaying Card Flip Animations</a></b>
+      </dt>
+      <dd>
+        Learn how to animate between two views with a flipping motion.
+      </dd>
+      <dt>
+        <b><a href="zoom.html">Zooming a View</a></b>
+      </dt>
+      <dd>
+        Learn how to enlarge views with a touch-to-zoom animation.
+      </dd>
+      <dt>
+        <b><a href="layout.html">Animating Layout Changes</a></b>
+      </dt>
+      <dd>
+        Learn how to enable built-in animations when adding, removing, or updating child views in a layout.
+      </dd>
+    </dl>
\ No newline at end of file
diff --git a/docs/html/training/animation/layout.jd b/docs/html/training/animation/layout.jd
new file mode 100644
index 0000000..b8e0077
--- /dev/null
+++ b/docs/html/training/animation/layout.jd
@@ -0,0 +1,77 @@
+page.title=Animating Layout Changes
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+  <ol>
+    <li><a href="#views">Create the Layout</a></li>
+    <li><a href="#add">Add, Update, or Remove Items from the Layout</a></li>
+  </ol>
+
+</div>
+</div>
+
+  <p>A layout animation is a pre-loaded animation that the system runs each time you make a change
+  to the layout configuration. All you need to do is set an attribute in the layout to tell the
+  Android system to animate these layout changes, and system-default animations are carried out for you.
+  </p>
+<p class="note"><strong>Tip</strong>: If you want to supply custom layout animations,
+create a {@link android.animation.LayoutTransition} object and supply it to
+the layout with the {@link android.view.ViewGroup#setLayoutTransition setLayoutTransition()}
+method.
+</p>
+  Here's what a default layout animation looks like when adding items to a list:
+</p>
+
+    <div class="framed-galaxynexus-land-span-8">
+      <video class="play-on-hover" autoplay>
+        <source src="anim_layout_changes.mp4" type="video/mp4">
+        <source src="anim_layout_changes.webm" type="video/webm">
+        <source src="anim_layout_changes.ogv" type="video/ogg">
+      </video>
+    </div>
+    <div class="figure-caption">
+      Layout animation
+      <div class="video-instructions">&nbsp;</div>
+    </div>
+
+<p>If you want to jump ahead and see a full working example,
+<a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+run the sample app and select the Crossfade example. See the following files for the
+code implementation:</p>
+<ol>
+  <li><code>src/LayoutChangesActivity.java</code></li>
+  <li><code>layout/activity_layout_changes.xml</code></li>
+  <li><code>menu/activity_layout_changes.xml</code></li>
+</ol>
+
+<h2 id="views">Create the Layout</h2>
+<p>In your activity's layout XML file, set the <code>android:animateLayoutChanges</code>
+    attribute to <code>true</code> for the layout that you want to enable animations for.
+    For instance:</p>
+
+<pre>
+&lt;LinearLayout android:id="@+id/container"
+    android:animateLayoutChanges="true"
+    ...
+/&gt;
+</pre>
+
+<h2 id="activity">Add, Update, or Remove Items from the Layout</h2>
+<p>
+Now, all you need to do is add, remove, or update items in the layout
+and the items are animated automatically:
+</p>
+<pre>
+private ViewGroup mContainerView;
+...
+private void addItem() {
+    View newView;
+    ...
+    mContainerView.addView(newView, 0);
+}
+</pre>
diff --git a/docs/html/training/animation/screen-slide.jd b/docs/html/training/animation/screen-slide.jd
new file mode 100755
index 0000000..8a7af67
--- /dev/null
+++ b/docs/html/training/animation/screen-slide.jd
@@ -0,0 +1,185 @@
+page.title=Using ViewPager for Screen Slides
+trainingnavtop=true
+
+@jd:body
+
+  <div id="tb-wrapper">
+    <div id="tb">
+      <h2>This lesson teaches you to</h2>
+         <ol>
+            <li><a href="#views">Create the Views</a></li>
+            <li><a href="#fragment">Create the Fragment</a></li>
+            <li><a href="#viewpager">Animate the Screen Slide</a></li>
+        </ol>
+    </div>
+  </div>
+  <p>
+      Screen slides are transitions between one entire screen to another and are common with UIs
+      like setup wizards or slideshows. This lesson shows you how to do screen slides with
+      a {@link android.support.v4.view.ViewPager} provided by the <a href=
+      "{@docRoot}/tools/extras/support-library.html">support library</a>.
+      {@link android.support.v4.view.ViewPager}s can animate screen slides
+      automatically. Here's what a screen slide looks like that transitions from
+      one screen of content to the next:
+    </p>
+
+    <div class="framed-galaxynexus-land-span-8">
+      <video class="play-on-hover" autoplay>
+        <source src="anim_screenslide.mp4" type="video/mp4">
+        <source src="anim_screenslide.webm" type="video/webm">
+        <source src="anim_screenslide.ogv" type="video/ogg">
+      </video>
+    </div>
+
+    <div class="figure-caption">
+      Screen slide animation
+      <div class="video-instructions">&nbsp;</div>
+    </div>
+
+<p>If you want to jump ahead and see a full working example,
+<a href="{@docRoot}shareables/training/Animations.zip">download</a>
+and run the sample app and select the Screen Slide example. See the
+following files for the code implementation:</p>
+<ul>
+  <li><code>src/ScreenSlidePageFragment.java</code></li>
+  <li><code>src/ScreenSlideActivity.java</code></li>
+  <li><code>layout/activity_screen_slide.xml</code></li>
+  <li><code>layout/fragment_screen_slide_page.xml</code></li>
+</ul>
+
+<h2 id="views">Create the Views</h2>
+  <p>Create a layout file that you'll later use for the content of a fragment. The following example
+    contains a text view to display some text:
+
+<pre>
+&lt;com.example.android.animationsdemo.ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"&gt;
+
+        &lt;TextView style="?android:textAppearanceMedium"
+            android:padding="16dp"
+            android:lineSpacingMultiplier="1.2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/lorem_ipsum" /&gt;
+
+&lt;/com.example.android.animationsdemo.ScrollView&gt;
+</pre>
+
+<h2 id="fragment">Create the Fragment</h2>
+<p>Create a {@link android.support.v4.app.Fragment} class that returns the layout
+that you just created in the {@link android.app.Fragment#onCreateView onCreateView()}
+  method. You can then create instances of this fragment in the parent activity whenever you need a new page to
+  display to the user:</p>
+
+
+<pre>
+public class ScreenSlidePageFragment extends Fragment {
+
+    &#64;Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        ViewGroup rootView = (ViewGroup) inflater.inflate(
+                R.layout.fragment_screen_slide_page, container, false);
+
+        return rootView;
+    }
+}
+</pre>
+
+<h2 id="viewpager">Screen Slides with ViewPager</h2>
+
+<p>{@link android.support.v4.view.ViewPager}s have built-in swipe gestures to transition
+  through pages, and they display screen slide animations by default, so you don't need to create any. {@link android.support.v4.view.ViewPager}s use
+{@link android.support.v4.view.PagerAdapter}s as a supply for new pages to display, so the {@link android.support.v4.view.PagerAdapter} will use the
+fragment class that you created earlier.
+  </p>
+
+<p>To begin, create a layout that contains a {@link android.support.v4.view.ViewPager}:</p>
+
+<pre>
+&lt;android.support.v4.view.ViewPager
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pager"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" /&gt;
+</pre>
+
+<p>Create an activity that does the following things:
+</p>
+
+<ul>
+  <li>Sets the content view to be the layout with the {@link android.support.v4.view.ViewPager}.</li>
+  <li>Create a class that extends the {@link android.support.v13.app.FragmentStatePagerAdapter} abstract class. Implement
+  the {@link android.support.v4.app.FragmentStatePagerAdapter#getItem getItem()} method to supply
+    instances of <code>ScreenSlidePageFragment</code> as new pages. The pager adapter also requires that you implement the
+    {@link android.support.v4.view.PagerAdapter#getCount getCount()} method, which returns the amount of pages the adapter will create (five in the example).
+  <li>Hooks up the {@link android.support.v4.view.PagerAdapter} to the {@link android.support.v4.view.ViewPager}</code>.</li>
+  <li>Handle's the device's back button by moving backwards in the virtual stack of fragments.
+    If the user is already on the first page, go back on the activity back stack.</li>
+</ul>
+
+<pre>
+public class ScreenSlidePagerActivity extends FragmentActivity {
+    /**
+     * The number of pages (wizard steps) to show in this demo.
+     */
+    private static final int NUM_PAGES = 5;
+
+    /**
+     * The pager widget, which handles animation and allows swiping horizontally to access previous
+     * and next wizard steps.
+     */
+    private ViewPager mPager;
+
+    /**
+     * The pager adapter, which provides the pages to the view pager widget.
+     */
+    private PagerAdapter mPagerAdapter;
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_screen_slide_pager);
+
+        // Instantiate a ViewPager and a PagerAdapter.
+        mPager = (ViewPager) findViewById(R.id.pager);
+        mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
+        mPager.setAdapter(mPagerAdapter);
+    }
+
+    &#64;Override
+    public void onBackPressed() {
+        if (mPager.getCurrentItem() == 0) {
+            // If the user is currently looking at the first step, allow the system to handle the
+            // Back button. This calls finish() on this activity and pops the back stack.
+            super.onBackPressed();
+        } else {
+            // Otherwise, select the previous step.
+            mPager.setCurrentItem(mPager.getCurrentItem() - 1);
+        }
+    }
+
+    /**
+     * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
+     * sequence.
+     */
+    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+        public ScreenSlidePagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        &#64;Override
+        public Fragment getItem(int position) {
+            return new ScreenSlidePageFragment();
+        }
+
+        &#64;Override
+        public int getCount() {
+            return NUM_PAGES;
+        }
+    }
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/animation/zoom.jd b/docs/html/training/animation/zoom.jd
new file mode 100644
index 0000000..5dc2b6c
--- /dev/null
+++ b/docs/html/training/animation/zoom.jd
@@ -0,0 +1,320 @@
+page.title=Zooming a View
+trainingnavtop=true
+
+@jd:body
+
+    <div id="tb-wrapper">
+      <div id="tb">
+        <h2>
+          This lesson teaches you to:
+        </h2>
+        <ol>
+          <li>
+            <a href="#views">Create the Views</a>
+          </li>
+          <li>
+            <a href="#setup">Set up the Zoom Animation</a>
+          </li>
+          <li>
+            <a href="#animate">Zoom the View</a>
+          </li>
+        </ol>
+      </div>
+    </div>
+    <p>
+      This lesson demonstrates how to do a touch-to-zoom animation, which is useful for apps such as photo
+      galleries to animate a view from a thumbnail to a full-size image that fills the screen.
+    </p>
+    <p>Here's what a touch-to-zoom animation looks like that
+      expands an image thumbnail to fill the screen:
+    </p>
+
+    <div class="framed-galaxynexus-land-span-8">
+      <video class="play-on-hover" autoplay>
+        <source src="anim_zoom.mp4" type="video/mp4">
+        <source src="anim_zoom.webm" type="video/webm">
+        <source src="anim_zoom.ogv" type="video/ogg">
+      </video>
+    </div>
+    <div class="figure-caption">
+      Zoom animation
+      <div class="video-instructions">&nbsp;</div>
+    </div>
+
+    <p>
+      If you want to jump ahead and see a full working example,
+      <a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+      run the sample app and select the
+      Zoom example. See the following files for the code implementation:
+    </p>
+    <ul>
+      <li>
+        <code>src/TouchHighlightImageButton.java</code> (a simple helper class that shows a blue
+        touch highlight when the image button is pressed)
+      </li>
+      <li>
+        <code>src/ZoomActivity.java</code>
+      </li>
+      <li>
+        <code>layout/activity_zoom.xml</code>
+      </li>
+    </ul>
+    <h2 id="views">
+      Create the Views
+    </h2>
+    <p>
+      Create a layout file that contains the small and large version of the content that you want
+      to zoom. The following example creates an {@link android.widget.ImageButton} for clickable image thumbnail
+      and an {@link android.widget.ImageView} that displays the enlarged view of the image:
+    </p>
+    <pre>
+&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"&gt;
+
+    &lt;LinearLayout android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:padding="16dp"&gt;
+
+        &lt;ImageButton
+            android:id="@+id/thumb_button_1"
+            android:layout_width="100dp"
+            android:layout_height="75dp"
+            android:layout_marginRight="1dp"
+            android:src="@drawable/thumb1"
+            android:scaleType="centerCrop"
+            android:contentDescription="@string/description_image_1" /&gt;
+
+    &lt;/LinearLayout&gt;
+
+    &lt;!-- This initially-hidden ImageView will hold the expanded/zoomed version of
+         the images above. Without transformations applied, it takes up the entire
+         screen. To achieve the "zoom" animation, this view's bounds are animated
+         from the bounds of the thumbnail button above, to its final laid-out
+         bounds.
+         --&gt;
+
+    &lt;ImageView
+        android:id="@+id/expanded_image"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible"
+        android:contentDescription="@string/description_zoom_touch_close" /&gt;
+
+&lt;/FrameLayout&gt;
+</pre>
+    <h2 id="setup">
+      Set up the Zoom Animation
+    </h2>
+    <p>
+      Once you apply your layout, set up the event handlers that trigger the zoom animation.
+      The following example adds a {@link android.view.View.OnClickListener} to the {@link
+      android.widget.ImageButton} to execute the zoom animation when the user
+      clicks the image button:
+    </p>
+    <pre>
+public class ZoomActivity extends FragmentActivity {
+    // Hold a reference to the current animator,
+    // so that it can be canceled mid-way.
+    private Animator mCurrentAnimator;
+
+    // The system "short" animation time duration, in milliseconds. This
+    // duration is ideal for subtle animations or animations that occur
+    // very frequently.
+    private int mShortAnimationDuration;
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_zoom);
+
+        // Hook up clicks on the thumbnail views.
+
+        final View thumb1View = findViewById(R.id.thumb_button_1);
+        thumb1View.setOnClickListener(new View.OnClickListener() {
+            &#64;Override
+            public void onClick(View view) {
+                zoomImageFromThumb(thumb1View, R.drawable.image1);
+            }
+        });
+
+        // Retrieve and cache the system's default "short" animation time.
+        mShortAnimationDuration = getResources().getInteger(
+                android.R.integer.config_shortAnimTime);
+    }
+    ...
+}
+</pre>
+    <h2 id="animate">
+      Zoom the View
+    </h2>
+    <p>
+      You'll now need to animate from the normal sized view to the zoomed view
+      when appropriate. In general, you need to animate from the bounds of the normal-sized view to the
+      bounds of the larger-sized view. The following method shows you how to implement a zoom animation that
+      zooms from an image thumbnail to an enlarged view by doing the following things:
+    </p>
+    <ol>
+      <li>Assign the high-res image to the hidden "zoomed-in" (enlarged) {@link
+      android.widget.ImageView}. The following example loads a large image resource on the UI
+      thread for simplicity. You will want to do this loading in a separate thread to prevent
+      blocking on the UI thread and then set the bitmap on the UI thread. Ideally, the bitmap
+      should not be larger than the screen size.
+      </li>
+      <li>Calculate the starting and ending bounds for the {@link android.widget.ImageView}.
+      </li>
+      <li>Animate each of the four positioning and sizing properties <code>{@link
+      android.view.View#X}</code>, <code>{@link android.view.View#Y}</code>, ({@link
+      android.view.View#SCALE_X}, and <code>{@link android.view.View#SCALE_Y}</code>)
+      simultaneously, from the starting bounds to the ending bounds. These four animations are
+      added to an {@link android.animation.AnimatorSet} so that they can be started at the same
+      time.
+      </li>
+      <li>Zoom back out by running a similar animation but in reverse when the user touches the
+      screen when the image is zoomed in. You can do this by adding a {@link
+      android.view.View.OnClickListener} to the {@link android.widget.ImageView}. When clicked, the
+      {@link android.widget.ImageView} minimizes back down to the size of the image thumbnail and
+      sets its visibility to {@link android.view.View#GONE} to hide it.
+      </li>
+    </ol>
+    <pre>
+private void zoomImageFromThumb(final View thumbView, int imageResId) {
+    // If there's an animation in progress, cancel it
+    // immediately and proceed with this one.
+    if (mCurrentAnimator != null) {
+        mCurrentAnimator.cancel();
+    }
+
+    // Load the high-resolution "zoomed-in" image.
+    final ImageView expandedImageView = (ImageView) findViewById(
+            R.id.expanded_image);
+    expandedImageView.setImageResource(imageResId);
+
+    // Calculate the starting and ending bounds for the zoomed-in image.
+    // This step involves lots of math. Yay, math.
+    final Rect startBounds = new Rect();
+    final Rect finalBounds = new Rect();
+    final Point globalOffset = new Point();
+
+    // The start bounds are the global visible rectangle of the thumbnail,
+    // and the final bounds are the global visible rectangle of the container
+    // view. Also set the container view's offset as the origin for the
+    // bounds, since that's the origin for the positioning animation
+    // properties (X, Y).
+    thumbView.getGlobalVisibleRect(startBounds);
+    findViewById(R.id.container)
+            .getGlobalVisibleRect(finalBounds, globalOffset);
+    startBounds.offset(-globalOffset.x, -globalOffset.y);
+    finalBounds.offset(-globalOffset.x, -globalOffset.y);
+
+    // Adjust the start bounds to be the same aspect ratio as the final
+    // bounds using the "center crop" technique. This prevents undesirable
+    // stretching during the animation. Also calculate the start scaling
+    // factor (the end scaling factor is always 1.0).
+    float startScale;
+    if ((float) finalBounds.width() / finalBounds.height()
+            &gt; (float) startBounds.width() / startBounds.height()) {
+        // Extend start bounds horizontally
+        startScale = (float) startBounds.height() / finalBounds.height();
+        float startWidth = startScale * finalBounds.width();
+        float deltaWidth = (startWidth - startBounds.width()) / 2;
+        startBounds.left -= deltaWidth;
+        startBounds.right += deltaWidth;
+    } else {
+        // Extend start bounds vertically
+        startScale = (float) startBounds.width() / finalBounds.width();
+        float startHeight = startScale * finalBounds.height();
+        float deltaHeight = (startHeight - startBounds.height()) / 2;
+        startBounds.top -= deltaHeight;
+        startBounds.bottom += deltaHeight;
+    }
+
+    // Hide the thumbnail and show the zoomed-in view. When the animation
+    // begins, it will position the zoomed-in view in the place of the
+    // thumbnail.
+    thumbView.setAlpha(0f);
+    expandedImageView.setVisibility(View.VISIBLE);
+
+    // Set the pivot point for SCALE_X and SCALE_Y transformations
+    // to the top-left corner of the zoomed-in view (the default
+    // is the center of the view).
+    expandedImageView.setPivotX(0f);
+    expandedImageView.setPivotY(0f);
+
+    // Construct and run the parallel animation of the four translation and
+    // scale properties (X, Y, SCALE_X, and SCALE_Y).
+    AnimatorSet set = new AnimatorSet();
+    set
+            .play(ObjectAnimator.ofFloat(expandedImageView, View.X,
+                    startBounds.left, finalBounds.left))
+            .with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
+                    startBounds.top, finalBounds.top))
+            .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,
+            startScale, 1f)).with(ObjectAnimator.ofFloat(expandedImageView,
+                    View.SCALE_Y, startScale, 1f));
+    set.setDuration(mShortAnimationDuration);
+    set.setInterpolator(new DecelerateInterpolator());
+    set.addListener(new AnimatorListenerAdapter() {
+        &#64;Override
+        public void onAnimationEnd(Animator animation) {
+            mCurrentAnimator = null;
+        }
+
+        &#64;Override
+        public void onAnimationCancel(Animator animation) {
+            mCurrentAnimator = null;
+        }
+    });
+    set.start();
+    mCurrentAnimator = set;
+
+    // Upon clicking the zoomed-in image, it should zoom back down
+    // to the original bounds and show the thumbnail instead of
+    // the expanded image.
+    final float startScaleFinal = startScale;
+    expandedImageView.setOnClickListener(new View.OnClickListener() {
+        &#64;Override
+        public void onClick(View view) {
+            if (mCurrentAnimator != null) {
+                mCurrentAnimator.cancel();
+            }
+
+            // Animate the four positioning/sizing properties in parallel,
+            // back to their original values.
+            AnimatorSet set = new AnimatorSet();
+            set.play(ObjectAnimator
+                        .ofFloat(expandedImageView, View.X, startBounds.left))
+                        .with(ObjectAnimator
+                                .ofFloat(expandedImageView, 
+                                        View.Y,startBounds.top))
+                        .with(ObjectAnimator
+                                .ofFloat(expandedImageView, 
+                                        View.SCALE_X, startScaleFinal))
+                        .with(ObjectAnimator
+                                .ofFloat(expandedImageView, 
+                                        View.SCALE_Y, startScaleFinal));
+            set.setDuration(mShortAnimationDuration);
+            set.setInterpolator(new DecelerateInterpolator());
+            set.addListener(new AnimatorListenerAdapter() {
+                &#64;Override
+                public void onAnimationEnd(Animator animation) {
+                    thumbView.setAlpha(1f);
+                    expandedImageView.setVisibility(View.GONE);
+                    mCurrentAnimator = null;
+                }
+
+                &#64;Override
+                public void onAnimationCancel(Animator animation) {
+                    thumbView.setAlpha(1f);
+                    expandedImageView.setVisibility(View.GONE);
+                    mCurrentAnimator = null;
+                }
+            });
+            set.start();
+            mCurrentAnimator = set;
+        }
+    });
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 4ad1353..4a5b0fa 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -287,6 +287,34 @@
           </li>
         </ul>
       </li>
+
+        <li class="nav-section">
+        <div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html">
+            <span class="en">Adding Animations</span>
+          </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/animation/crossfade.html">
+            <span class="en">Crossfading Two Views</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/animation/screen-slide.html">
+            <span class="en">Using ViewPager for Screen Slide</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/animation/cardflip.html">
+            <span class="en">Displaying Card Flip Animations</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/animation/zoom.html">
+            <span class="en">Zooming a View</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/animation/layout.html">
+            <span class="en">Animating Layout Changes</span>
+          </a>
+          </li>
+        </ul>
+      </li>
       
       <li class="nav-section">
         <div class="nav-section-header"><a href="<?cs var:toroot ?>training/managing-audio/index.html">
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
index 03cca84..b3e4a88 100644
--- a/location/java/android/location/Geofence.java
+++ b/location/java/android/location/Geofence.java
@@ -22,9 +22,7 @@
 /**
  * Represents a geographical boundary, also known as a geofence.
  *
- * <p>Currently only circular geofences are supported, but this object
- * is opaque so could be used in the future to represent polygons or other
- * shapes.
+ * <p>Currently only circular geofences are supported.
  */
 public final class Geofence implements Parcelable {
     /** @hide */
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 82ed432..2a5a16e 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -32,7 +32,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
-import android.view.DisplayInfo;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -53,7 +52,7 @@
 public class MediaRouter {
     private static final String TAG = "MediaRouter";
 
-    static class Static {
+    static class Static implements DisplayManager.DisplayListener {
         final Resources mResources;
         final IAudioService mAudioService;
         final DisplayManager mDisplayService;
@@ -105,6 +104,8 @@
             mDefaultAudioVideo = new RouteInfo(mSystemCategory);
             mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
             mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
+            mDefaultAudioVideo.mPresentationDisplay = choosePresentationDisplayForRoute(
+                    mDefaultAudioVideo, getAllPresentationDisplays());
             addRouteStatic(mDefaultAudioVideo);
 
             // This will select the active wifi display route if there is one.
@@ -115,6 +116,8 @@
             appContext.registerReceiver(new VolumeChangeReceiver(),
                     new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
 
+            mDisplayService.registerDisplayListener(this, mHandler);
+
             AudioRoutesInfo newAudioRoutes = null;
             try {
                 newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
@@ -191,6 +194,39 @@
                 }
             }
         }
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            updatePresentationDisplays(displayId);
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            updatePresentationDisplays(displayId);
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            updatePresentationDisplays(displayId);
+        }
+
+        public Display[] getAllPresentationDisplays() {
+            return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+        }
+
+        private void updatePresentationDisplays(int changedDisplayId) {
+            final Display[] displays = getAllPresentationDisplays();
+            final int count = mRoutes.size();
+            for (int i = 0; i < count; i++) {
+                final RouteInfo info = mRoutes.get(i);
+                Display display = choosePresentationDisplayForRoute(info, displays);
+                if (display != info.mPresentationDisplay
+                        || (display != null && display.getDisplayId() == changedDisplayId)) {
+                    info.mPresentationDisplay = display;
+                    dispatchRoutePresentationDisplayChanged(info);
+                }
+            }
+        }
     }
 
     static Static sStatic;
@@ -218,6 +254,9 @@
      * While remote routing is active the application may use a
      * {@link android.app.Presentation Presentation} to replace the mirrored view
      * on the external display with different content.</p>
+     *
+     * @see RouteInfo#getPresentationDisplay()
+     * @see android.app.Presentation
      */
     public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
 
@@ -239,6 +278,9 @@
         if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
             result.append("ROUTE_TYPE_LIVE_AUDIO ");
         }
+        if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+            result.append("ROUTE_TYPE_LIVE_VIDEO ");
+        }
         if ((types & ROUTE_TYPE_USER) != 0) {
             result.append("ROUTE_TYPE_USER ");
         }
@@ -671,6 +713,14 @@
         }
     }
 
+    static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
+        for (CallbackInfo cbi : sStatic.mCallbacks) {
+            if ((cbi.type & info.mSupportedTypes) != 0) {
+                cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
+            }
+        }
+    }
+
     static void systemVolumeChanged(int newValue) {
         final RouteInfo selectedRoute = sStatic.mSelectedRoute;
         if (selectedRoute == null) return;
@@ -752,6 +802,9 @@
         newRoute.mEnabled = available;
 
         newRoute.mName = display.getFriendlyDisplayName();
+
+        newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
+                sStatic.getAllPresentationDisplays());
         return newRoute;
     }
 
@@ -827,6 +880,27 @@
         return null;
     }
 
+    private static Display choosePresentationDisplayForRoute(RouteInfo route, Display[] displays) {
+        if ((route.mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+            if (route.mDeviceAddress != null) {
+                // Find the indicated Wifi display by its address.
+                for (Display display : displays) {
+                    if (display.getType() == Display.TYPE_WIFI
+                            && route.mDeviceAddress.equals(display.getAddress())) {
+                        return display;
+                    }
+                }
+                return null;
+            }
+
+            if (route == sStatic.mDefaultAudioVideo && displays.length > 0) {
+                // Choose the first presentation display from the list.
+                return displays[0];
+            }
+        }
+        return null;
+    }
+
     /**
      * Information about a media route.
      */
@@ -845,6 +919,7 @@
         int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
         int mPlaybackStream = AudioManager.STREAM_MUSIC;
         VolumeCallbackInfo mVcb;
+        Display mPresentationDisplay;
 
         String mDeviceAddress;
         boolean mEnabled = true;
@@ -1116,6 +1191,38 @@
         }
 
         /**
+         * Gets the {@link Display} that should be used by the application to show
+         * a {@link android.app.Presentation} on an external display when this route is selected.
+         * Depending on the route, this may only be valid if the route is currently
+         * selected.
+         * <p>
+         * The preferred presentation display may change independently of the route
+         * being selected or unselected.  For example, the presentation display
+         * of the default system route may change when an external HDMI display is connected
+         * or disconnected even though the route itself has not changed.
+         * </p><p>
+         * This method may return null if there is no external display associated with
+         * the route or if the display is not ready to show UI yet.
+         * </p><p>
+         * The application should listen for changes to the presentation display
+         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
+         * show or dismiss its {@link android.app.Presentation} accordingly when the display
+         * becomes available or is removed.
+         * </p><p>
+         * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
+         * </p>
+         *
+         * @return The preferred presentation display to use when this route is
+         * selected or null if none.
+         *
+         * @see #ROUTE_TYPE_LIVE_VIDEO
+         * @see android.app.Presentation
+         */
+        public Display getPresentationDisplay() {
+            return mPresentationDisplay;
+        }
+
+        /**
          * @return true if this route is enabled and may be selected
          */
         public boolean isEnabled() {
@@ -1156,9 +1263,11 @@
         @Override
         public String toString() {
             String supportedTypes = typesToString(getSupportedTypes());
-            return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() +
-                    " category=" + getCategory() +
-                    " supportedTypes=" + supportedTypes + "}";
+            return getClass().getSimpleName() + "{ name=" + getName() +
+                    ", status=" + getStatus() +
+                    ", category=" + getCategory() +
+                    ", supportedTypes=" + supportedTypes +
+                    ", presentationDisplay=" + mPresentationDisplay + "}";
         }
     }
 
@@ -1853,6 +1962,21 @@
          * @param info The route with altered volume
          */
         public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
+
+        /**
+         * Called when a route's presentation display changes.
+         * <p>
+         * This method is called whenever the route's presentation display becomes
+         * available, is removes or has changes to some of its properties (such as its size).
+         * </p>
+         *
+         * @param router the MediaRouter reporting the event
+         * @param info The route whose presentation display changed
+         *
+         * @see RouteInfo#getPresentationDisplay()
+         */
+        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
+        }
     }
 
     /**
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 02b5326..cfe70dc 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -214,7 +214,7 @@
 
         <activity android:name=".Somnambulator"
             android:label="@string/start_dreams"
-            android:icon="@mipmap/ic_daydreams"
+            android:icon="@mipmap/ic_launcher_dreams"
             android:theme="@android:style/Theme.Wallpaper.NoTitleBar"
             android:exported="true"
             android:excludeFromRecents="true"
diff --git a/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
deleted file mode 100644
index 56cbac1..0000000
--- a/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
index a335d6d..37185f3 100644
--- a/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
deleted file mode 100644
index ea3d991..0000000
--- a/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
index ef2e27b..1993b0d 100644
--- a/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
deleted file mode 100644
index ddc7f66..0000000
--- a/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
index 7b42cb4..c92b681 100644
--- a/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 69355d5..e5cc356 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skerm is in landskapsoriëntasie gesluit."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skerm is in portretoriëntasie gesluit."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Slaap nou"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Vliegtuigmodus"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Laai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 735da78..57ad48a 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ማያ ገጽ በወርድ ገፅ አቀማመጥ ተቆልፏል።"</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ማያ ገጽ በቁም ገፅ አቀማመጥ ተቆልፏል።"</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"አሁን ተኛ"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"የቀን ህልም"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"ኤተርኔት"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"የአውሮፕላን ሁነታ"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"ባትሪ በመሙላት ላይ፣ <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index da78d29..c40e26d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"تم تأمين الشاشة في الاتجاه الأفقي."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"تم تأمين الشاشة في الاتجاه العمودي."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"السكون الآن"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"حلم اليقظة"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"وضع الطائرة"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"جارٍ الشحن، <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 79bc53a..a4b4b73 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -176,7 +176,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Экран заблакiраваны ў альбомнай арыентацыі."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Экран заблакiраваны ў партрэтнай арыентацыі."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Засыпай"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Рэжым палёту"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Зарадка, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 59ba693..8876a38 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екранът е заключен в хоризонтална ориентация."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екранът е заключен във вертикална ориентация."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"В спящ режим сега"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Самолетен режим"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Зарежда се, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index bf10ce2..8d5cf36 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla està bloquejada en orientació horitzontal."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla està bloquejada en orientació vertical."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Entra en mode repòs"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Somnis despert"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode d\'avió"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"S\'està carregant, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 5e632fa..a623612 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamčena v orientaci na šířku."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamčena v orientaci na výšku."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Spát nyní"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Spořič obrazovky"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Režim V letadle"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nabíjení, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 671f292..ddb6265 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="6170089063982549905">"Gå i dvale nu"</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">"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-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 3e767a0..fc18361 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildschirm bleibt im Querformat."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildschirm bleibt im Hochformat."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Ruhemodus ein"</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">"Flugmodus"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Lädt, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0665020..abe6082 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Η οθόνη έχει κλειδωθεί σε οριζόντιο προσανατολισμό."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Η οθόνη έχει κλειδωθεί σε κατακόρυφο προσανατολισμό."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Ενεργ. αναστ. λειτ."</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">"Λειτουργία πτήσης"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Φόρτιση, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 906b5c6..890e05e 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Screen is locked in landscape orientation."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Screen is locked in portrait orientation."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Sleep Now"</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">"Aeroplane mode"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Charging, <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 9bbd5d7..8abf3a8 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -176,7 +176,8 @@
     <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="6170089063982549905">"Activar suspensión"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <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 90f93b2..79f0bb0 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="6170089063982549905">"Suspender"</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 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-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index d77b26a..5339006 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekraan on lukustatud horisontaalsuunas."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekraan on lukustatud vertikaalsuunas."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Nüüd unerežiimi"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Unistus"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lennurežiim"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Laadimine, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 6accc9f..aa80148 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"صفحه اکنون در جهت افقی قفل است."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"صفحه اکنون در جهت عمودی قفل است."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"اکنون خواب"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"رویاپردازی"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"اترنت"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"حالت هواپیما"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"در حال شارژ، <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3bc0371..d4b82f0 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ruutu on lukittu vaakasuuntaan."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ruutu on lukittu pystysuuntaan."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Virransäästötilaan"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Unelmoi"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lentokonetila"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Ladataan (<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 02eb529..e547478 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -176,7 +176,8 @@
     <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="6170089063982549905">"Mettre en veille"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <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-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e2bc2c3..cdcda79 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"स्‍क्रीन लैंडस्केप अभिविन्यास में लॉक है."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"स्‍क्रीन पोर्ट्रेट अभिविन्‍यास में लॉक है."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"अभी निष्क्रिय करें"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"ईथरनेट"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"हवाई जहाज़ मोड"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"चार्ज हो रही है, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e318120..cfa5d54 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaključan u pejzažnoj orijentaciji."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaključan u portretnoj orijentaciji."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Miruj sad"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Sanjarenje"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Način rada u zrakoplovu"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Puni se, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 3dafd2b..27bf2f5 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A képernyő zárolva van fekvő tájolásban."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A képernyő zárolva van álló tájolásban."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Alvó mód"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Álmodozás"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Repülőgép üzemmód"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Töltés (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index c26cdce..baa7b221 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Layar dikunci dalam orientasi lanskap."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Layar dikunci dalam orientasi potret."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Tidur Sekarang"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode pesawat"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Mengisi baterai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e9ac98f..812d1ac 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Lo schermo è bloccato in orientamento orizzontale."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Lo schermo è bloccato in orientamento verticale."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Sospendi ora"</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">"Modalità aereo"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"In carica (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 0481d63..150375d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"המסך נעול כעת לרוחב."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"המסך נעול כעת לאורך."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"עבור לשינה עכשיו"</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">"מצב טיסה"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"טוען (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 1b89e13..c427ac4 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -176,7 +176,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"画面は横向きにロックされています。"</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"画面は縦向きにロックされています。"</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"スリープ開始"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"イーサネット"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"機内モード"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"充電中: <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 71d305f..deafa45 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"화면이 가로 방향으로 잠겨 있습니다."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"화면이 세로 방향으로 잠겨 있습니다."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"절전 모드로 전환"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"이더넷"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"비행기 모드"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 76de197..02d0c5b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Užrakintas ekranas yra horizontalios orientacijos."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Užrakintas ekranas yra vertikalios orientacijos."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Įj. miego rež. dabar"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Svajonė"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Eternetas"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lėktuvo režimas"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Įkraunama, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 21299b2..57e70c8 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekrāns tagad ir bloķēts ainavas orientācijā."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekrāns tagad ir bloķēts portreta orientācijā."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Ieslēgt miega režīmu"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Ekrānsaudzētājs"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Tīkls Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Lidojuma režīms"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Notiek uzlāde, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 01751b0..7ce5b62 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrin dikunci dalam orientasi landskap."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrin dikunci dalam orientasi potret."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Tidur Sekarang"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mod kapal terbang"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Mengecas, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ccba8e5..abd7f84 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skjermen er låst i liggende retning."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skjermen er låst i stående retning."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Aktiver hvilemodus"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Flymodus"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Lader: <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 9978b76..c055aec 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Het scherm is nu vergrendeld in liggende stand."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Het scherm is nu vergrendeld in staande stand."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Nu slapen"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Dagdroom"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Vliegmodus"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Opladen, <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 98f13c7..a05aa8f 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="6170089063982549905">"Zaśnij teraz"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Śnij na jawie"</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 639201c..1ca085c 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -174,16 +174,16 @@
     <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="6170089063982549905">"Em Suspensão Agora"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Sonho"</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>
     <string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"Carregada"</string>
     <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Dispositivos)"</string>
-    <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Desat."</string>
+    <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth desat."</string>
     <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
-    <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rodar Automat."</string>
+    <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rodar automat."</string>
     <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotação Bloqueada"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de Introdução"</string>
     <string name="quick_settings_location_label" msgid="3292451598267467545">"Localização em utilização"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 9eeb01b..68f08dd 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -176,7 +176,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A tela está bloqueada na orientação paisagem."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A tela está bloqueada na orientação retrato."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Suspender"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo para avião"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Carregando, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index ef770a0..606160b 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -318,7 +318,7 @@
     <skip />
     <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
     <skip />
-    <!-- no translation found for start_dreams (6170089063982549905) -->
+    <!-- no translation found for start_dreams (7219575858348719790) -->
     <skip />
     <!-- no translation found for ethernet_label (7967563676324087464) -->
     <skip />
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 616ddb7..085f607 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ecranul este blocat în orientarea de tip peisaj."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ecranul este blocat în orientarea de tip portret."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Activaţi mod inactiv"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mod Avion"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Se încarcă, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6e10de4..2596f88 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -176,7 +176,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Выбрана только альбомная ориентация экрана."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Выбрана только книжная ориентация экрана."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Спящий режим"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Режим полета"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Зарядка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 9151512..ea70c10 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -176,7 +176,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamknutá v orientácii na šírku."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamknutá v orientácii na výšku."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Spať"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Režim V lietadle"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nabíjanie, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7be851c..fb8812a 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaklenjen v ležeči usmerjenosti."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaklenjen v pokončni usmerjenosti."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Stanje pripravljenosti"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Način za letalo"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Polnjenje, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 9d9da87..42ab38a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран је закључан у хоризонталном положају."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран је закључан у вертикалном положају."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Спавај одмах"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Сањарење"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Етернет"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Режим рада у авиону"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Пуњење, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index bae649d..7bf9d4c 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildskärmens riktning är nu låst i liggande format."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildskärmens riktning är nu låst i stående format."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Viloläge nu"</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">"Flygplansläge"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Laddar, <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 3b29b39..fba3da5 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="6170089063982549905">"Lala Sasa"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Ndoto ya mchana"</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/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 668504e..2d6f5f2 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"เข้าสู่โหมดสลีปเลย"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"อีเทอร์เน็ต"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้งานบนเครื่องบิน"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 312d8fd..52b3758 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Naka-lock ang screen sa pahigang oryentasyon."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Naka-lock ang screen sa patayong oryentasyon."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Mag-sleep Ngayon"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Airplane mode"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nagcha-charge, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 5012bc5..08111ef 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran yatay yönde kilitlendi."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran dikey yönde kilitlendi."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Şimdi Uyu"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Uçak modu"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Şarj oluyor, <xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index ba36be6..71a2bca 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран заблоковано в альбомній орієнтації."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран заблоковано в книжковій орієнтації."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Перейти в режим сну"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Заставка \"Видіння\""</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Режим польоту"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Заряджається, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index ce90afc..87ee67b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -174,7 +174,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Màn hình hiện bị khóa theo hướng ngang."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Màn hình hiện bị khóa theo hướng dọc."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Ngủ bây giờ"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Chế độ trên máy bay"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Đang sạc, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ba0d9c8..45be2e9 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -176,7 +176,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"屏幕锁定为横向模式。"</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"屏幕锁定为纵向模式。"</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"果冻豆大乱舞"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"立即休眠"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"以太网"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"飞行模式"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"正在充电:<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index a77dd8c..d64ca20 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -176,7 +176,8 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"螢幕已鎖定為橫向模式。"</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"螢幕已鎖定為垂直模式。"</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"立即休眠"</string>
+    <!-- no translation found for start_dreams (7219575858348719790) -->
+    <skip />
     <string name="ethernet_label" msgid="7967563676324087464">"乙太網路"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"飛航模式"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"充電中 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8ad1881..3b772be 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Isikrini sikhiyelwe ngomumo we-landscape."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Isikrini sikhiyelwe ngomumo we-portrait."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"I-BeanFlinger"</string>
-    <string name="start_dreams" msgid="6170089063982549905">"Lala manje"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Ukuphupha emini"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"I-Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Isimo sendiza"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Iyashaja <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBagDream.java b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
index a367367..39e4727 100644
--- a/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
+++ b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui;
 
 import android.service.dreams.DreamService;
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 0a7dd7c..9da883a 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -401,6 +401,7 @@
             Throwable exception = null;
             try {
                 mWallpaperManager.forgetLoadedWallpaper(); // force reload
+                mBackground = null;
                 mBackground = mWallpaperManager.getBitmap();
             } catch (RuntimeException e) {
                 exception = e;
diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
index 1f00bc1..0dd6d92 100644
--- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java
+++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
@@ -43,7 +43,7 @@
                     | Intent.FLAG_ACTIVITY_NEW_TASK);
             Intent resultIntent = new Intent();
             resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
-                    Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_daydreams));
+                    Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher_dreams));
             resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
             resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.start_dreams));
             setResult(RESULT_OK, resultIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java b/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
index 5dd45a4..735ee25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui.statusbar;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index eef5446..983328d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -55,6 +55,9 @@
 
     final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
 
+    // slippery nav bar when everything is disabled, e.g. during setup
+    final static boolean SLIPPERY_WHEN_DISABLED= true;
+
     final static boolean ANIMATE_HIDE_TRANSITION = false; // turned off because it introduces unsightly delay when videos goes to full screen
 
     protected IStatusBarService mBarService;
@@ -237,7 +240,9 @@
         final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0);
         final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
 
-        setSlippery(disableHome && disableRecent && disableBack);
+        if (SLIPPERY_WHEN_DISABLED) {
+            setSlippery(disableHome && disableRecent && disableBack && disableSearch);
+        }
 
         if (!mScreenOn && mCurrentView != null) {
             ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index c9a137c..4b07b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui.statusbar.phone;
 
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
index 241ac3e..8a54347 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
@@ -63,4 +79,4 @@
     public void setBar(PanelBar panelBar) {
         mBar = panelBar;
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6184e30..7035006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui.statusbar.phone;
 
 import android.animation.ObjectAnimator;
@@ -320,7 +336,7 @@
                             final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
                             if (deltaY < mFlingGestureMinDistPx
                                     || vel < mFlingExpandMinVelocityPx
-                                    || mJustPeeked) {
+                                    ) {
                                 vel = 0;
                             }
 
@@ -328,10 +344,8 @@
                                 vel = -vel;
                             }
 
-                            if (DEBUG) LOG("gesture: dy=%f vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
+                            if (DEBUG) LOG("gesture: dy=%f vel=(%f,%f) vlinear=%f",
                                     deltaY,
-                                    mVelocityTracker.getXVelocity(),
-                                    mVelocityTracker.getYVelocity(),
                                     xVel, yVel,
                                     vel);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
index 7a2f25a..225ebc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CurrentUserTracker.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui.statusbar.policy;
 
 import android.app.ActivityManager;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
new file mode 100644
index 0000000..db7e231
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+import com.android.internal.policy.impl.keyguard.KeyguardActivityLauncher.CameraWidgetInfo;
+
+public class CameraWidgetFrame extends KeyguardWidgetFrame {
+    private static final String TAG = CameraWidgetFrame.class.getSimpleName();
+    private static final boolean DEBUG = KeyguardHostView.DEBUG;
+    private static final int WIDGET_ANIMATION_DURATION = 250;
+
+    interface Callbacks {
+        void onLaunchingCamera();
+        void onCameraLaunched();
+    }
+
+    private final Handler mHandler = new Handler();
+    private final KeyguardActivityLauncher mActivityLauncher;
+    private final Callbacks mCallbacks;
+
+    private View mWidgetView;
+    private long mLaunchCameraStart;
+    private boolean mRendered;
+
+    private final Runnable mLaunchCameraRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mLaunchCameraStart = SystemClock.uptimeMillis();
+            mActivityLauncher.launchCamera();
+        }};
+
+    private final Runnable mRenderRunnable = new Runnable() {
+        @Override
+        public void run() {
+            render();
+        }};
+
+    private CameraWidgetFrame(Context context, Callbacks callbacks,
+            KeyguardActivityLauncher activityLauncher) {
+        super(context);
+
+        mCallbacks = callbacks;
+        mActivityLauncher = activityLauncher;
+    }
+
+    public static CameraWidgetFrame create(Context context, Callbacks callbacks,
+            KeyguardActivityLauncher launcher) {
+        if (context == null || callbacks == null || launcher == null)
+            return null;
+
+        CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo();
+        if (widgetInfo == null)
+            return null;
+        View widgetView = widgetInfo.layoutId > 0 ?
+                inflateWidgetView(context, widgetInfo) :
+                inflateGenericWidgetView(context);
+        if (widgetView == null)
+            return null;
+
+        ImageView preview = new ImageView(context);
+        preview.setLayoutParams(new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT));
+        preview.setScaleType(ScaleType.FIT_CENTER);
+        CameraWidgetFrame cameraWidgetFrame = new CameraWidgetFrame(context, callbacks, launcher);
+        cameraWidgetFrame.addView(preview);
+        cameraWidgetFrame.mWidgetView = widgetView;
+        return cameraWidgetFrame;
+    }
+
+    private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
+        View widgetView = null;
+        Exception exception = null;
+        try {
+            Context cameraContext = context.createPackageContext(
+                    widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
+            LayoutInflater cameraInflater = (LayoutInflater)
+                    cameraContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            cameraInflater = cameraInflater.cloneInContext(cameraContext);
+            widgetView = cameraInflater.inflate(widgetInfo.layoutId, null, false);
+        } catch (NameNotFoundException e) {
+            exception = e;
+        } catch (RuntimeException e) {
+            exception = e;
+        }
+        if (exception != null) {
+            Log.w(TAG, "Error creating camera widget view", exception);
+        }
+        return widgetView;
+    }
+
+    private static View inflateGenericWidgetView(Context context) {
+        ImageView iv = new ImageView(context);
+        iv.setImageResource(com.android.internal.R.drawable.ic_lockscreen_camera);
+        iv.setScaleType(ScaleType.CENTER);
+        iv.setBackgroundColor(Color.argb(127, 0, 0, 0));
+        return iv;
+    }
+
+    public void render() {
+        if (mRendered) return;
+
+        try {
+            int width = getRootView().getWidth();
+            int height = getRootView().getHeight();
+            if (DEBUG) Log.d(TAG, String.format("render [%sx%s] %s",
+                    width, height, Integer.toHexString(hashCode())));
+            if (width == 0 || height == 0) {
+                return;
+            }
+            Bitmap offscreen = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(offscreen);
+            mWidgetView.measure(
+                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+            mWidgetView.layout(0, 0, width, height);
+            mWidgetView.draw(c);
+            ((ImageView)getChildAt(0)).setImageBitmap(offscreen);
+            mRendered = true;
+        } catch (Throwable t) {
+            Log.w(TAG, "Error rendering camera widget", t);
+            removeAllViews();
+            View genericView = inflateGenericWidgetView(mContext);
+            addView(genericView);
+        }
+    }
+
+    private void transitionToCamera() {
+        int startWidth = getChildAt(0).getWidth();
+        int startHeight = getChildAt(0).getHeight();
+
+        int finishWidth = getRootView().getWidth();
+        int finishHeight = getRootView().getHeight();
+
+        float scaleX = (float) finishWidth / startWidth;
+        float scaleY = (float) finishHeight / startHeight;
+
+        float scale = Math.max(scaleX, scaleY);
+        animate()
+            .scaleX(scale)
+            .scaleY(scale)
+            .setDuration(WIDGET_ANIMATION_DURATION)
+            .withEndAction(mLaunchCameraRunnable)
+            .start();
+        mCallbacks.onLaunchingCamera();
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        super.onWindowFocusChanged(hasWindowFocus);
+
+        if (!hasWindowFocus) {
+            if (mLaunchCameraStart > 0) {
+                long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
+                if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
+                mLaunchCameraStart = 0;
+                onCameraLaunched();
+            }
+        }
+    }
+
+    @Override
+    public void onActive(boolean isActive) {
+        if (isActive) {
+            mHandler.post(new Runnable(){
+                @Override
+                public void run() {
+                    transitionToCamera();
+                }});
+        } else {
+            reset();
+        }
+    }
+
+    private void onCameraLaunched() {
+        reset();
+        mCallbacks.onCameraLaunched();
+    }
+
+    private void reset() {
+        animate().cancel();
+        setScaleX(1);
+        setScaleY(1);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mHandler.post(mRenderRunnable);
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
new file mode 100644
index 0000000..605a738
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+/**
+ * Interface implemented by ViewGroup-derived layouts that implement
+ * special logic for presenting security challenges to the user.
+ */
+public interface ChallengeLayout {
+    /**
+     * @return true if the security challenge area of this layout is currently visible
+     */
+    boolean isChallengeShowing();
+
+    /**
+     * @return true if the challenge area significantly overlaps other content
+     */
+    boolean isChallengeOverlapping();
+
+    /**
+     * Show or hide the challenge layout.
+     *
+     * If you want to show the challenge layout in bouncer mode where applicable,
+     * use {@link #showBouncer()} instead.
+     *
+     * @param b true to show, false to hide
+     */
+    void showChallenge(boolean b);
+
+    /**
+     * Show the bouncer challenge. This may block access to other child views.
+     */
+    void showBouncer();
+
+    /**
+     * Hide the bouncer challenge if it is currently showing.
+     * This may restore previously blocked access to other child views.
+     */
+    void hideBouncer();
+
+    /**
+     * Returns true if the challenge is currently in bouncer mode,
+     * potentially blocking access to other child views.
+     */
+    boolean isBouncing();
+
+    /**
+     * Set a listener that will respond to changes in bouncer state.
+     *
+     * @param listener listener to register
+     */
+    void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener);
+
+    /**
+     * Listener interface that reports changes in bouncer state.
+     * The bouncer is
+     */
+    public interface OnBouncerStateChangedListener {
+        /**
+         * Called when the bouncer state changes.
+         * The bouncer is activated when the user must pass a security challenge
+         * to proceed with the requested action.
+         *
+         * <p>This differs from simply showing or hiding the security challenge
+         * as the bouncer will prevent interaction with other elements of the UI.
+         * If the user attempts to escape from the bouncer, it will be dismissed,
+         * this method will be called with false as the parameter, and the action
+         * should be canceled. If the security component reports a successful
+         * authentication and the containing code calls hideBouncer() as a result,
+         * this method will also be called with a false parameter. It is up to the
+         * caller of hideBouncer to be ready for this.</p>
+         *
+         * @param bouncerActive true if the bouncer is now active,
+         *                      false if the bouncer was dismissed.
+         */
+        public void onBouncerStateChanged(boolean bouncerActive);
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java b/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java
new file mode 100644
index 0000000..020fdba
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+public class CheckLongPressHelper {
+    private View mView;
+    private boolean mHasPerformedLongPress;
+    private CheckForLongPress mPendingCheckForLongPress;
+    private float mDownX, mDownY;
+    private int mLongPressTimeout;
+    private int mScaledTouchSlop;
+
+    class CheckForLongPress implements Runnable {
+        public void run() {
+            if ((mView.getParent() != null) && mView.hasWindowFocus()
+                    && !mHasPerformedLongPress) {
+                if (mView.performLongClick()) {
+                    mView.setPressed(false);
+                    mHasPerformedLongPress = true;
+                }
+            }
+        }
+    }
+
+    public CheckLongPressHelper(View v) {
+        mScaledTouchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
+        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
+        mView = v;
+    }
+
+    public void postCheckForLongPress(MotionEvent ev) {
+        mDownX = ev.getX();
+        mDownY = ev.getY();
+        mHasPerformedLongPress = false;
+
+        if (mPendingCheckForLongPress == null) {
+            mPendingCheckForLongPress = new CheckForLongPress();
+        }
+        mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout);
+    }
+
+    public void onMove(MotionEvent ev) {
+        float x = ev.getX();
+        float y = ev.getY();
+
+        if (Math.sqrt(Math.pow(mDownX - x, 2) + Math.pow(mDownY - y, 2)) > mScaledTouchSlop) {
+            cancelLongPress();
+        }
+    }
+
+    public void cancelLongPress() {
+        mHasPerformedLongPress = false;
+        if (mPendingCheckForLongPress != null) {
+            mView.removeCallbacks(mPendingCheckForLongPress);
+            mPendingCheckForLongPress = null;
+        }
+    }
+
+    public boolean hasPerformedLongPress() {
+        return mHasPerformedLongPress;
+    }
+}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
index 203ba3c..cab4ed9 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
@@ -23,7 +23,6 @@
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.widget.Button;
 
 import com.android.internal.telephony.IccCardConstants.State;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
index 3fe16cf..000acb1 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
@@ -147,7 +147,7 @@
     public boolean stop() {
         if (DEBUG) Log.d(TAG, "stop()");
         if (mHandler.getLooper() != Looper.myLooper()) {
-            Log.e(TAG, "stop() called off of the UI thread");
+            Log.e(TAG, "stop() called from non-UI thread");
         }
 
         boolean mWasRunning = mIsRunning;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
new file mode 100644
index 0000000..2eb10fe
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * Base class for PIN and password unlock screens.
+ */
+public abstract class KeyguardAbsKeyInputView extends LinearLayout
+        implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+    protected KeyguardSecurityCallback mCallback;
+    protected TextView mPasswordEntry;
+    protected LockPatternUtils mLockPatternUtils;
+    protected SecurityMessageDisplay mSecurityMessageDisplay;
+    protected boolean mEnableHaptics;
+
+    // To avoid accidental lockout due to events while the device in in the pocket, ignore
+    // any passwords with length less than or equal to this length.
+    protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+
+    public KeyguardAbsKeyInputView(Context context) {
+        this(context, null);
+    }
+
+    public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+        mCallback = callback;
+    }
+
+    public void setLockPatternUtils(LockPatternUtils utils) {
+        mLockPatternUtils = utils;
+        mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        if (hasWindowFocus) {
+            reset();
+        }
+    }
+
+    public void reset() {
+        // start fresh
+        mPasswordEntry.setText("");
+        mPasswordEntry.requestFocus();
+
+        // if the user is currently locked out, enforce it.
+        long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+        if (deadline != 0) {
+            handleAttemptLockout(deadline);
+        } else {
+            resetState();
+        }
+    }
+
+    protected abstract void resetState();
+
+    @Override
+    protected void onFinishInflate() {
+        // We always set a dummy NavigationManager to avoid null checks
+        mSecurityMessageDisplay = new KeyguardNavigationManager(null);
+
+        mLockPatternUtils = new LockPatternUtils(mContext);
+
+        mPasswordEntry = (TextView) findViewById(R.id.passwordEntry);
+        mPasswordEntry.setOnEditorActionListener(this);
+        mPasswordEntry.addTextChangedListener(this);
+
+        // Poke the wakelock any time the text is selected or modified
+        mPasswordEntry.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                mCallback.userActivity(0); // TODO: customize timeout for text?
+            }
+        });
+
+        mPasswordEntry.addTextChangedListener(new TextWatcher() {
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+            }
+
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+            }
+
+            public void afterTextChanged(Editable s) {
+                if (mCallback != null) {
+                    mCallback.userActivity(0);
+                }
+            }
+        });
+    }
+
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        // send focus to the password field
+        return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
+    }
+
+    protected void verifyPasswordAndUnlock() {
+        String entry = mPasswordEntry.getText().toString();
+        if (mLockPatternUtils.checkPassword(entry)) {
+            mCallback.reportSuccessfulUnlockAttempt();
+            mCallback.dismiss(true);
+        } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
+            // to avoid accidental lockout, only count attempts that are long enough to be a
+            // real password. This may require some tweaking.
+            mCallback.reportFailedUnlockAttempt();
+            if (0 == (mCallback.getFailedAttempts()
+                    % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+                long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+                handleAttemptLockout(deadline);
+            }
+            mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pin, true);
+        }
+        mPasswordEntry.setText("");
+    }
+
+    // Prevent user from using the PIN/Password entry until scheduled deadline.
+    protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
+        mPasswordEntry.setEnabled(false);
+        long elapsedRealtime = SystemClock.elapsedRealtime();
+        new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
+
+            @Override
+            public void onTick(long millisUntilFinished) {
+                int secondsRemaining = (int) (millisUntilFinished / 1000);
+                mSecurityMessageDisplay.setMessage(
+                        R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
+            }
+
+            @Override
+            public void onFinish() {
+                resetState();
+            }
+        }.start();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        mCallback.userActivity(0);
+        return false;
+    }
+
+    @Override
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        // Check if this was the result of hitting the enter key
+        if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
+                || actionId == EditorInfo.IME_ACTION_NEXT) {
+            verifyPasswordAndUnlock();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean needsInput() {
+        return false;
+    }
+
+    @Override
+    public void onPause() {
+
+    }
+
+    @Override
+    public void onResume() {
+        reset();
+    }
+
+    @Override
+    public KeyguardSecurityCallback getCallback() {
+        return mCallback;
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        if (mCallback != null) {
+            mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS);
+        }
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+    }
+
+    @Override
+    public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
+        mSecurityMessageDisplay = display;
+        reset();
+    }
+
+    // Cause a VIRTUAL_KEY vibration
+    public void doHapticKeyClick() {
+        if (mEnableHaptics) {
+            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+                    | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+        }
+    }
+}
+
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
index ebca4ac..ea7a8e7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
@@ -321,5 +321,9 @@
         mSecurityMessageDisplay = display;
         reset();
     }
+
+    @Override
+    public void showUsabilityHint() {
+    }
 }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
new file mode 100644
index 0000000..a224a42
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.ActivityManagerNative;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.util.List;
+
+public abstract class KeyguardActivityLauncher {
+    private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
+    private static final boolean DEBUG = KeyguardHostView.DEBUG;
+    private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
+    private static final Intent SECURE_CAMERA_INTENT =
+            new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+                    .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+    private static final Intent INSECURE_CAMERA_INTENT =
+            new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+
+    abstract Context getContext();
+
+    abstract KeyguardSecurityCallback getCallback();
+
+    abstract LockPatternUtils getLockPatternUtils();
+
+    public static class CameraWidgetInfo {
+        public String contextPackage;
+        public int layoutId;
+    }
+
+    public CameraWidgetInfo getCameraWidgetInfo() {
+        CameraWidgetInfo info = new CameraWidgetInfo();
+        Intent intent = getCameraIntent();
+        PackageManager packageManager = getContext().getPackageManager();
+        final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+                intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+        if (appList.size() == 0) {
+            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
+            return null;
+        }
+        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+                getLockPatternUtils().getCurrentUser());
+        if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
+        if (wouldLaunchResolverActivity(resolved, appList)) {
+            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
+            return info;
+        }
+        if (resolved == null || resolved.activityInfo == null) {
+            return null;
+        }
+        if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
+            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
+            return info;
+        }
+        int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
+        if (layoutId == 0) {
+            if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
+            return info;
+        }
+        info.contextPackage = resolved.activityInfo.packageName;
+        info.layoutId = layoutId;
+        return info;
+    }
+
+    public void launchCamera() {
+        LockPatternUtils lockPatternUtils = getLockPatternUtils();
+        if (lockPatternUtils.isSecure()) {
+            // Launch the secure version of the camera
+            if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
+                // TODO: Show disambiguation dialog instead.
+                // For now, we'll treat this like launching any other app from secure keyguard.
+                // When they do, user sees the system's ResolverActivity which lets them choose
+                // which secure camera to use.
+                launchActivity(SECURE_CAMERA_INTENT, false);
+            } else {
+                launchActivity(SECURE_CAMERA_INTENT, true);
+            }
+        } else {
+            // Launch the normal camera
+            launchActivity(INSECURE_CAMERA_INTENT, false);
+        }
+    }
+
+    /**
+     * Launches the said intent for the current foreground user.
+     * @param intent
+     * @param showsWhileLocked true if the activity can be run on top of keyguard.
+     * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
+     */
+    public void launchActivity(final Intent intent, boolean showsWhileLocked) {
+        final Context context = getContext();
+        LockPatternUtils lockPatternUtils = getLockPatternUtils();
+        intent.addFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        boolean isSecure = lockPatternUtils.isSecure();
+        if (!isSecure || showsWhileLocked) {
+            if (!isSecure) try {
+                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+            } catch (RemoteException e) {
+                Log.w(TAG, "can't dismiss keyguard on launch");
+            }
+            try {
+                context.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+            } catch (ActivityNotFoundException e) {
+                Log.w(TAG, "Activity not found for intent + " + intent.getAction());
+            }
+        } else {
+            // Create a runnable to start the activity and ask the user to enter their
+            // credentials.
+            KeyguardSecurityCallback callback = getCallback();
+            callback.setOnDismissRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    context.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+                }
+            });
+            callback.dismiss(false);
+        }
+    }
+
+    private Intent getCameraIntent() {
+        return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+    }
+
+    private boolean wouldLaunchResolverActivity(Intent intent) {
+        PackageManager packageManager = getContext().getPackageManager();
+        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+                PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+        List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+                intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+        return wouldLaunchResolverActivity(resolved, appList);
+    }
+
+    private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
+        // If the list contains the above resolved activity, then it can't be
+        // ResolverActivity itself.
+        for (int i = 0; i < appList.size(); i++) {
+            ResolveInfo tmp = appList.get(i);
+            if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
+                    && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
new file mode 100644
index 0000000..29124c4
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import android.util.Log;
+
+class KeyguardCircleFramedDrawable extends Drawable {
+
+    private final Bitmap mBitmap;
+    private final int mSize;
+    private final Paint mPaint;
+    private final float mShadowRadius;
+    private final float mStrokeWidth;
+    private final int mFrameColor;
+    private final int mHighlightColor;
+    private final int mFrameShadowColor;
+
+    private float mScale;
+    private Path mFramePath;
+    private Rect mSrcRect;
+    private RectF mDstRect;
+    private RectF mFrameRect;
+    private boolean mPressed;
+
+    public KeyguardCircleFramedDrawable(Bitmap bitmap, int size,
+            int frameColor, float strokeWidth,
+            int frameShadowColor, float shadowRadius,
+            int highlightColor) {
+        super();
+        mSize = size;
+        mShadowRadius = shadowRadius;
+        mFrameColor = frameColor;
+        mFrameShadowColor = frameShadowColor;
+        mStrokeWidth = strokeWidth;
+        mHighlightColor = highlightColor;
+
+        mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(mBitmap);
+
+        final int width = bitmap.getWidth();
+        final int height = bitmap.getHeight();
+        final int square = Math.min(width, height);
+
+        final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
+        final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
+        circleRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
+        circleRect.inset(mShadowRadius, mShadowRadius);
+
+        final Path fillPath = new Path();
+        fillPath.addArc(circleRect, 0f, 360f);
+
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        // opaque circle matte
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setColor(Color.BLACK);
+        mPaint.setStyle(Paint.Style.FILL);
+        canvas.drawPath(fillPath, mPaint);
+
+        // mask in the icon where the bitmap is opaque
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
+        canvas.drawBitmap(bitmap, cropRect, circleRect, mPaint);
+
+        // prepare paint for frame drawing
+        mPaint.setXfermode(null);
+
+        mScale = 1f;
+
+        mSrcRect = new Rect(0, 0, mSize, mSize);
+        mDstRect = new RectF(0, 0, mSize, mSize);
+        mFrameRect = new RectF(mDstRect);
+        mFramePath = new Path();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        // clear background
+        final float outside = Math.min(canvas.getWidth(), canvas.getHeight());
+        final float inside = mScale * outside;
+        final float pad = (outside - inside) / 2f;
+
+        mDstRect.set(pad, pad, outside - pad, outside - pad);
+        canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+
+        mFrameRect.set(mDstRect);
+        mFrameRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
+        mFrameRect.inset(mShadowRadius, mShadowRadius);
+
+        mFramePath.reset();
+        mFramePath.addArc(mFrameRect, 0f, 360f);
+
+        // white frame
+        if (mPressed) {
+            mPaint.setStyle(Paint.Style.FILL);
+            mPaint.setColor(Color.argb((int) (0.33f * 255),
+                            Color.red(mHighlightColor),
+                            Color.green(mHighlightColor),
+                            Color.blue(mHighlightColor)));
+            canvas.drawPath(mFramePath, mPaint);
+        }
+        mPaint.setStrokeWidth(mStrokeWidth);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setColor(mPressed ? mHighlightColor : mFrameColor);
+        mPaint.setShadowLayer(mShadowRadius, 0f, 0f, mFrameShadowColor);
+        canvas.drawPath(mFramePath, mPaint);
+    }
+
+    public void setScale(float scale) {
+        Log.i("KFD", "scale: " + scale);
+        mScale = scale;
+    }
+
+    public float getScale() {
+        return mScale;
+    }
+
+    public void setPressed(boolean pressed) {
+        mPressed = pressed;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
index 04ab871..4aa6b05 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -39,6 +39,9 @@
     private View mFaceUnlockAreaView;
     private ImageButton mCancelButton;
 
+    private boolean mIsShowing = false;
+    private final Object mIsShowingLock = new Object();
+
     public KeyguardFaceUnlockView(Context context) {
         this(context, null);
     }
@@ -112,6 +115,7 @@
     }
 
     private void initializeBiometricUnlockView() {
+        if (DEBUG) Log.d(TAG, "initializeBiometricUnlockView()");
         mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view);
         if (mFaceUnlockAreaView != null) {
             mBiometricUnlock = new FaceUnlock(mContext);
@@ -129,9 +133,9 @@
     }
 
     /**
-     * Starts the biometric unlock if it should be started based on a number of factors including
-     * the mSuppressBiometricUnlock flag.  If it should not be started, it hides the biometric
-     * unlock area.
+     * Starts the biometric unlock if it should be started based on a number of factors.  If it
+     * should not be started, it either goes to the back up, or remains showing to prepare for
+     * it being started later.
      */
     private void maybeStartBiometricUnlock() {
         if (DEBUG) Log.d(TAG, "maybeStartBiometricUnlock()");
@@ -142,12 +146,25 @@
                     LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
             PowerManager powerManager = (PowerManager) mContext.getSystemService(
                     Context.POWER_SERVICE);
+
+            boolean isShowing;
+            synchronized(mIsShowingLock) {
+                isShowing = mIsShowing;
+            }
+
+            // Don't start it if the screen is off or if it's not showing, but keep this view up
+            // because we want it here and ready for when the screen turns on or when it does start
+            // showing.
+            if (!powerManager.isScreenOn() || !isShowing) {
+                mBiometricUnlock.stop(); // It shouldn't be running but calling this can't hurt.
+                return;
+            }
+
             // TODO: Some of these conditions are handled in KeyguardSecurityModel and may not be
             // necessary here.
             if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
                     && !monitor.getMaxBiometricUnlockAttemptsReached()
-                    && !backupIsTimedOut
-                    && powerManager.isScreenOn()) {
+                    && !backupIsTimedOut) {
                 mBiometricUnlock.start();
             } else {
                 mBiometricUnlock.stopAndShowBackup();
@@ -161,7 +178,9 @@
         public void onPhoneStateChanged(int phoneState) {
             if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")");
             if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
-                mBiometricUnlock.stopAndShowBackup();
+                if (mBiometricUnlock != null) {
+                    mBiometricUnlock.stopAndShowBackup();
+                }
             }
         }
 
@@ -174,10 +193,33 @@
             // No longer required; static value set by KeyguardViewMediator
             // mLockPatternUtils.setCurrentUser(userId);
         }
+
+        @Override
+        public void onKeyguardVisibilityChanged(boolean showing) {
+            if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
+            boolean wasShowing = false;
+            synchronized(mIsShowingLock) {
+                wasShowing = mIsShowing;
+                mIsShowing = showing;
+            }
+            PowerManager powerManager = (PowerManager) mContext.getSystemService(
+                    Context.POWER_SERVICE);
+            if (mBiometricUnlock != null) {
+                if (!showing && wasShowing) {
+                    mBiometricUnlock.stop();
+                } else if (showing && powerManager.isScreenOn() && !wasShowing) {
+                    maybeStartBiometricUnlock();
+                }
+            }
+        }
     };
 
     @Override
     public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
-        mSecurityMessageDisplay = display;    
+        mSecurityMessageDisplay = display;
+    }
+
+    @Override
+    public void showUsabilityHint() {
     }
 }
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 b86e5b8..b846729 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -35,7 +35,9 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
@@ -47,7 +49,6 @@
 import android.view.animation.AnimationUtils;
 import android.widget.RemoteViews.OnClickHandler;
 import android.widget.TextView;
-import android.widget.ViewFlipper;
 
 import com.android.internal.R;
 import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -60,7 +61,7 @@
     private static final String TAG = "KeyguardViewHost";
 
     // Use this to debug all of keyguard
-    public static boolean DEBUG;
+    public static boolean DEBUG = KeyguardViewMediator.DEBUG;
 
     // also referenced in SecuritySettings.java
     static final int APPWIDGET_HOST_ID = 0x4B455947;
@@ -71,9 +72,8 @@
     private static final int TRANSPORT_VISIBLE = 2;
 
     private AppWidgetHost mAppWidgetHost;
-    private KeyguardWidgetRegion mAppWidgetRegion;
     private KeyguardWidgetPager mAppWidgetContainer;
-    private ViewFlipper mSecurityViewContainer;
+    private KeyguardSecurityViewFlipper mSecurityViewContainer;
     private KeyguardSelectorView mKeyguardSelectorView;
     private KeyguardTransportControlView mTransportControl;
     private boolean mEnableMenuKey;
@@ -87,6 +87,7 @@
     private LockPatternUtils mLockPatternUtils;
 
     private KeyguardSecurityModel mSecurityModel;
+    private KeyguardViewStateManager mViewStateManager;
 
     private Rect mTempRect = new Rect();
     private int mTransportState = TRANSPORT_GONE;
@@ -101,6 +102,7 @@
         void hideSecurityView(int duration);
         void showSecurityView();
         void showUnlockHint();
+        void userActivity();
     }
 
     public KeyguardHostView(Context context) {
@@ -151,21 +153,41 @@
 
     @Override
     protected void onFinishInflate() {
-        mAppWidgetRegion = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region);
-        mAppWidgetRegion.setVisibility(VISIBLE);
-        mAppWidgetRegion.setCallbacks(mWidgetCallbacks);
-
+        // Grab instances of and make any necessary changes to the main layouts. Create
+        // view state manager and wire up necessary listeners / callbacks.
         mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
-        mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
-        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
+        mAppWidgetContainer.setVisibility(VISIBLE);
+        mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
+        mAppWidgetContainer.setMinScale(0.5f);
 
         addDefaultWidgets();
-        updateSecurityViews();
-        setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+        addWidgetsFromSettings();
 
-        if (KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
-            showPrimarySecurityScreen(false);
+        mViewStateManager = new KeyguardViewStateManager();
+        SlidingChallengeLayout slider =
+                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+        if (slider != null) {
+            slider.setOnChallengeScrolledListener(mViewStateManager);
         }
+        mAppWidgetContainer.setViewStateManager(mViewStateManager);
+        mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
+
+        mViewStateManager.setPagedView(mAppWidgetContainer);
+        mViewStateManager.setChallengeLayout(slider != null ? slider :
+                (ChallengeLayout) findViewById(R.id.multi_pane_challenge));
+        mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
+        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
+        mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
+
+        mViewStateManager.showUsabilityHints();
+
+        if (!(mContext instanceof Activity)) {
+            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+        }
+
+        showPrimarySecurityScreen(false);
+
+        updateSecurityViews();
     }
 
     private void updateSecurityViews() {
@@ -195,22 +217,9 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mAppWidgetHost.startListening();
-        // TODO: Re-enable when we have layouts that can support a better variety of widgets.
-        // maybePopulateWidgets();
-        disableStatusViewInteraction();
         post(mSwitchPageRunnable);
     }
 
-    private void disableStatusViewInteraction() {
-        // Disable all user interaction on status view. This is done to prevent falsing in the
-        // pocket from triggering useractivity and prevents 3rd party replacement widgets
-        // from responding to user interaction while in this position.
-        View statusView = findViewById(R.id.keyguard_status_view);
-        if (statusView instanceof KeyguardWidgetFrame) {
-            ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true);
-        }
-    }
-
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
@@ -221,12 +230,12 @@
         return mAppWidgetHost;
     }
 
-    void addWidget(AppWidgetHostView view) {
-        mAppWidgetContainer.addWidget(view);
+    void addWidget(AppWidgetHostView view, int pageIndex) {
+        mAppWidgetContainer.addWidget(view, pageIndex);
     }
 
-    private KeyguardWidgetRegion.Callbacks mWidgetCallbacks
-            = new KeyguardWidgetRegion.Callbacks() {
+    private KeyguardWidgetPager.Callbacks mWidgetCallbacks
+            = new KeyguardWidgetPager.Callbacks() {
         @Override
         public void userActivity() {
             if (mViewMediatorCallback != null) {
@@ -240,14 +249,22 @@
                 mViewMediatorCallback.onUserActivityTimeoutChanged();
             }
         }
+
+        @Override
+        public void onPageSwitch(int newPageIndex) {
+            if (!isCameraOrAdd(newPageIndex)) {
+                if (DEBUG) Log.d(TAG, "Setting sticky widget index: " + newPageIndex);
+                mLockPatternUtils.setStickyWidgetIndex(newPageIndex);
+            }
+        }
     };
 
     @Override
     public long getUserActivityTimeout() {
         // Currently only considering user activity timeouts needed by widgets.
         // Could also take into account longer timeouts for certain security views.
-        if (mAppWidgetRegion != null) {
-            return mAppWidgetRegion.getUserActivityTimeout();
+        if (mAppWidgetContainer != null) {
+            return mAppWidgetContainer.getUserActivityTimeout();
         }
         return -1;
     }
@@ -317,13 +334,11 @@
             case Pattern:
                 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
                 break;
-
-            case Password: {
-                    final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
-                        DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-                    messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message
-                            : R.string.kg_too_many_failed_password_attempts_dialog_message;
-                }
+            case PIN:
+                messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
+                break;
+            case Password:
+                messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
                 break;
         }
 
@@ -418,7 +433,8 @@
     void showPrimarySecurityScreen(boolean turningOff) {
         SecurityMode securityMode = mSecurityModel.getSecurityMode();
         if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
-        if (!turningOff && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
+        if (!turningOff && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()
+                && !KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
             // If we're not turning off, then allow biometric alternate.
             // We'll reload it when the device comes back on.
             securityMode = mSecurityModel.getAlternateFor(securityMode);
@@ -465,6 +481,7 @@
             switch (mCurrentSecuritySelection) {
                 case Pattern:
                 case Password:
+                case PIN:
                 case Account:
                 case Biometric:
                     finish = true;
@@ -504,6 +521,8 @@
             if (mViewMediatorCallback != null) {
                 mViewMediatorCallback.keyguardDone(true);
             }
+        } else {
+            mViewStateManager.showBouncer(true);
         }
     }
 
@@ -607,7 +626,6 @@
                 break;
             }
         }
-        boolean simPukFullScreen = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen);
         int layoutId = getLayoutIdFor(securityMode);
         if (view == null && layoutId != 0) {
             final LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -624,14 +642,9 @@
             if (navigationText != null) {
                 view.setSecurityMessageDisplay(new KeyguardNavigationManager(navigationText));
             } else {
-                view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
-            }
-        }
-
-        if (securityMode == SecurityMode.SimPin || securityMode == SecurityMode.SimPuk ||
-            securityMode == SecurityMode.Account) {
-            if (simPukFullScreen) {
-                mAppWidgetRegion.setVisibility(View.GONE);
+                if (mKeyguardStatusViewManager != null) {
+                    view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
+                }
             }
         }
 
@@ -658,6 +671,15 @@
         KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
         KeyguardSecurityView newView = getSecurityView(securityMode);
 
+        // Enter full screen mode if we're in SIM or Account screen
+        boolean fullScreenEnabled = getResources().getBoolean(
+                com.android.internal.R.bool.kg_sim_puk_account_full_screen);
+        boolean isSimOrAccount = securityMode == SecurityMode.SimPin
+                || securityMode == SecurityMode.SimPuk
+                || securityMode == SecurityMode.Account;
+        mAppWidgetContainer.setVisibility(
+                isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
+
         // Emulate Activity life cycle
         if (oldView != null) {
             oldView.onPause();
@@ -698,7 +720,7 @@
 
     @Override
     public void onScreenTurnedOn() {
-        if (DEBUG) Log.d(TAG, "screen on");
+        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
         showPrimarySecurityScreen(false);
         getSecurityView(mCurrentSecuritySelection).onResume();
 
@@ -706,18 +728,23 @@
         // layout is blank but forcing a layout causes it to reappear (e.g. with with
         // hierarchyviewer).
         requestLayout();
+
+        if (mViewStateManager != null) {
+            mViewStateManager.showUsabilityHints();
+        }
     }
 
     @Override
     public void onScreenTurnedOff() {
-        if (DEBUG) Log.d(TAG, "screen off");
+        if (DEBUG) Log.d(TAG, "screen off, instance " + Integer.toHexString(hashCode()));
         showPrimarySecurityScreen(true);
         getSecurityView(mCurrentSecuritySelection).onPause();
     }
 
     @Override
     public void show() {
-        onScreenTurnedOn();
+        if (DEBUG) Log.d(TAG, "show()");
+        showPrimarySecurityScreen(false);
     }
 
     private boolean isSecure() {
@@ -726,6 +753,7 @@
             case Pattern:
                 return mLockPatternUtils.isLockPatternEnabled();
             case Password:
+            case PIN:
                 return mLockPatternUtils.isLockPasswordEnabled();
             case SimPin:
             case SimPuk:
@@ -760,6 +788,7 @@
                 mViewMediatorCallback.keyguardDone(true);
             }
         } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
+                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
                 && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
             // can only verify unlock when in pattern/password mode
             if (mViewMediatorCallback != null) {
@@ -776,6 +805,7 @@
         switch (securityMode) {
             case None: return R.id.keyguard_selector_view;
             case Pattern: return R.id.keyguard_pattern_view;
+            case PIN: return R.id.keyguard_pin_view;
             case Password: return R.id.keyguard_password_view;
             case Biometric: return R.id.keyguard_face_unlock_view;
             case Account: return R.id.keyguard_account_view;
@@ -789,6 +819,7 @@
         switch (securityMode) {
             case None: return R.layout.keyguard_selector_view;
             case Pattern: return R.layout.keyguard_pattern_view;
+            case PIN: return R.layout.keyguard_pin_view;
             case Password: return R.layout.keyguard_password_view;
             case Biometric: return R.layout.keyguard_face_unlock_view;
             case Account: return R.layout.keyguard_account_view;
@@ -799,23 +830,99 @@
         }
     }
 
-    private void addWidget(int appId) {
+    private void addWidget(int appId, int pageIndex) {
         AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
         AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
         if (appWidgetInfo != null) {
             AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
-            addWidget(view);
+            addWidget(view, pageIndex);
         } else {
-            Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
+            Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
+            mLockPatternUtils.removeAppWidget(appId);
         }
     }
 
+    private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
+        new CameraWidgetFrame.Callbacks() {
+            @Override
+            public void onLaunchingCamera() {
+                SlidingChallengeLayout slider = locateSlider();
+                if (slider != null) {
+                    slider.showHandle(false);
+                }
+            }
+
+            @Override
+            public void onCameraLaunched() {
+                SlidingChallengeLayout slider = locateSlider();
+                if (slider != null) {
+                    slider.showHandle(true);
+                    slider.showChallenge(true);
+                }
+                View v = mAppWidgetContainer.getChildAt(mAppWidgetContainer.getCurrentPage());
+                if (v instanceof CameraWidgetFrame) {
+                    mAppWidgetContainer.scrollLeft();
+                }
+            }
+
+            private SlidingChallengeLayout locateSlider() {
+                return (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+            }
+        };
+
+    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
+        @Override
+        Context getContext() {
+            return mContext;
+        }
+
+        @Override
+        KeyguardSecurityCallback getCallback() {
+            return mCallback;
+        }
+
+        @Override
+        LockPatternUtils getLockPatternUtils() {
+            return mLockPatternUtils;
+        }};
+
     private void addDefaultWidgets() {
         LayoutInflater inflater = LayoutInflater.from(mContext);
-        inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
         inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
 
-        inflateAndAddUserSelectorWidgetIfNecessary();
+        View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
+        mAppWidgetContainer.addWidget(addWidget);
+        if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
+            View cameraWidget =
+                    CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
+            if (cameraWidget != null) {
+                mAppWidgetContainer.addWidget(cameraWidget);
+            }
+        }
+
+        View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
+        addWidgetButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mCallback.setOnDismissRunnable(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
+                        intent.addFlags(
+                                Intent.FLAG_ACTIVITY_NEW_TASK
+                                | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                                | Intent.FLAG_ACTIVITY_CLEAR_TOP
+                                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                        mContext.startActivityAsUser(intent,
+                                new UserHandle(UserHandle.USER_CURRENT));
+                    }
+                });
+                mCallback.dismiss(false);
+            }
+        });
+
+        enableUserSelectorIfNecessary();
         initializeTransportControl();
     }
 
@@ -857,12 +964,15 @@
             });
         }
 
-        mKeyguardStatusViewManager = ((KeyguardStatusView)
-                findViewById(R.id.keyguard_status_view_face_palm)).getManager();
+        KeyguardStatusView ksv = (KeyguardStatusView)
+                findViewById(R.id.keyguard_status_view_face_palm);
+        if (ksv != null) {
+            mKeyguardStatusViewManager = ksv.getManager();
+        }
 
     }
 
-    private void maybePopulateWidgets() {
+    private void addWidgetsFromSettings() {
         DevicePolicyManager dpm =
                 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
         if (dpm != null) {
@@ -874,27 +984,29 @@
             }
         }
 
-        // Replace status widget if selected by user in Settings
-        int statusWidgetId = mLockPatternUtils.getStatusWidget();
-        if (statusWidgetId != -1) {
-            addWidget(statusWidgetId);
-            View newStatusWidget = mAppWidgetContainer.getChildAt(
-                    mAppWidgetContainer.getChildCount() - 1);
-
-            int oldStatusWidgetPosition = getWidgetPosition(R.id.keyguard_status_view);
-            mAppWidgetContainer.removeViewAt(oldStatusWidgetPosition);
-
-            // Re-add new status widget at position of old one
-            mAppWidgetContainer.removeView(newStatusWidget);
-            newStatusWidget.setId(R.id.keyguard_status_view);
-            mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition);
+        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
+        int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
+        // This shouldn't happen, but just to be safe!
+        if (addPageIndex < 0) {
+            addPageIndex = 0;
         }
 
         // Add user-selected widget
-        final int[] widgets = mLockPatternUtils.getUserDefinedWidgets();
-        for (int i = 0; i < widgets.length; i++) {
-            if (widgets[i] != -1) {
-                addWidget(widgets[i]);
+        final int[] widgets = mLockPatternUtils.getAppWidgets();
+        if (widgets == null) {
+            Log.d(TAG, "Problem reading widgets");
+        } else {
+            for (int i = widgets.length -1; i >= 0; i--) {
+                if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
+                    LayoutInflater inflater = LayoutInflater.from(mContext);
+                    View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
+                    mAppWidgetContainer.addWidget(statusWidget, addPageIndex + 1);
+                } else {
+                    // We add the widgets from left to right, starting after the first page after
+                    // the add page. We count down, since the order will be persisted from right
+                    // to left, starting after camera.
+                    addWidget(widgets[i], addPageIndex + 1);
+                }
             }
         }
     }
@@ -957,47 +1069,77 @@
     }
 
     private void showAppropriateWidgetPage() {
-
-        // The following sets the priority for showing widgets. Transport should be shown if
-        // music is playing, followed by the multi-user widget if enabled, followed by the
-        // status widget.
-        final int pageToShow;
-        if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
+        boolean music = mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
+        if (music) {
             mTransportState = TRANSPORT_VISIBLE;
-            pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
-        } else {
-            UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
-            final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
-            if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
-                pageToShow = multiUserPosition;
-            } else {
-                final View statusView = findViewById(R.id.keyguard_status_view);
-                pageToShow = mAppWidgetContainer.indexOfChild(statusView);
-            }
-            if (mTransportState == TRANSPORT_VISIBLE) {
-                mTransportState = TRANSPORT_INVISIBLE;
-            }
+        } else if (mTransportState == TRANSPORT_VISIBLE) {
+            mTransportState = TRANSPORT_INVISIBLE;
         }
+        int pageToShow = getAppropriateWidgetPage();
         mAppWidgetContainer.setCurrentPage(pageToShow);
     }
 
-    private void inflateAndAddUserSelectorWidgetIfNecessary() {
+    private boolean isCameraOrAdd(int pageIndex) {
+        View v = mAppWidgetContainer.getChildAt(pageIndex);
+        return v.getId() == R.id.keyguard_add_widget || v instanceof CameraWidgetFrame;
+    }
+
+    private int getAppropriateWidgetPage() {
+        // assumes at least one widget (besides camera + add)
+
+        boolean music = mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
+        // if music playing, show transport
+        if (music) {
+            if (DEBUG) Log.d(TAG, "Music playing, show transport");
+            return mAppWidgetContainer.indexOfChild(mTransportControl);
+        }
+
+        // if multi-user applicable, show it
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
+        int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
+        if (multiUserPosition != -1 && userManager.getUsers(true).size() > 1) {
+            if (DEBUG) Log.d(TAG, "Multi-user applicable, show it");
+            return multiUserPosition;
+        }
+
+        // if we have a sticky widget, show it
+        int stickyWidgetIndex = mLockPatternUtils.getStickyWidgetIndex();
+        if (stickyWidgetIndex > -1
+                && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
+                && !isCameraOrAdd(stickyWidgetIndex)) {
+            if (DEBUG) Log.d(TAG, "Sticky widget found, show it");
+            return stickyWidgetIndex;
+        }
+
+        // if we have a status view, show it
+        View statusView = findViewById(R.id.keyguard_status_view);
+        int statusViewIndex = mAppWidgetContainer.indexOfChild(statusView);
+        if (statusViewIndex > -1) {
+            if (DEBUG) Log.d(TAG, "Status widget found, show it");
+            return mAppWidgetContainer.indexOfChild(statusView);
+        }
+
+        // else the right-most (except for camera)
+        int rightMost = mAppWidgetContainer.getChildCount() - 1;
+        if (mAppWidgetContainer.getChildAt(rightMost) instanceof CameraWidgetFrame) {
+            rightMost--;
+        }
+        if (DEBUG) Log.d(TAG, "Show right-most");
+        return rightMost;
+    }
+
+    private void enableUserSelectorIfNecessary() {
         // if there are multiple users, we need to add the multi-user switcher widget to the
         // keyguard.
         UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         List<UserInfo> users = mUm.getUsers(true);
 
         if (users.size() > 1) {
-            KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
-                LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
-                        mAppWidgetContainer, false);
-
-            // add the switcher in the first position
-            mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
-            KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
-                    userSwitcher.getChildAt(0);
-
+            KeyguardMultiUserSelectorView multiUser =
+                    (KeyguardMultiUserSelectorView) findViewById(R.id.keyguard_user_selector);
+            multiUser.setVisibility(View.VISIBLE);
+            multiUser.addUsers(mUm.getUsers(true));
             UserSwitcherCallback callback = new UserSwitcherCallback() {
                 @Override
                 public void hideSecurityView(int duration) {
@@ -1012,10 +1154,16 @@
                 @Override
                 public void showUnlockHint() {
                     if (mKeyguardSelectorView != null) {
-                        mKeyguardSelectorView.ping();
+                        mKeyguardSelectorView.showUsabilityHint();
                     }
                 }
 
+                @Override
+                public void userActivity() {
+                    if (mViewMediatorCallback != null) {
+                        mViewMediatorCallback.userActivity();
+                    }
+                }
             };
             multiUser.setCallback(callback);
         }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java
new file mode 100644
index 0000000..0fc54cd
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A layout that arranges its children into a special type of grid.
+ */
+public class KeyguardLinearLayout extends LinearLayout {
+    int mTopChild = 0;
+
+    public KeyguardLinearLayout(Context context) {
+        this(context, null, 0);
+    }
+
+    public KeyguardLinearLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyguardLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setTopChild(View child) {
+        int top = indexOfChild(child);
+        mTopChild = top;
+        invalidate();
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
index 3c972bc..a21ebe3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -18,15 +18,18 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.Color;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -34,23 +37,38 @@
 import com.android.internal.R;
 
 class KeyguardMultiUserAvatar extends FrameLayout {
+    private static final String TAG = KeyguardMultiUserAvatar.class.getSimpleName();
+    private static final boolean DEBUG = KeyguardHostView.DEBUG;
 
     private ImageView mUserImage;
     private TextView mUserName;
     private UserInfo mUserInfo;
     private static final float ACTIVE_ALPHA = 1.0f;
-    private static final float INACTIVE_ALPHA = 0.5f;
-    private static final float ACTIVE_SCALE = 1.2f;
-    private static final float ACTIVE_TEXT_BACGROUND_ALPHA = 0.5f;
-    private static final float INACTIVE_TEXT_BACGROUND_ALPHA = 0f;
-    private static int mActiveTextColor;
-    private static int mInactiveTextColor;
+    private static final float INACTIVE_ALPHA = 1.0f;
+    private static final float ACTIVE_SCALE = 1.5f;
+    private static final float ACTIVE_TEXT_ALPHA = 0f;
+    private static final float INACTIVE_TEXT_ALPHA = 0.5f;
+    private static final int SWITCH_ANIMATION_DURATION = 150;
+
+    private final float mActiveAlpha;
+    private final float mActiveScale;
+    private final float mActiveTextAlpha;
+    private final float mInactiveAlpha;
+    private final float mInactiveTextAlpha;
+    private final float mShadowRadius;
+    private final float mStroke;
+    private final float mIconSize;
+    private final int mFrameColor;
+    private final int mFrameShadowColor;
+    private final int mTextColor;
+    private final int mHighlightColor;
+
+    private boolean mTouched;
+
     private boolean mActive;
     private boolean mInit = true;
     private KeyguardMultiUserSelectorView mUserSelector;
-
-    boolean mPressedStateLocked = false;
-    boolean mTempPressedStateHolder = false;
+    private KeyguardCircleFramedDrawable mFramed;
 
     public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
             KeyguardMultiUserSelectorView userSelector, UserInfo info) {
@@ -73,8 +91,29 @@
         super(context, attrs, defStyle);
 
         Resources res = mContext.getResources();
-        mActiveTextColor = res.getColor(R.color.kg_multi_user_text_active);
-        mInactiveTextColor = res.getColor(R.color.kg_multi_user_text_inactive);
+        mTextColor = res.getColor(R.color.keyguard_avatar_nick_color);
+        mIconSize = res.getDimension(R.dimen.keyguard_avatar_size);
+        mStroke = res.getDimension(R.dimen.keyguard_avatar_frame_stroke_width);
+        mShadowRadius = res.getDimension(R.dimen.keyguard_avatar_frame_shadow_radius);
+        mFrameColor = res.getColor(R.color.keyguard_avatar_frame_color);
+        mFrameShadowColor = res.getColor(R.color.keyguard_avatar_frame_shadow_color);
+        mHighlightColor = res.getColor(R.color.keyguard_avatar_frame_pressed_color);
+        mActiveTextAlpha = ACTIVE_TEXT_ALPHA;
+        mInactiveTextAlpha = INACTIVE_TEXT_ALPHA;
+        mActiveScale = ACTIVE_SCALE;
+        mActiveAlpha = ACTIVE_ALPHA;
+        mInactiveAlpha = INACTIVE_ALPHA;
+
+        mTouched = false;
+
+        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+    }
+
+    protected String rewriteIconPath(String path) {
+        if (!this.getClass().getName().contains("internal")) {
+            return path.replace("system", "data");
+        }
+        return path;
     }
 
     public void init(UserInfo user, KeyguardMultiUserSelectorView userSelector) {
@@ -84,39 +123,52 @@
         mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
         mUserName = (TextView) findViewById(R.id.keyguard_user_name);
 
-        mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
+        Bitmap icon = null; 
+        try {
+            icon = BitmapFactory.decodeFile(rewriteIconPath(user.iconPath));
+        } catch (Exception e) {
+            if (DEBUG) Log.d(TAG, "failed to open profile icon " + user.iconPath, e);
+        }
+
+        if (icon == null) {
+            icon = BitmapFactory.decodeResource(mContext.getResources(),
+                    com.android.internal.R.drawable.ic_contact_picture);
+        }
+
+        mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke,
+                mFrameShadowColor, mShadowRadius, mHighlightColor);
+        mUserImage.setImageDrawable(mFramed);
         mUserName.setText(mUserInfo.name);
         setOnClickListener(mUserSelector);
-        setActive(false, false, 0, null);
         mInit = false;
     }
 
-    public void setActive(boolean active, boolean animate, int duration, final Runnable onComplete) {
+    public void setActive(boolean active, boolean animate, final Runnable onComplete) {
         if (mActive != active || mInit) {
             mActive = active;
 
             if (active) {
-                KeyguardSubdivisionLayout parent = (KeyguardSubdivisionLayout) getParent();
-                parent.setTopChild(parent.indexOfChild(this));
+                KeyguardLinearLayout parent = (KeyguardLinearLayout) getParent();
+                parent.setTopChild(this);
             }
         }
-        updateVisualsForActive(mActive, animate, duration, true, onComplete);
+        updateVisualsForActive(mActive, animate, SWITCH_ANIMATION_DURATION, onComplete);
     }
 
-    void updateVisualsForActive(boolean active, boolean animate, int duration, boolean scale,
+    void updateVisualsForActive(boolean active, boolean animate, int duration,
             final Runnable onComplete) {
-        final float finalAlpha = active ? ACTIVE_ALPHA : INACTIVE_ALPHA;
-        final float initAlpha = active ? INACTIVE_ALPHA : ACTIVE_ALPHA;
-        final float finalScale = active && scale ? ACTIVE_SCALE : 1.0f;
-        final float initScale = active ? 1.0f : ACTIVE_SCALE;
-        final int finalTextBgAlpha = active ? (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255) :
-            (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255);
-        final int initTextBgAlpha = active ? (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255) :
-            (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255);
-        int textColor = active ? mActiveTextColor : mInactiveTextColor;
+        final float finalAlpha = active ? mActiveAlpha : mInactiveAlpha;
+        final float initAlpha = active ? mInactiveAlpha : mActiveAlpha;
+        final float finalScale = active ? 1f : 1f / mActiveScale;
+        final float initScale = mFramed.getScale();
+        final int finalTextAlpha = active ? (int) (mActiveTextAlpha * 255) :
+                (int) (mInactiveTextAlpha * 255);
+        final int initTextAlpha = active ? (int) (mInactiveTextAlpha * 255) :
+                (int) (mActiveTextAlpha * 255);
+        int textColor = mTextColor;
         mUserName.setTextColor(textColor);
 
-        if (animate) {
+        if (animate && mTouched) {
             ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
             va.addUpdateListener(new AnimatorUpdateListener() {
                 @Override
@@ -124,12 +176,11 @@
                     float r = animation.getAnimatedFraction();
                     float scale = (1 - r) * initScale + r * finalScale;
                     float alpha = (1 - r) * initAlpha + r * finalAlpha;
-                    int textBgAlpha = (int) ((1 - r) * initTextBgAlpha + r * finalTextBgAlpha);
-                    setScaleX(scale);
-                    setScaleY(scale);
+                    int textAlpha = (int) ((1 - r) * initTextAlpha + r * finalTextAlpha);
+                    mFramed.setScale(scale);
                     mUserImage.setAlpha(alpha);
-                    mUserName.setBackgroundColor(Color.argb(textBgAlpha, 0, 0, 0));
-                    mUserSelector.invalidate();
+                    mUserName.setTextColor(Color.argb(textAlpha, 255, 255, 255));
+                    mUserImage.invalidate();
                 }
             });
             va.addListener(new AnimatorListenerAdapter() {
@@ -143,37 +194,23 @@
             va.setDuration(duration);
             va.start();
         } else {
-            setScaleX(finalScale);
-            setScaleY(finalScale);
+            mFramed.setScale(finalScale);
             mUserImage.setAlpha(finalAlpha);
-            mUserName.setBackgroundColor(Color.argb(finalTextBgAlpha, 0, 0, 0));
+            mUserName.setTextColor(Color.argb(finalTextAlpha, 255, 255, 255));
             if (onComplete != null) {
                 post(onComplete);
             }
         }
-    }
 
-    public void lockPressedState() {
-        mPressedStateLocked = true;
-    }
-
-    public void resetPressedState() {
-        mPressedStateLocked = false;
-        post(new Runnable() {
-            @Override
-            public void run() {
-                KeyguardMultiUserAvatar.this.setPressed(mTempPressedStateHolder);
-            }
-        });
+        mTouched = true;
     }
 
     @Override
     public void setPressed(boolean pressed) {
-        if (!mPressedStateLocked) {
+        if (!pressed || isClickable()) {
             super.setPressed(pressed);
-            updateVisualsForActive(pressed || mActive, false, 0, mActive, null);
-        } else {
-            mTempPressedStateHolder = pressed;
+            mFramed.setPressed(pressed);
+            mUserImage.invalidate();
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
index 246c255..728e87c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
@@ -20,26 +20,26 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
-import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.MotionEvent;
 import android.view.View;
-import android.view.WindowManagerGlobal;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import com.android.internal.R;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 
 public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener {
     private static final String TAG = "KeyguardMultiUserSelectorView";
 
-    private KeyguardSubdivisionLayout mUsersGrid;
+    private ViewGroup mUsersGrid;
     private KeyguardMultiUserAvatar mActiveUserAvatar;
     private KeyguardHostView.UserSwitcherCallback mCallback;
-    private static final int SWITCH_ANIMATION_DURATION = 150;
     private static final int FADE_OUT_ANIMATION_DURATION = 100;
 
     public KeyguardMultiUserSelectorView(Context context) {
@@ -55,19 +55,18 @@
     }
 
     protected void onFinishInflate () {
-        init();
+        mUsersGrid = (ViewGroup) findViewById(R.id.keyguard_users_grid);
+        mUsersGrid.removeAllViews();
+        setClipChildren(false);
+        setClipToPadding(false);
+
     }
 
     public void setCallback(KeyguardHostView.UserSwitcherCallback callback) {
         mCallback = callback;
     }
 
-    public void init() {
-        mUsersGrid = (KeyguardSubdivisionLayout) findViewById(R.id.keyguard_users_grid);
-        mUsersGrid.removeAllViews();
-        setClipChildren(false);
-        setClipToPadding(false);
-
+    public void addUsers(Collection<UserInfo> userList) {
         UserInfo activeUser;
         try {
             activeUser = ActivityManagerNative.getDefault().getCurrentUser();
@@ -75,17 +74,18 @@
             activeUser = null;
         }
 
-        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers(true));
+        ArrayList<UserInfo> users = new ArrayList<UserInfo>(userList);
         Collections.sort(users, mOrderAddedComparator);
 
         for (UserInfo user: users) {
             KeyguardMultiUserAvatar uv = createAndAddUser(user);
             if (user.id == activeUser.id) {
                 mActiveUserAvatar = uv;
+                mActiveUserAvatar.setActive(true, false, null);
+            } else {
+                uv.setActive(false, false, null);
             }
         }
-        mActiveUserAvatar.setActive(true, false, 0, null);
     }
 
     Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
@@ -103,27 +103,57 @@
     }
 
     @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        if(event.getActionMasked() != MotionEvent.ACTION_CANCEL && mCallback != null) {
+            mCallback.userActivity();
+        }
+        return false;
+    }
+
+    private void setAllClickable(boolean clickable)
+    {
+        for(int i = 0; i < mUsersGrid.getChildCount(); i++) {
+            View v = mUsersGrid.getChildAt(i);
+            v.setClickable(clickable);
+            v.setPressed(false);
+        }
+    }
+
+    @Override
     public void onClick(View v) {
         if (!(v instanceof KeyguardMultiUserAvatar)) return;
         final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v;
-        if (mActiveUserAvatar == avatar) {
-            // If they click the currently active user, show the unlock hint
-            mCallback.showUnlockHint();
-            return;
-        } else {
-            // Reset the previously active user to appear inactive
-            avatar.lockPressedState();
-            mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
-            mActiveUserAvatar.setActive(false, true,  SWITCH_ANIMATION_DURATION, new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        ActivityManagerNative.getDefault().switchUser(avatar.getUserInfo().id);
-                    } catch (RemoteException re) {
-                        Log.e(TAG, "Couldn't switch user " + re);
+        if (avatar.isClickable()) { // catch race conditions
+            if (mActiveUserAvatar == avatar) {
+                // If they click the currently active user, show the unlock hint
+                mCallback.showUnlockHint();
+                return;
+            } else {
+                // Reset the previously active user to appear inactive
+                mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
+                setAllClickable(false);
+                mActiveUserAvatar.setActive(false, true, new Runnable() {
+                    @Override
+                    public void run() {
+                        mActiveUserAvatar = avatar;
+                        mActiveUserAvatar.setActive(true, true, new Runnable() {
+                            @Override
+                            public void run() {
+                                if (this.getClass().getName().contains("internal")) {
+                                    try {
+                                        ActivityManagerNative.getDefault()
+                                                .switchUser(avatar.getUserInfo().id);
+                                    } catch (RemoteException re) {
+                                        Log.e(TAG, "Couldn't switch user " + re);
+                                    }
+                                } else {
+                                    setAllClickable(true);
+                                }
+                            }
+                        });
                     }
-                }
-            });
+                });
+            }
         }
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
new file mode 100644
index 0000000..5cdf4d3
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.text.method.DigitsKeyListener;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.R;
+
+/**
+ * Displays a PIN pad for unlocking.
+ */
+public class KeyguardPINView extends KeyguardAbsKeyInputView
+        implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+
+    // To avoid accidental lockout due to events while the device in in the pocket, ignore
+    // any passwords with length less than or equal to this length.
+    private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+
+    // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing
+    private static final boolean ENABLE_HIDE_KEYBOARD = false;
+
+    public KeyguardPINView(Context context) {
+        this(context, null);
+    }
+
+    public KeyguardPINView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    protected void resetState() {
+        mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false);
+        mPasswordEntry.setEnabled(true);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        final View ok = findViewById(R.id.key_enter);
+        if (ok != null) {
+            ok.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    doHapticKeyClick();
+                    verifyPasswordAndUnlock();
+                }
+            });
+        }
+
+        // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
+        // not a separate view
+        View pinDelete = findViewById(R.id.delete_button);
+        if (pinDelete != null) {
+            pinDelete.setVisibility(View.VISIBLE);
+            pinDelete.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    CharSequence str = mPasswordEntry.getText();
+                    if (str.length() > 0) {
+                        mPasswordEntry.setText(str.subSequence(0, str.length()-1));
+                    }
+                    doHapticKeyClick();
+                }
+            });
+            pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
+                public boolean onLongClick(View v) {
+                    mPasswordEntry.setText("");
+                    doHapticKeyClick();
+                    return true;
+                }
+            });
+        }
+
+        mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
+        mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
+                | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+
+        mPasswordEntry.requestFocus();
+    }
+
+    @Override
+    public void showUsabilityHint() {
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
index be2dc8f..b6334f0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
@@ -16,70 +16,35 @@
 
 package com.android.internal.policy.impl.keyguard;
 
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-import java.util.List;
-
 import android.app.admin.DevicePolicyManager;
+import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Rect;
-
-import com.android.internal.widget.PasswordEntryKeyboardView;
-
-import android.os.CountDownTimer;
-import android.os.SystemClock;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.TextWatcher;
 import android.text.method.DigitsKeyListener;
 import android.text.method.TextKeyListener;
-import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.inputmethod.EditorInfo;
+import android.util.AttributeSet;
+import android.view.View;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
+import com.android.internal.R;
 import com.android.internal.widget.PasswordEntryKeyboardHelper;
+import com.android.internal.widget.PasswordEntryKeyboardView;
+
+import java.util.List;
 /**
- * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
+ * Displays an alphanumeric (latin-1) key entry for the user to enter
  * an unlock password
  */
 
-public class KeyguardPasswordView extends LinearLayout
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
-    /** Delay in ms between updates to the "too many attempts" count down. */
-    private static final long LOCKOUT_INTERVAL = 1000;
 
-    /**
-     * Delay in ms between updates to the "too many attempts" count down used
-     * when accessibility is turned on. Less annoying than the shorter default
-     * {@link #LOCKOUT_INTERVAL}.
-     */
-    private static final long ACCESSIBILITY_LOCKOUT_INTERVAL = 10000;
-
-    private KeyguardSecurityCallback mCallback;
-    private EditText mPasswordEntry;
-    private LockPatternUtils mLockPatternUtils;
-    private PasswordEntryKeyboardView mKeyboardView;
-    private PasswordEntryKeyboardHelper mKeyboardHelper;
-    private boolean mIsAlpha;
-    private SecurityMessageDisplay mSecurityMessageDisplay;
-
-    // To avoid accidental lockout due to events while the device in in the pocket, ignore
-    // any passwords with length less than or equal to this length.
-    private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
-
-    // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing
-    private static final boolean ENABLE_HIDE_KEYBOARD = false;
+    InputMethodManager mImm;
 
     public KeyguardPasswordView(Context context) {
         super(context);
@@ -89,110 +54,40 @@
         super(context, attrs);
     }
 
-    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
-        mCallback = callback;
-    }
-
-    public void setLockPatternUtils(LockPatternUtils utils) {
-        mLockPatternUtils = utils;
+    protected void resetState() {
+        mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
+        mPasswordEntry.setEnabled(true);
     }
 
     @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        if (hasWindowFocus) {
-            reset();
-        }
+    public boolean needsInput() {
+        return true;
     }
 
-    public void reset() {
-        // start fresh
-        mPasswordEntry.setText("");
-        mPasswordEntry.requestFocus();
-
-        // if the user is currently locked out, enforce it.
-        long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
-        if (deadline != 0) {
-            handleAttemptLockout(deadline);
-        } else {
-            resetState();
-        }
+    @Override
+    public void onResume() {
+        super.onResume();
+        mImm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
     }
 
-    private void resetState() {
-        mSecurityMessageDisplay.setMessage(
-                mIsAlpha ? R.string.kg_password_instructions : R.string.kg_pin_instructions, false);
-        mPasswordEntry.setEnabled(true);
-        mKeyboardView.setEnabled(true);
+    @Override
+    public void onPause() {
+        super.onPause();
+        mImm.hideSoftInputFromWindow(getWindowToken(), 0);
     }
 
     @Override
     protected void onFinishInflate() {
-        // We always set a dummy NavigationManager to avoid null checks
-        mSecurityMessageDisplay = new KeyguardNavigationManager(null);
-
-        mLockPatternUtils = new LockPatternUtils(mContext); // TODO: use common one
-
-        final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
-        mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
-                || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality
-                || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality;
-
-        mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
-        mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
-        mPasswordEntry.setOnEditorActionListener(this);
-        mPasswordEntry.addTextChangedListener(this);
-
-        mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false,
-                new int[] {
-                    R.xml.kg_password_kbd_numeric,
-                    com.android.internal.R.xml.password_kbd_qwerty,
-                    com.android.internal.R.xml.password_kbd_qwerty_shifted,
-                    com.android.internal.R.xml.password_kbd_symbols,
-                    com.android.internal.R.xml.password_kbd_symbols_shift
-                    }
-        );
-        mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+        super.onFinishInflate();
 
         boolean imeOrDeleteButtonVisible = false;
-        if (mIsAlpha) {
-            // We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard
-            mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
-            mKeyboardView.setVisibility(View.GONE);
-        } else {
-            mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
 
-            // Use lockscreen's numeric keyboard if the physical keyboard isn't showing
-            boolean hardKeyboardVisible = getResources().getConfiguration().hardKeyboardHidden
-                    == Configuration.HARDKEYBOARDHIDDEN_NO;
-            mKeyboardView.setVisibility(
-                    (ENABLE_HIDE_KEYBOARD && hardKeyboardVisible) ? View.INVISIBLE : View.VISIBLE);
+        mImm = (InputMethodManager) getContext().getSystemService(
+                Context.INPUT_METHOD_SERVICE);
 
-            // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
-            // not a separate view
-            View pinDelete = findViewById(R.id.delete_button);
-            if (pinDelete != null) {
-                pinDelete.setVisibility(View.VISIBLE);
-                imeOrDeleteButtonVisible = true;
-                pinDelete.setOnClickListener(new OnClickListener() {
-                    public void onClick(View v) {
-                        mKeyboardHelper.handleBackspace();
-                    }
-                });
-            }
-        }
-
-        mPasswordEntry.requestFocus();
-
-        // This allows keyboards with overlapping qwerty/numeric keys to choose just numeric keys.
-        if (mIsAlpha) {
-            mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
-            mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
-                    | InputType.TYPE_TEXT_VARIATION_PASSWORD);
-        } else {
-            mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
-            mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
-                    | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
-        }
+        mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+        mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
+                | InputType.TYPE_TEXT_VARIATION_PASSWORD);
 
         // Poke the wakelock any time the text is selected or modified
         mPasswordEntry.setOnClickListener(new OnClickListener() {
@@ -215,17 +110,17 @@
             }
         });
 
+        mPasswordEntry.requestFocus();
+
         // If there's more than one IME, enable the IME switcher button
         View switchImeButton = findViewById(R.id.switch_ime_button);
-        final InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
-                Context.INPUT_METHOD_SERVICE);
-        if (mIsAlpha && switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(imm, false)) {
+        if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) {
             switchImeButton.setVisibility(View.VISIBLE);
             imeOrDeleteButtonVisible = true;
             switchImeButton.setOnClickListener(new OnClickListener() {
                 public void onClick(View v) {
                     mCallback.userActivity(0); // Leave the screen on a bit longer
-                    imm.showInputMethodPicker();
+                    mImm.showInputMethodPicker();
                 }
             });
         }
@@ -291,113 +186,6 @@
     }
 
     @Override
-    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-        // send focus to the password field
-        return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
-    }
-
-    private void verifyPasswordAndUnlock() {
-        String entry = mPasswordEntry.getText().toString();
-        if (mLockPatternUtils.checkPassword(entry)) {
-            mCallback.reportSuccessfulUnlockAttempt();
-            mCallback.dismiss(true);
-        } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
-            // to avoid accidental lockout, only count attempts that are long enough to be a
-            // real password. This may require some tweaking.
-            mCallback.reportFailedUnlockAttempt();
-            if (0 == (mCallback.getFailedAttempts()
-                    % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
-                long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
-                handleAttemptLockout(deadline);
-            }
-            mSecurityMessageDisplay.setMessage(
-                    mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin, true);
-        }
-        mPasswordEntry.setText("");
-    }
-
-    // Prevent user from using the PIN/Password entry until scheduled deadline.
-    private void handleAttemptLockout(long elapsedRealtimeDeadline) {
-        mPasswordEntry.setEnabled(false);
-        mKeyboardView.setEnabled(false);
-        long elapsedRealtime = SystemClock.elapsedRealtime();
-        final AccessibilityManager accessManager =
-                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        // Use a longer update interval when accessibility is turned on.
-        final long interval = accessManager.isEnabled() ? ACCESSIBILITY_LOCKOUT_INTERVAL
-                : LOCKOUT_INTERVAL;
-        new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, interval) {
-
-            @Override
-            public void onTick(long millisUntilFinished) {
-                int secondsRemaining = (int) (millisUntilFinished / 1000);
-                mSecurityMessageDisplay.setMessage(
-                        R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
-            }
-
-            @Override
-            public void onFinish() {
-                resetState();
-            }
-        }.start();
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        mCallback.userActivity(0);
-        return false;
-    }
-
-    @Override
-    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-        // Check if this was the result of hitting the enter key
-        if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
-                || actionId == EditorInfo.IME_ACTION_NEXT) {
-            verifyPasswordAndUnlock();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean needsInput() {
-        return mIsAlpha;
-    }
-
-    @Override
-    public void onPause() {
-
-    }
-
-    @Override
-    public void onResume() {
-        reset();
-    }
-
-    @Override
-    public KeyguardSecurityCallback getCallback() {
-        return mCallback;
-    }
-
-    @Override
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-        if (mCallback != null) {
-            mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS);
-        }
-    }
-
-    @Override
-    public void onTextChanged(CharSequence s, int start, int before, int count) {
-    }
-
-    @Override
-    public void afterTextChanged(Editable s) {
-    }
-
-    @Override
-    public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
-        mSecurityMessageDisplay = display;
-        reset();
+    public void showUsabilityHint() {
     }
 }
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
index dcf40bf..408a9c8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
@@ -200,6 +200,10 @@
 
     }
 
+    @Override
+    public void showUsabilityHint() {
+    }
+
     /** TODO: hook this up */
     public void cleanUp() {
         if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
new file mode 100644
index 0000000..f6a90c5
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
@@ -0,0 +1,20 @@
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class KeyguardSecurityContainer extends FrameLayout {
+
+    public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyguardSecurityContainer(Context context) {
+        this(null, null, 0);
+    }
+
+    public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
index 59e2ca9..7a69586 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
@@ -31,7 +31,8 @@
         Invalid, // NULL state
         None, // No security enabled
         Pattern, // Unlock by drawing a pattern.
-        Password, // Unlock by entering a password or PIN
+        Password, // Unlock by entering an alphanumeric password
+        PIN, // Strictly numeric password
         Biometric, // Unlock with a biometric key (e.g. finger print or face unlock)
         Account, // Unlock by entering an account's login and password.
         SimPin, // Unlock by entering a sim pin.
@@ -85,6 +86,9 @@
             final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality();
             switch (security) {
                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+                    mode = mLockPatternUtils.isLockPasswordEnabled() ?
+                            SecurityMode.PIN : SecurityMode.None;
+                    break;
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
@@ -117,7 +121,9 @@
      */
     SecurityMode getAlternateFor(SecurityMode mode) {
         if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed()
-                && (mode == SecurityMode.Password || mode == SecurityMode.Pattern)) {
+                && (mode == SecurityMode.Password
+                        || mode == SecurityMode.PIN
+                        || mode == SecurityMode.Pattern)) {
             return SecurityMode.Biometric;
         }
         return mode; // no alternate, return what was given
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
index 19bcae9..c3684c4 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
@@ -62,4 +62,6 @@
     KeyguardSecurityCallback getCallback();
 
     void setSecurityMessageDisplay(SecurityMessageDisplay display);
+
+    void showUsabilityHint();
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
index c4e1607..9cdbc2d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
@@ -21,14 +21,17 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.ViewFlipper;
 
+import com.android.internal.widget.LockPatternUtils;
+
 /**
  * Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
  * we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy.
  *
  */
-public class KeyguardSecurityViewFlipper extends ViewFlipper {
+public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
     private Rect mTempRect = new Rect();
 
     public KeyguardSecurityViewFlipper(Context context) {
@@ -55,4 +58,79 @@
         return result;
     }
 
+    KeyguardSecurityView getSecurityView() {
+        View child = getChildAt(getDisplayedChild());
+        if (child instanceof KeyguardSecurityView) {
+            return (KeyguardSecurityView) child;
+        }
+        return null;
+    }
+
+    @Override
+    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.setKeyguardCallback(callback);
+        }
+    }
+
+    @Override
+    public void setLockPatternUtils(LockPatternUtils utils) {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.setLockPatternUtils(utils);
+        }
+    }
+
+    @Override
+    public void reset() {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.reset();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.onPause();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.onResume();
+        }
+    }
+
+    @Override
+    public boolean needsInput() {
+        KeyguardSecurityView ksv = getSecurityView();
+        return (ksv != null) ? ksv.needsInput() : false;
+    }
+
+    @Override
+    public KeyguardSecurityCallback getCallback() {
+        KeyguardSecurityView ksv = getSecurityView();
+        return (ksv != null) ? ksv.getCallback() : null;
+    }
+
+    @Override
+    public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.setSecurityMessageDisplay(display);
+        }
+    }
+
+    @Override
+    public void showUsabilityHint() {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.showUsabilityHint();
+        }
+    }
 }
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 1d26def..eba9a76 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -16,18 +16,12 @@
 package com.android.internal.policy.impl.keyguard;
 
 import android.animation.ObjectAnimator;
-import android.app.ActivityManagerNative;
 import android.app.SearchManager;
 import android.app.admin.DevicePolicyManager;
-import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.MediaStore;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -41,8 +35,6 @@
 import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
 import com.android.internal.R;
 
-import java.util.List;
-
 public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView {
     private static final boolean DEBUG = KeyguardHostView.DEBUG;
     private static final String TAG = "SecuritySelectorView";
@@ -67,7 +59,7 @@
                             ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
                             .getAssistIntent(mContext, UserHandle.USER_CURRENT);
                     if (assistIntent != null) {
-                        launchActivity(assistIntent, false);
+                        mActivityLauncher.launchActivity(assistIntent, false);
                     } else {
                         Log.w(TAG, "Failed to get intent for assist activity");
                     }
@@ -75,7 +67,7 @@
                     break;
 
                 case com.android.internal.R.drawable.ic_lockscreen_camera:
-                    launchCamera();
+                    mActivityLauncher.launchCamera();
                     mCallback.userActivity(0);
                     break;
 
@@ -119,49 +111,27 @@
         }
     };
 
+    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
+
+        @Override
+        KeyguardSecurityCallback getCallback() {
+            return mCallback;
+        }
+
+        @Override
+        LockPatternUtils getLockPatternUtils() {
+            return mLockPatternUtils;
+        }
+
+        @Override
+        Context getContext() {
+            return mContext;
+        }};
+
     public KeyguardSelectorView(Context context) {
         this(context, null);
     }
 
-    private boolean wouldLaunchResolverActivity(Intent intent) {
-        PackageManager packageManager = mContext.getPackageManager();
-        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
-                PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
-        final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
-                intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
-        // If the list contains the above resolved activity, then it can't be
-        // ResolverActivity itself.
-        for (int i = 0; i < appList.size(); i++) {
-            ResolveInfo tmp = appList.get(i);
-            if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
-                    && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    protected void launchCamera() {
-        if (mLockPatternUtils.isSecure()) {
-            // Launch the secure version of the camera
-            final Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
-            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
-            if (wouldLaunchResolverActivity(intent)) {
-                // TODO: Show disambiguation dialog instead.
-                // For now, we'll treat this like launching any other app from secure keyguard.
-                // When they do, user sees the system's ResolverActivity which lets them choose
-                // which secure camera to use.
-                launchActivity(intent, false);
-            } else {
-                launchActivity(intent, true);
-            }
-        } else {
-            // Launch the normal camera
-            launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA), false);
-        }
-    }
-
     public KeyguardSelectorView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLockPatternUtils = new LockPatternUtils(getContext());
@@ -183,7 +153,8 @@
         return mGlowPadView.getTargetPosition(resId) != -1;
     }
 
-    public void ping() {
+    @Override
+    public void showUsabilityHint() {
         mGlowPadView.ping();
     }
 
@@ -266,42 +237,6 @@
         mLockPatternUtils = utils;
     }
 
-    /**
-     * Launches the said intent for the current foreground user.
-     * @param intent
-     * @param showsWhileLocked true if the activity can be run on top of keyguard.
-     * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
-     */
-    private void launchActivity(final Intent intent, boolean showsWhileLocked) {
-        intent.addFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_SINGLE_TOP
-                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        boolean isSecure = mLockPatternUtils.isSecure();
-        if (!isSecure || showsWhileLocked) {
-            if (!isSecure) try {
-                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-            } catch (RemoteException e) {
-                Log.w(TAG, "can't dismiss keyguard on launch");
-            }
-            try {
-                mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
-            } catch (ActivityNotFoundException e) {
-                Log.w(TAG, "Activity not found for intent + " + intent.getAction());
-            }
-        } else {
-            // Create a runnable to start the activity and ask the user to enter their
-            // credentials.
-            mCallback.setOnDismissRunnable(new Runnable() {
-                @Override
-                public void run() {
-                    mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
-                }
-            });
-            mCallback.dismiss(false);
-        }
-    }
-
     @Override
     public void reset() {
         mGlowPadView.reset(false);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
index 7878e46..018a1aa 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
@@ -121,6 +121,10 @@
         mPinEntry.requestFocus();
     }
 
+    @Override
+    public void showUsabilityHint() {
+    }
+
     /** {@inheritDoc} */
     public void cleanUp() {
         // dismiss the dialog.
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
index 562d893..d0585b9 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
@@ -91,7 +91,8 @@
             } else if (state == CONFIRM_PIN) {
                 if (confirmPin()) {
                     state = DONE;
-                    msg = R.string.lockscreen_sim_unlock_progress_dialog_message;
+                    msg =
+                        com.android.internal.R.string.lockscreen_sim_unlock_progress_dialog_message;
                     updateSim();
                 } else {
                     msg = R.string.kg_invalid_confirm_pin_hint;
@@ -169,6 +170,10 @@
         reset();
     }
 
+    @Override
+    public void showUsabilityHint() {
+    }
+
     /** {@inheritDoc} */
     public void cleanUp() {
         // dismiss the dialog.
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
index 00cd5b9..f2cb522 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
@@ -20,6 +20,8 @@
 import android.util.AttributeSet;
 import android.widget.GridLayout;
 
+import com.android.internal.widget.LockPatternUtils;
+
 public class KeyguardStatusView extends GridLayout {
     @SuppressWarnings("unused")
     private KeyguardStatusViewManager mStatusViewManager;
@@ -36,6 +38,10 @@
         super(context, attrs, defStyle);
     }
 
+    public int getAppWidgetId() {
+        return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
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 5b85064..2837a66 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
@@ -16,15 +16,6 @@
 
 package com.android.internal.policy.impl.keyguard;
 
-import com.android.internal.R;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.widget.DigitalClock;
-import com.android.internal.widget.LockPatternUtils;
-
-import java.util.Date;
-
-import libcore.util.MutableInt;
-
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
@@ -39,6 +30,10 @@
 import android.view.View;
 import android.widget.TextView;
 
+import com.android.internal.R;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.widget.LockPatternUtils;
+
 import java.util.Date;
 
 import libcore.util.MutableInt;
@@ -78,7 +73,7 @@
     // Whether to use the last line as a combined line to either display owner info / charging.
     // If false, each item will be given a dedicated space.
     private boolean mShareStatusRegion = false;
-    
+
     // last known battery level
     private int mBatteryLevel = 100;
 
@@ -121,9 +116,9 @@
         if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
         mContainer = view;
         Resources res = getContext().getResources();
-        mDateFormatString = 
+        mDateFormatString =
                 res.getText(com.android.internal.R.string.abbrev_wday_month_day_no_year);
-        mShareStatusRegion = res.getBoolean(R.bool.kg_share_status_area);
+        mShareStatusRegion = res.getBoolean(com.android.internal.R.bool.kg_share_status_area);
         mLockPatternUtils = new LockPatternUtils(view.getContext());
         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(view.getContext());
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
deleted file mode 100644
index 1cd796c..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * A layout that arranges its children into a special type of grid.
- */
-public class KeyguardSubdivisionLayout extends ViewGroup {
-    ArrayList<BiTree> mCells = new ArrayList<BiTree>();
-    int mNumChildren = -1;
-    int mWidth = -1;
-    int mHeight = -1;
-    int mTopChild = 0;
-
-    public KeyguardSubdivisionLayout(Context context) {
-        this(context, null, 0);
-    }
-
-    public KeyguardSubdivisionLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public KeyguardSubdivisionLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        setClipChildren(false);
-        setClipToPadding(false);
-        setChildrenDrawingOrderEnabled(true);
-    }
-
-    private class BiTree {
-        Rect rect;
-        BiTree left;
-        BiTree right;
-        int nodeDepth;
-        ArrayList<BiTree> leafs;
-
-        public BiTree(Rect r) {
-            rect = r;
-        }
-
-        public BiTree() {
-        }
-
-        boolean isLeaf() {
-            return (left == null) && (right == null);
-        }
-
-        int depth() {
-            if (left != null && right != null) {
-                return Math.max(left.depth(), right.depth()) + 1;
-            } else if (left != null) {
-                return left.depth() + 1;
-            } else if (right != null) {
-                return right.depth() + 1;
-            } else {
-                return 1;
-            }
-        }
-
-        int numLeafs() {
-            if (left != null && right != null) {
-                return left.numLeafs() + right.numLeafs();
-            } else if (left != null) {
-                return left.numLeafs();
-            } else if (right != null) {
-                return right.numLeafs();
-            } else {
-                return 1;
-            }
-        }
-
-        BiTree getNextNodeToBranch() {
-            if (leafs == null) {
-                leafs = new ArrayList<BiTree>();
-            }
-            leafs.clear();
-            getLeafs(leafs, 1);
-
-            // If the tree is complete, then we start a new level at the rightmost side.
-            double r = log2(leafs.size());
-            if (Math.ceil(r) == Math.floor(r)) {
-                return leafs.get(leafs.size() - 1);
-            }
-
-            // Tree is not complete, find the first leaf who's depth is less than the depth of
-            // the tree.
-            int treeDepth = depth();
-            for (int i = leafs.size() - 1; i >= 0; i--) {
-                BiTree n = leafs.get(i);
-                if (n.nodeDepth < treeDepth) {
-                    return n;
-                }
-            }
-            return null;
-        }
-
-        // Gets leafs in left to right order
-        void getLeafs(ArrayList<BiTree> leafs, int depth) {
-            if (isLeaf()) {
-                this.nodeDepth = depth;
-                leafs.add(this);
-            } else {
-                if (left != null) {
-                    left.getLeafs(leafs, depth + 1);
-                }
-                if (right != null) {
-                    right.getLeafs(leafs, depth + 1);
-                }
-            }
-        }
-    }
-
-    double log2(double d) {
-        return Math.log(d) / Math.log(2);
-    }
-
-    private void addCell(BiTree tree) {
-        BiTree branch = tree.getNextNodeToBranch();
-        Rect r = branch.rect;
-        branch.left = new BiTree();
-        branch.right = new BiTree();
-        int newDepth = tree.depth();
-
-        // For each level of the tree, we alternate between horizontal and vertical division
-        if (newDepth % 2 == 0) {
-            // Divide the cell vertically
-            branch.left.rect = new Rect(r.left, r.top, r.right, r.top + r.height() / 2);
-            branch.right.rect = new Rect(r.left, r.top + r.height() / 2, r.right, r.bottom);
-        } else {
-            // Divide the cell horizontally
-            branch.left.rect = new Rect(r.left, r.top, r.left + r.width() / 2, r.bottom);
-            branch.right.rect = new Rect(r.left + r.width() / 2, r.top, r.right, r.bottom);
-        }
-    }
-
-    private void constructGrid(int width, int height, int numChildren) {
-        mCells.clear();
-        BiTree root = new BiTree(new Rect(0, 0, width, height));
-
-        // We add nodes systematically until the number of leafs matches the number of children
-        while (root.numLeafs() < numChildren) {
-            addCell(root);
-        }
-
-        // Spit out the final list of cells
-        root.getLeafs(mCells, 1);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height =  MeasureSpec.getSize(heightMeasureSpec);
-        int childCount = getChildCount();
-
-        if (mNumChildren != childCount || width != getMeasuredWidth() ||
-                height != getMeasuredHeight()) {
-            constructGrid(width, height, childCount);
-        }
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            Rect rect = mCells.get(i).rect;
-            child.measure(MeasureSpec.makeMeasureSpec(rect.width(), MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.EXACTLY));
-        }
-        setMeasuredDimension(width, height);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            Rect rect = mCells.get(i).rect;
-            child.layout(rect.left, rect.top, rect.right, rect.bottom);
-        }
-    }
-
-    public void setTopChild(int top) {
-        mTopChild = top;
-        invalidate();
-    }
-
-    protected int getChildDrawingOrder(int childCount, int i) {
-        int ret = i;
-        if (i == childCount - 1) {
-            ret = mTopChild;
-        } else if (i >= mTopChild){
-            ret = i + 1;
-        }
-        return ret;
-    }
-}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index 6a3c7c1..89f220a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -16,17 +16,15 @@
 
 package com.android.internal.policy.impl.keyguard;
 
-import java.lang.ref.WeakReference;
-
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
+import android.media.IRemoteControlDisplay;
 import android.media.MediaMetadataRetriever;
 import android.media.RemoteControlClient;
-import android.media.IRemoteControlDisplay;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -42,11 +40,12 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.internal.R;
+
+import java.lang.ref.WeakReference;
 /**
  * This is the widget responsible for showing music controls in keyguard.
  */
@@ -264,7 +263,7 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
+//        int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
 //        Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim);
 //        mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
     }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index d8e1c1a..316825a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@
     private static final int MSG_DPM_STATE_CHANGED = 309;
     private static final int MSG_USER_SWITCHED = 310;
     private static final int MSG_USER_REMOVED = 311;
+    private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
 
     private static KeyguardUpdateMonitor sInstance;
 
@@ -147,6 +148,10 @@
                 case MSG_USER_REMOVED:
                     handleUserRemoved(msg.arg1);
                     break;
+                case MSG_KEYGUARD_VISIBILITY_CHANGED:
+                    handleKeyguardVisibilityChanged(msg.arg1);
+                    break;
+
             }
         }
     };
@@ -557,6 +562,19 @@
         }
     }
 
+    /**
+     * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED}
+     */
+    private void handleKeyguardVisibilityChanged(int showing) {
+        if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")");
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onKeyguardVisibilityChanged(showing == 1);
+            }
+        }
+    }
+
     private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
         final boolean nowPluggedIn = current.isPluggedIn();
         final boolean wasPluggedIn = old.isPluggedIn();
@@ -659,6 +677,13 @@
         callback.onSimStateChanged(mSimState);
     }
 
+    public void sendKeyguardVisibilityChanged(boolean showing) {
+        if (DEBUG) Log.d(TAG, "sendKeyguardVisibilityChanged(" + showing + ")");
+        Message message = mHandler.obtainMessage(MSG_KEYGUARD_VISIBILITY_CHANGED);
+        message.arg1 = showing ? 1 : 0;
+        message.sendToTarget();
+    }
+
     public void reportClockVisible(boolean visible) {
         mClockVisible = visible;
         mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
index 3d65e68..8c9ac8b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
@@ -62,6 +62,12 @@
     void onPhoneStateChanged(int phoneState) { }
 
     /**
+     * Called when the visibility of the keyguard changes.
+     * @param showing Indicates if the keyguard is now visible.
+     */
+    void onKeyguardVisibilityChanged(boolean showing) { }
+
+    /**
      * Called when visibility of lockscreen clock changes, such as when
      * obscured by a widget.
      */
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
index 3191f4a..9e3424d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.policy.impl.keyguard;
 
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
@@ -27,11 +28,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.TelephonyManager;
-import android.view.KeyEvent;
-import android.widget.LinearLayout;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
+import android.view.KeyEvent;
+import android.widget.FrameLayout;
 
 /**
  * Base class for keyguard view.  {@link #reset} is where you should
@@ -42,7 +43,7 @@
  * Handles intercepting of media keys that still work when the keyguard is
  * showing.
  */
-public abstract class KeyguardViewBase extends LinearLayout {
+public abstract class KeyguardViewBase extends FrameLayout {
 
     private static final int BACKGROUND_COLOR = 0x70000000;
     private AudioManager mAudioManager;
@@ -249,7 +250,10 @@
     @Override
     public void dispatchSystemUiVisibilityChanged(int visibility) {
         super.dispatchSystemUiVisibilityChanged(visibility);
-        setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+
+        if (!(mContext instanceof Activity)) {
+            setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+        }
     }
 
     public void setViewMediatorCallback(
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 b66c883..9fa14f5 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -48,7 +48,7 @@
  * reported to this class by the current {@link KeyguardViewBase}.
  */
 public class KeyguardViewManager {
-    private final static boolean DEBUG = false;
+    private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
     private static String TAG = "KeyguardViewManager";
     public static boolean USE_UPPER_CASE = true;
 
@@ -65,7 +65,6 @@
     private FrameLayout mKeyguardHost;
     private KeyguardHostView mKeyguardView;
 
-    private boolean mScreenOn = false;
     private LockPatternUtils mLockPatternUtils;
 
     public interface ShowListener {
@@ -96,7 +95,7 @@
 
         boolean enableScreenRotation = shouldEnableScreenRotation();
 
-        maybeCreateKeyguardLocked(enableScreenRotation, options);
+        maybeCreateKeyguardLocked(enableScreenRotation, false, options);
         maybeEnableScreenRotation(enableScreenRotation);
 
         // Disable common aspects of the system/status/navigation bars that are not appropriate or
@@ -104,7 +103,7 @@
         // activities. Other disabled bits are handled by the KeyguardViewMediator talking
         // directly to the status bar service.
         final int visFlags = View.STATUS_BAR_DISABLE_HOME;
-        if (DEBUG) Log.v(TAG, "KGVM: Set visibility on " + mKeyguardHost + " to " + visFlags);
+        if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
         mKeyguardHost.setSystemUiVisibility(visFlags);
 
         mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
@@ -127,7 +126,12 @@
         @Override
         protected void onConfigurationChanged(Configuration newConfig) {
             super.onConfigurationChanged(newConfig);
-            maybeCreateKeyguardLocked(shouldEnableScreenRotation(), null);
+            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+                // only propagate configuration messages if we're currently showing
+                maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
+            } else {
+                if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
+            }
         }
 
         @Override
@@ -145,7 +149,8 @@
 
     SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
 
-    private void maybeCreateKeyguardLocked(boolean enableScreenRotation, Bundle options) {
+    private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
+            Bundle options) {
         final boolean isActivity = (mContext instanceof Activity); // for test activity
 
         if (mKeyguardHost != null) {
@@ -189,7 +194,9 @@
             mViewManager.addView(mKeyguardHost, lp);
         }
 
-        inflateKeyguardView(options);
+        if (force || mKeyguardView == null) {
+            inflateKeyguardView(options);
+        }
         updateUserActivityTimeoutInWindowLayoutParams();
         mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
 
@@ -230,11 +237,6 @@
                 mKeyguardView.showNextSecurityScreenIfPresent();
             }
         }
-
-        if (mScreenOn) {
-            mKeyguardView.show();
-            mKeyguardView.requestFocus();
-        }
     }
 
     public void updateUserActivityTimeout() {
@@ -295,12 +297,11 @@
         if (DEBUG) Log.d(TAG, "reset()");
         // User might have switched, check if we need to go back to keyguard
         // TODO: It's preferable to stay and show the correct lockscreen or unlock if none
-        maybeCreateKeyguardLocked(shouldEnableScreenRotation(), options);
+        maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options);
     }
 
     public synchronized void onScreenTurnedOff() {
         if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
-        mScreenOn = false;
         if (mKeyguardView != null) {
             mKeyguardView.onScreenTurnedOff();
         }
@@ -309,7 +310,6 @@
     public synchronized void onScreenTurnedOn(
             final KeyguardViewManager.ShowListener showListener) {
         if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
-        mScreenOn = true;
         if (mKeyguardView != null) {
             mKeyguardView.onScreenTurnedOn();
 
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 cb70922..bc12e96 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -46,7 +46,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -96,7 +95,7 @@
  */
 public class KeyguardViewMediator {
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
-    private final static boolean DEBUG = false;
+    final static boolean DEBUG = false;
     private final static boolean DBG_WAKE = false;
 
     private final static String TAG = "KeyguardViewMediator";
@@ -520,11 +519,11 @@
             if (DEBUG) Log.d(TAG, "onSystemReady");
             mSystemReady = true;
             mUpdateMonitor.registerCallback(mUpdateCallback);
-            
+
             // Disable alternate unlock right after boot until things have settled.
             mUpdateMonitor.setAlternateUnlockEnabled(false);
             mUpdateMonitor.setIsFirstBoot(true);
-            
+
             doKeyguardLocked();
         }
         // Most services aren't available until the system reaches the ready state, so we
@@ -629,7 +628,9 @@
             mScreenOn = true;
             cancelDoKeyguardLaterLocked();
             if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
-            notifyScreenOnLocked(showListener);
+            if (showListener != null) {
+                notifyScreenOnLocked(showListener);
+            }
         }
         maybeSendUserPresentBroadcast();
     }
@@ -769,6 +770,7 @@
      */
     public void setHidden(boolean isHidden) {
         if (DEBUG) Log.d(TAG, "setHidden " + isHidden);
+        mUpdateMonitor.sendKeyguardVisibilityChanged(!isHidden);
         mHandler.removeMessages(SET_HIDDEN);
         Message msg = mHandler.obtainMessage(SET_HIDDEN, (isHidden ? 1 : 0), 0);
         mHandler.sendMessage(msg);
@@ -1306,13 +1308,6 @@
                 // (like recents). Temporary enable/disable (e.g. the "back" button) are
                 // done in KeyguardHostView.
                 flags |= StatusBarManager.DISABLE_RECENT;
-                if (!mScreenOn) {
-                    // Disable all navbar buttons on screen off.  The navigation bar will hide
-                    // these immediately to avoid seeing the end of layout transition animations
-                    // if quickly turning back on.
-                    flags |= StatusBarManager.DISABLE_HOME;
-                    flags |= StatusBarManager.DISABLE_BACK;
-                }
                 if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
                     // showing secure lockscreen; disable expanding.
                     flags |= StatusBarManager.DISABLE_EXPAND;
@@ -1328,7 +1323,9 @@
                         + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
             }
 
-            mStatusBarManager.disable(flags);
+            if (!(mContext instanceof Activity)) {
+                mStatusBarManager.disable(flags);
+            }
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
new file mode 100644
index 0000000..c163b97
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+
+public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChallengeScrolledListener {
+
+    private KeyguardWidgetPager mPagedView;
+    private int mCurrentPageIndex;
+    private ChallengeLayout mChallengeLayout;
+    private Runnable mHideHintsRunnable;
+    private KeyguardSecurityView mKeyguardSecurityContainer;
+    private int[] mTmpPoint = new int[2];
+    private static final int SCREEN_ON_HINT_DURATION = 1000;
+    Handler mMainQueue = new Handler(Looper.myLooper());
+
+    int mChallengeTop = 0;
+
+    public KeyguardViewStateManager() {
+    }
+
+    public void setPagedView(KeyguardWidgetPager pagedView) {
+        mPagedView = pagedView;
+    }
+
+    public void setChallengeLayout(ChallengeLayout layout) {
+        mChallengeLayout = layout;
+    }
+
+    public void setSecurityViewContainer(KeyguardSecurityView container) {
+        mKeyguardSecurityContainer = container;
+    }
+
+    public void onPageBeginMoving() {
+        if (mChallengeLayout.isChallengeShowing()) {
+            mChallengeLayout.showChallenge(false);
+        }
+        if (mHideHintsRunnable != null) {
+            mMainQueue.removeCallbacks(mHideHintsRunnable);
+            mHideHintsRunnable = null;
+        }
+    }
+
+    public void onPageEndMoving() {
+    }
+
+    public void showBouncer(boolean show) {
+        mChallengeLayout.showBouncer();
+    }
+
+    public void onPageSwitch(View newPage, int newPageIndex) {
+        // Reset the previous page size and ensure the current page is sized appropriately
+        if (mPagedView != null) {
+            KeyguardWidgetFrame oldPage = mPagedView.getWidgetPageAt(mCurrentPageIndex);
+            // Reset the old widget page to full size
+            if (oldPage != null) {
+                oldPage.resetSize();
+            }
+
+            KeyguardWidgetFrame newCurPage = mPagedView.getWidgetPageAt(newPageIndex);
+            if (mChallengeLayout.isChallengeOverlapping()) {
+                sizeWidgetFrameToChallengeTop(newCurPage);
+            }
+        }
+        mCurrentPageIndex = newPageIndex;
+    }
+
+    private void sizeWidgetFrameToChallengeTop(KeyguardWidgetFrame frame) {
+        if (frame == null) return;
+        mTmpPoint[0] = 0;
+        mTmpPoint[1] = mChallengeTop;
+        mapPoint((View) mChallengeLayout, frame, mTmpPoint);
+        frame.setChallengeTop(mTmpPoint[1]);
+    }
+
+    /**
+     * Simple method to map a point from one view's coordinates to another's. Note: this method
+     * doesn't account for transforms, so if the views will be transformed, this should not be used.
+     *
+     * @param fromView The view to which the point is relative
+     * @param toView The view into which the point should be mapped
+     * @param pt The point
+     */
+    public void mapPoint(View fromView, View toView, int pt[]) {
+        int[] loc = new int[2];
+        fromView.getLocationInWindow(loc);
+        int x = loc[0];
+        int y = loc[1];
+
+        toView.getLocationInWindow(loc);
+        int vX = loc[0];
+        int vY = loc[1];
+
+        pt[0] += x - vX;
+        pt[1] += y - vY;
+    }
+
+    @Override
+    public void onScrollStateChanged(int scrollState) {
+        if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
+            if (mPagedView == null) return;
+
+            boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
+            int curPage = mPagedView.getCurrentPage();
+            KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(curPage);
+
+            if (frame != null) {
+                if (!challengeOverlapping) {
+                    frame.resetSize();
+                } else {
+                    sizeWidgetFrameToChallengeTop(frame);
+                }
+            }
+
+            if (challengeOverlapping) {
+                mPagedView.setOnlyAllowEdgeSwipes(true);
+            } else {
+                mPagedView.setOnlyAllowEdgeSwipes(false);
+            }
+
+            if (mChallengeLayout.isChallengeShowing()) {
+                mKeyguardSecurityContainer.onResume();
+            } else {
+                mKeyguardSecurityContainer.onPause();
+            }
+        } else {
+            // View is on the move.  Pause the security view until it completes.
+            mKeyguardSecurityContainer.onPause();
+        }
+    }
+
+    public void showUsabilityHints() {
+        mKeyguardSecurityContainer.showUsabilityHint();
+        mPagedView.showInitialPageHints();
+        mHideHintsRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mPagedView.hideOutlinesAndSidePages();
+                mHideHintsRunnable = null;
+            }
+        };
+
+        mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION);
+    }
+
+    @Override
+    public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
+        mChallengeTop = challengeTop;
+    }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
new file mode 100644
index 0000000..02c32d4
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.internal.R;
+
+public class KeyguardWidgetCarousel extends KeyguardWidgetPager {
+
+    private float mAdjacentPagesAngle;
+    private static float CAMERA_DISTANCE = 10000;
+
+    public KeyguardWidgetCarousel(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyguardWidgetCarousel(Context context) {
+        this(context, null, 0);
+    }
+
+    public KeyguardWidgetCarousel(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mAdjacentPagesAngle = context.getResources().getInteger(R.integer.kg_carousel_angle);
+    }
+
+    protected float getMaxScrollProgress() {
+        return 1.5f;
+    }
+
+    private void updatePageAlphaValues(int screenCenter) {
+        boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
+        if (!isInOverscroll) {
+            for (int i = 0; i < getChildCount(); i++) {
+                KeyguardWidgetFrame child = getWidgetPageAt(i);
+                if (child != null) {
+                    float scrollProgress = getScrollProgress(screenCenter, child, i);
+                    if (!isReordering(false)) {
+                        child.setBackgroundAlphaMultiplier(
+                                backgroundAlphaInterpolator(Math.abs(scrollProgress)));
+                    } else {
+                        child.setBackgroundAlphaMultiplier(1f);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void screenScrolled(int screenCenter) {
+        updatePageAlphaValues(screenCenter);
+        for (int i = 0; i < getChildCount(); i++) {
+            KeyguardWidgetFrame v = getWidgetPageAt(i);
+            if (v == mDragView) continue;
+            if (v != null) {
+                float scrollProgress = getScrollProgress(screenCenter, v, i);
+                int width = v.getMeasuredWidth();
+                float pivotX = (width / 2f) + scrollProgress * (width / 2f);
+                float pivotY = v.getMeasuredHeight() / 2;
+                float rotationY = - mAdjacentPagesAngle * scrollProgress;
+                v.setCameraDistance(CAMERA_DISTANCE);
+                v.setPivotX(pivotX);
+                v.setPivotY(pivotY);
+                v.setRotationY(rotationY);
+            }
+        }
+    }
+}
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 311eec6..9e1189c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -16,20 +16,20 @@
 
 package com.android.internal.policy.impl.keyguard;
 
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.LinearGradient;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.Shader;
-import android.os.PowerManager;
-import android.os.SystemClock;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.View;
 import android.widget.FrameLayout;
 
 import com.android.internal.R;
@@ -48,8 +48,12 @@
     private float mOverScrollAmount = 0f;
     private final Rect mForegroundRect = new Rect();
     private int mForegroundAlpha = 0;
-    private PowerManager mPowerManager;
-    private boolean mDisableInteraction;
+    private CheckLongPressHelper mLongPressHelper;
+
+    private float mBackgroundAlpha;
+    private float mBackgroundAlphaMultiplier = 1.0f;
+    private Drawable mBackgroundDrawable;
+    private Rect mBackgroundRect = new Rect();
 
     public KeyguardWidgetFrame(Context context) {
         this(context, null, 0);
@@ -62,35 +66,115 @@
     public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mLongPressHelper = new CheckLongPressHelper(this);
 
         Resources res = context.getResources();
-        int hPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_horizontal_padding);
-        int topPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_top_padding);
-        int bottomPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
-        setPadding(hPadding, topPadding, hPadding, bottomPadding);
+        // TODO: this padding should really correspond to the padding embedded in the background
+        // drawable (ie. outlines).
+        int padding = (int) (res.getDisplayMetrics().density * 8);
+        setPadding(padding, padding, padding, padding);
+
+        mBackgroundDrawable = res.getDrawable(R.drawable.security_frame);
         mGradientColor = res.getColor(com.android.internal.R.color.kg_widget_pager_gradient);
         mGradientPaint.setXfermode(sAddBlendMode);
     }
 
-    public void setDisableUserInteraction(boolean disabled) {
-        mDisableInteraction = disabled;
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Watch for longpress events at this level to make sure
+        // users can always pick up this widget
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mLongPressHelper.postCheckForLongPress(ev);
+                break;
+            case MotionEvent.ACTION_MOVE:
+                mLongPressHelper.onMove(ev);
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mLongPressHelper.cancelLongPress();
+                break;
+        }
+
+        // Otherwise continue letting touch events fall through to children
+        return false;
     }
 
     @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (!mDisableInteraction) {
-            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
-            return super.onInterceptTouchEvent(ev);
+    public boolean onTouchEvent(MotionEvent ev) {
+        // Watch for longpress events at this level to make sure
+        // users can always pick up this widget
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_MOVE:
+                mLongPressHelper.onMove(ev);
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mLongPressHelper.cancelLongPress();
+                break;
         }
+
+        // We return true here to ensure that we will get cancel / up signal
+        // even if none of our children have requested touch.
         return true;
     }
 
     @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        cancelLongPress();
+    }
+
+    @Override
+    public void cancelLongPress() {
+        super.cancelLongPress();
+        mLongPressHelper.cancelLongPress();
+    }
+
+    @Override
     protected void dispatchDraw(Canvas canvas) {
+        drawBg(canvas);
         super.dispatchDraw(canvas);
         drawGradientOverlay(canvas);
+    }
 
+    /**
+     * Because this view has fading outlines, it is essential that we enable hardware
+     * layers on the content (child) so that updating the alpha of the outlines doesn't
+     * result in the content layer being recreated.
+     */
+    public void enableHardwareLayersForContent() {
+        View widget = getContent();
+        if (widget != null) {
+            widget.setLayerType(LAYER_TYPE_HARDWARE, null);
+        }
+    }
+
+    /**
+     * Because this view has fading outlines, it is essential that we enable hardware
+     * layers on the content (child) so that updating the alpha of the outlines doesn't
+     * result in the content layer being recreated.
+     */
+    public void disableHardwareLayersForContent() {
+        View widget = getContent();
+        if (widget != null) {
+            widget.setLayerType(LAYER_TYPE_NONE, null);
+        }
+    }
+
+    public View getContent() {
+        return getChildAt(0);
+    }
+
+    public int getContentAppWidgetId() {
+        View content = getContent();
+        if (content instanceof AppWidgetHostView) {
+            return ((AppWidgetHostView) content).getAppWidgetId();
+        } else {
+            return ((KeyguardStatusView) content).getAppWidgetId();
+        }
     }
 
     private void drawGradientOverlay(Canvas c) {
@@ -99,6 +183,81 @@
         c.drawRect(mForegroundRect, mGradientPaint);
     }
 
+    protected void drawBg(Canvas canvas) {
+        if (mBackgroundAlpha > 0.0f) {
+            Drawable bg = mBackgroundDrawable;
+
+            bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
+            bg.setBounds(mBackgroundRect);
+            bg.draw(canvas);
+        }
+    }
+
+    public float getBackgroundAlpha() {
+        return mBackgroundAlpha;
+    }
+
+    public void setBackgroundAlphaMultiplier(float multiplier) {
+        if (mBackgroundAlphaMultiplier != multiplier) {
+            mBackgroundAlphaMultiplier = multiplier;
+            invalidate();
+        }
+    }
+
+    public float getBackgroundAlphaMultiplier() {
+        return mBackgroundAlphaMultiplier;
+    }
+
+    public void setBackgroundAlpha(float alpha) {
+        if (mBackgroundAlpha != alpha) {
+            mBackgroundAlpha = alpha;
+            invalidate();
+        }
+    }
+
+    public void setContentAlpha(float alpha) {
+        View content = getContent();
+        if (content != null) {
+            content.setAlpha(alpha);
+        }
+    }
+
+    /**
+     * Depending on whether the security is up, the widget size needs to change
+     * 
+     * @param height The height of the widget, -1 for full height
+     */
+    public void setWidgetHeight(int height) {
+        boolean needLayout = false;
+        View widget = getContent();
+        if (widget != null) {
+            LayoutParams lp = (LayoutParams) widget.getLayoutParams();
+            if (lp.height != height) {
+                needLayout = true;
+                lp.height = height;
+            }
+        }
+        if (needLayout) {
+            requestLayout();
+        }
+    }
+
+    /**
+     * Set the top location of the challenge.
+     *
+     * @param top The top of the challenge, in _local_ coordinates, or -1 to indicate the challenge
+     *              is down.
+     */
+    public void setChallengeTop(int top) {
+        // The widget starts below the padding, and extends to the top of the challengs.
+        int widgetHeight = top - getPaddingTop();
+        setWidgetHeight(widgetHeight);
+    }
+
+    public void resetSize() {
+        setWidgetHeight(LayoutParams.MATCH_PARENT);
+    }
+
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
@@ -110,6 +269,7 @@
                 mGradientColor, 0, Shader.TileMode.CLAMP);
         mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f,
                 mGradientColor, 0, Shader.TileMode.CLAMP);
+        mBackgroundRect.set(0, 0, w, h);
     }
 
     void setOverScrollAmount(float r, boolean left) {
@@ -120,4 +280,8 @@
             invalidate();
         }
     }
+
+    public void onActive(boolean isActive) {
+        // hook for subclasses
+    }
 }
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 1e65665..63e7fdd 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -15,29 +15,52 @@
  */
 package com.android.internal.policy.impl.keyguard;
 
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
+import android.content.res.Resources;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
-
 import android.widget.FrameLayout;
 
 import com.android.internal.R;
 
-public class KeyguardWidgetPager extends PagedView {
+import com.android.internal.widget.LockPatternUtils;
+
+public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
+        OnLongClickListener {
+
     ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
     private static float CAMERA_DISTANCE = 10000;
-    private static float TRANSITION_SCALE_FACTOR = 0.74f;
-    private static float TRANSITION_PIVOT = 0.65f;
     private static float TRANSITION_MAX_ROTATION = 30;
     private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
-    private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
-    private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
+
+    private KeyguardViewStateManager mViewStateManager;
+    private LockPatternUtils mLockPatternUtils;
+
+    // Related to the fading in / out background outlines
+    private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
+    private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
+    private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
+    private ObjectAnimator mChildrenOutlineFadeInAnimation;
+    private ObjectAnimator mChildrenOutlineFadeOutAnimation;
+    private float mChildrenOutlineAlpha = 0;
+    private float mSidePagesAlpha = 1f;
+
+    private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
+
+    private int mPage = 0;
+    private Callbacks mCallbacks;
+
+    private boolean mCameraWidgetEnabled;
 
     public KeyguardWidgetPager(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -52,28 +75,180 @@
         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
         }
+
+        setPageSwitchListener(this);
+
+        Resources r = getResources();
+        mCameraWidgetEnabled = r.getBoolean(R.bool.kg_enable_camera_default_widget);
+    }
+
+    public void setViewStateManager(KeyguardViewStateManager viewStateManager) {
+        mViewStateManager = viewStateManager;
+    }
+
+    public void setLockPatternUtils(LockPatternUtils l) {
+        mLockPatternUtils = l;
+    }
+
+    @Override
+    public void onPageSwitch(View newPage, int newPageIndex) {
+        boolean showingStatusWidget = false;
+        if (newPage instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) newPage;
+            if (vg.getChildAt(0) instanceof KeyguardStatusView) {
+                showingStatusWidget = true;
+            }
+        }
+
+        // Disable the status bar clock if we're showing the default status widget
+        if (showingStatusWidget) {
+            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
+        } else {
+            setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
+        }
+
+        // Extend the display timeout if the user switches pages
+        if (mPage != newPageIndex) {
+            int oldPageIndex = mPage;
+            mPage = newPageIndex;
+            if (mCallbacks != null) {
+                mCallbacks.onUserActivityTimeoutChanged();
+                mCallbacks.userActivity();
+                mCallbacks.onPageSwitch(newPageIndex);
+            }
+            KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
+            if (oldWidgetPage != null) {
+                oldWidgetPage.onActive(false);
+            }
+            KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
+            if (newWidgetPage != null) {
+                newWidgetPage.onActive(true);
+            }
+        }
+        if (mViewStateManager != null) {
+            mViewStateManager.onPageSwitch(newPage, newPageIndex);
+        }
+    }
+
+    public void showPagingFeedback() {
+        // Nothing yet.
+    }
+
+    public long getUserActivityTimeout() {
+        View page = getPageAt(mPage);
+        if (page instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) page;
+            View view = vg.getChildAt(0);
+            if (!(view instanceof KeyguardStatusView)
+                    && !(view instanceof KeyguardMultiUserSelectorView)) {
+                return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
+            }
+        }
+        return -1;
+    }
+
+    public void setCallbacks(Callbacks callbacks) {
+        mCallbacks = callbacks;
+    }
+
+    public interface Callbacks {
+        public void userActivity();
+        public void onUserActivityTimeoutChanged();
+        public void onPageSwitch(int newPageIndex);
+    }
+
+    public void addWidget(View widget) {
+        addWidget(widget, -1);
+    }
+
+
+    public void onRemoveView(View v) {
+        int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+        mLockPatternUtils.removeAppWidget(appWidgetId);
+    }
+
+    public void onAddView(View v, int index) {
+        int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+        getVisiblePages(mTempVisiblePagesRange);
+        boundByReorderablePages(true, mTempVisiblePagesRange);
+        // Subtract from the index to take into account pages before the reorderable
+        // pages (e.g. the "add widget" page)
+        mLockPatternUtils.addAppWidget(appWidgetId, index - mTempVisiblePagesRange[0]);
     }
 
     /*
-     * We wrap widgets in a special frame which handles drawing the overscroll foreground.
+     * We wrap widgets in a special frame which handles drawing the over scroll foreground.
      */
-    public void addWidget(AppWidgetHostView widget) {
-        KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
-        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT);
-        lp.gravity = Gravity.CENTER;
-        // The framework adds a default padding to AppWidgetHostView. We don't need this padding
-        // for the Keyguard, so we override it to be 0.
-        widget.setPadding(0,  0, 0, 0);
-        widget.setContentDescription(widget.getAppWidgetInfo().label);
-        frame.addView(widget, lp);
-        addView(frame);
+    public void addWidget(View widget, int pageIndex) {
+        KeyguardWidgetFrame frame;
+        // All views contained herein should be wrapped in a KeyguardWidgetFrame
+        if (!(widget instanceof KeyguardWidgetFrame)) {
+            frame = new KeyguardWidgetFrame(getContext());
+            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.MATCH_PARENT);
+            lp.gravity = Gravity.TOP;
+            // The framework adds a default padding to AppWidgetHostView. We don't need this padding
+            // for the Keyguard, so we override it to be 0.
+            widget.setPadding(0,  0, 0, 0);
+            if (widget instanceof AppWidgetHostView) {
+                AppWidgetHostView awhv = (AppWidgetHostView) widget;
+                widget.setContentDescription(awhv.getAppWidgetInfo().label);
+            }
+            frame.addView(widget, lp);
+        } else {
+            frame = (KeyguardWidgetFrame) widget;
+        }
+
+        ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        frame.setOnLongClickListener(this);
+
+        if (pageIndex == -1) {
+            addView(frame, pageLp);
+        } else {
+            addView(frame, pageIndex, pageLp);
+        }
+    }
+
+    // We enforce that all children are KeyguardWidgetFrames
+    @Override
+    public void addView(View child, int index) {
+        enforceKeyguardWidgetFrame(child);
+        super.addView(child, index);
+    }
+
+    @Override
+    public void addView(View child, int width, int height) {
+        enforceKeyguardWidgetFrame(child);
+        super.addView(child, width, height);
+    }
+
+    @Override
+    public void addView(View child, LayoutParams params) {
+        enforceKeyguardWidgetFrame(child);
+        super.addView(child, params);
+    }
+
+    @Override
+    public void addView(View child, int index, LayoutParams params) {
+        enforceKeyguardWidgetFrame(child);
+        super.addView(child, index, params);
+    }
+
+    private void enforceKeyguardWidgetFrame(View child) {
+        if (!(child instanceof KeyguardWidgetFrame)) {
+            throw new IllegalArgumentException(
+                    "KeyguardWidgetPager children must be KeyguardWidgetFrames");
+        }
+    }
+
+    public KeyguardWidgetFrame getWidgetPageAt(int index) {
+        // This is always a valid cast as we've guarded the ability to
+        return (KeyguardWidgetFrame) getChildAt(index);
     }
 
     protected void onUnhandledTap(MotionEvent ev) {
-        if (getParent() instanceof KeyguardWidgetRegion) {
-            ((KeyguardWidgetRegion) getParent()).showPagingFeedback();
-        }
+        showPagingFeedback();
     }
 
     @Override
@@ -82,8 +257,13 @@
         // TODO: We should only do this for the two views that are actually moving
         int children = getChildCount();
         for (int i = 0; i < children; i++) {
-            getChildAt(i).setLayerType(LAYER_TYPE_HARDWARE, null);
+            getWidgetPageAt(i).enableHardwareLayersForContent();
         }
+
+        if (mViewStateManager != null) {
+            mViewStateManager.onPageBeginMoving();
+        }
+        showOutlinesAndSidePages();
     }
 
     @Override
@@ -91,8 +271,13 @@
         // Disable hardware layers while pages are moving
         int children = getChildCount();
         for (int i = 0; i < children; i++) {
-            getChildAt(i).setLayerType(LAYER_TYPE_NONE, null);
+            getWidgetPageAt(i).disableHardwareLayersForContent();
         }
+
+        if (mViewStateManager != null) {
+            mViewStateManager.onPageEndMoving();
+        }
+        hideOutlinesAndSidePages();
     }
 
     /*
@@ -118,7 +303,7 @@
     public String getCurrentPageDescription() {
         final int nextPageIndex = getNextPage();
         if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) {
-            KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(nextPageIndex);
+            KeyguardWidgetFrame frame = getWidgetPageAt(nextPageIndex);
             CharSequence title = frame.getChildAt(0).getContentDescription();
             if (title == null) {
                 title = "";
@@ -135,74 +320,59 @@
         acceleratedOverScroll(amount);
     }
 
-    // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
+    float backgroundAlphaInterpolator(float r) {
+        return Math.min(1f, r);
+    }
+
+    private void updatePageAlphaValues(int screenCenter) {
+        boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
+        if (!isInOverscroll) {
+            for (int i = 0; i < getChildCount(); i++) {
+                KeyguardWidgetFrame child = getWidgetPageAt(i);
+                if (child != null) {
+                    float scrollProgress = getScrollProgress(screenCenter, child, i);
+                    if (!isReordering(false)) {
+                        child.setBackgroundAlphaMultiplier(
+                                backgroundAlphaInterpolator(Math.abs(scrollProgress)));
+                    } else {
+                        child.setBackgroundAlphaMultiplier(1f);
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     protected void screenScrolled(int screenCenter) {
-        super.screenScrolled(screenCenter);
-
+        updatePageAlphaValues(screenCenter);
         for (int i = 0; i < getChildCount(); i++) {
-            View v = getPageAt(i);
+            KeyguardWidgetFrame v = getWidgetPageAt(i);
+            if (v == mDragView) continue;
             if (v != null) {
                 float scrollProgress = getScrollProgress(screenCenter, v, i);
 
-                float interpolatedProgress =
-                        mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0)));
-                float scale = (1 - interpolatedProgress) +
-                        interpolatedProgress * TRANSITION_SCALE_FACTOR;
-                float translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth();
-
-                float alpha;
-
-                if (scrollProgress < 0) {
-                    alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
-                        1 - Math.abs(scrollProgress)) : 1.0f;
-                } else {
-                    // On large screens we need to fade the page as it nears its leftmost position
-                    alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
-                }
+                float alpha = 1.0f;
 
                 v.setCameraDistance(mDensity * CAMERA_DISTANCE);
-                int pageWidth = v.getMeasuredWidth();
-                int pageHeight = v.getMeasuredHeight();
 
                 if (PERFORM_OVERSCROLL_ROTATION) {
                     if (i == 0 && scrollProgress < 0) {
-                        // Overscroll to the left
-                        v.setPivotX(TRANSITION_PIVOT * pageWidth);
+                        // Over scroll to the left
                         v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
-                        if (v instanceof KeyguardWidgetFrame) {
-                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
-                                    true);
-                        }
-                        scale = 1.0f;
+                        v.setOverScrollAmount(Math.abs(scrollProgress), true);
                         alpha = 1.0f;
                         // On the first page, we don't want the page to have any lateral motion
-                        translationX = 0;
                     } else if (i == getChildCount() - 1 && scrollProgress > 0) {
-                        // Overscroll to the right
-                        v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
+                        // Over scroll to the right
                         v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
-                        scale = 1.0f;
                         alpha = 1.0f;
-                        if (v instanceof KeyguardWidgetFrame) {
-                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
-                                    false);
-                        }
+                        v.setOverScrollAmount(Math.abs(scrollProgress), false);
                         // On the last page, we don't want the page to have any lateral motion.
-                        translationX = 0;
                     } else {
-                        v.setPivotY(pageHeight / 2.0f);
-                        v.setPivotX(pageWidth / 2.0f);
                         v.setRotationY(0f);
-                        if (v instanceof KeyguardWidgetFrame) {
-                            ((KeyguardWidgetFrame) v).setOverScrollAmount(0, false);
-                        }
+                        v.setOverScrollAmount(0, false);
                     }
                 }
-
-                v.setTranslationX(translationX);
-                v.setScaleX(scale);
-                v.setScaleY(scale);
                 v.setAlpha(alpha);
 
                 // If the view has 0 alpha, we set it to be invisible so as to prevent
@@ -215,4 +385,130 @@
             }
         }
     }
+    @Override
+    void boundByReorderablePages(boolean isReordering, int[] range) {
+        if (isReordering) {
+            if (isAddWidgetPageVisible()) {
+                range[0]++;
+            }
+            if (isMusicWidgetVisible()) {
+                range[1]--;
+            }
+            if (isCameraWidgetVisible()) {
+                range[1]--;
+            }
+        }
+    }
+
+    /*
+     * Special widgets
+     */
+    boolean isAddWidgetPageVisible() {
+        // TODO: Make proper test once we decide whether the add-page is always showing
+        return true;
+    }
+    boolean isMusicWidgetVisible() {
+        // TODO: Make proper test once we have music in the list
+        return false;
+    }
+    boolean isCameraWidgetVisible() {
+        return mCameraWidgetEnabled;
+    }
+
+    @Override
+    protected void onStartReordering() {
+        super.onStartReordering();
+        setChildrenOutlineMultiplier(1.0f);
+        showOutlinesAndSidePages();
+    }
+
+    @Override
+    protected void onEndReordering() {
+        super.onEndReordering();
+        hideOutlinesAndSidePages();
+    }
+
+    void showOutlinesAndSidePages() {
+        if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
+        if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
+
+        PropertyValuesHolder outlinesAlpha =
+                PropertyValuesHolder.ofFloat("childrenOutlineAlpha", 1.0f);
+        PropertyValuesHolder sidePagesAlpha = PropertyValuesHolder.ofFloat("sidePagesAlpha", 1.0f);
+        mChildrenOutlineFadeInAnimation =
+                ObjectAnimator.ofPropertyValuesHolder(this, outlinesAlpha, sidePagesAlpha);
+
+        mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
+        mChildrenOutlineFadeInAnimation.start();
+    }
+
+    public void showInitialPageHints() {
+        // We start with everything showing
+        setChildrenOutlineAlpha(1.0f);
+        setSidePagesAlpha(1.0f);
+        setChildrenOutlineMultiplier(1.0f);
+
+        int currPage = getCurrentPage();
+        KeyguardWidgetFrame frame = getWidgetPageAt(currPage);
+        frame.setBackgroundAlphaMultiplier(0f);
+    }
+
+    void hideOutlinesAndSidePages() {
+        if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
+        if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
+
+        PropertyValuesHolder outlinesAlpha =
+                PropertyValuesHolder.ofFloat("childrenOutlineAlpha", 0f);
+        PropertyValuesHolder sidePagesAlpha = PropertyValuesHolder.ofFloat("sidePagesAlpha", 0f);
+        mChildrenOutlineFadeOutAnimation =
+                ObjectAnimator.ofPropertyValuesHolder(this, outlinesAlpha, sidePagesAlpha);
+
+        mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
+        mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
+        mChildrenOutlineFadeOutAnimation.start();
+    }
+
+    public void setChildrenOutlineAlpha(float alpha) {
+        mChildrenOutlineAlpha = alpha;
+        for (int i = 0; i < getChildCount(); i++) {
+            getWidgetPageAt(i).setBackgroundAlpha(alpha);
+        }
+    }
+
+    public void setSidePagesAlpha(float alpha) {
+        // This gives the current page, or the destination page if in transit.
+        int curPage = getNextPage();
+        mSidePagesAlpha = alpha;
+        for (int i = 0; i < getChildCount(); i++) {
+            if (curPage != i) {
+                getWidgetPageAt(i).setContentAlpha(alpha);
+            } else {
+                // We lock the current page alpha to 1.
+                getWidgetPageAt(i).setContentAlpha(1.0f);
+            }
+        }
+    }
+
+    public void setChildrenOutlineMultiplier(float alpha) {
+        mChildrenOutlineAlpha = alpha;
+        for (int i = 0; i < getChildCount(); i++) {
+            getWidgetPageAt(i).setBackgroundAlphaMultiplier(alpha);
+        }
+    }
+
+    public float getSidePagesAlpha() {
+        return mSidePagesAlpha;
+    }
+
+    public float getChildrenOutlineAlpha() {
+        return mChildrenOutlineAlpha;
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (startReordering()) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
deleted file mode 100644
index 4ff6f27..0000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import com.android.internal.R;
-public class KeyguardWidgetRegion extends LinearLayout implements PagedView.PageSwitchListener {
-    KeyguardGlowStripView mLeftStrip;
-    KeyguardGlowStripView mRightStrip;
-    KeyguardWidgetPager mPager;
-    private int mPage = 0;
-    private Callbacks mCallbacks;
-
-    // We are disabling touch interaction of the widget region for factory ROM. 
-    private static final boolean DISABLE_TOUCH_INTERACTION = true;
-
-    private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
-
-    public KeyguardWidgetRegion(Context context) {
-        this(context, null, 0);
-    }
-
-    public KeyguardWidgetRegion(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public KeyguardWidgetRegion(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mLeftStrip = (KeyguardGlowStripView) findViewById(R.id.left_strip);
-        mRightStrip = (KeyguardGlowStripView) findViewById(R.id.right_strip);
-        mPager = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
-        mPager.setPageSwitchListener(this);
-
-        setSoundEffectsEnabled(false);
-        if (!DISABLE_TOUCH_INTERACTION) {
-            setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    showPagingFeedback();
-                }
-            });
-        }
-    }
-
-    public void showPagingFeedback() {
-        if ((mPage < mPager.getPageCount() - 1)) {
-            mLeftStrip.makeEmGo();
-        }
-        if ((mPage > 0)) {
-            mRightStrip.makeEmGo();
-        }
-    }
-
-    @Override
-    public void onPageSwitch(View newPage, int newPageIndex) {
-        boolean showingStatusWidget = false;
-        if (newPage instanceof ViewGroup) {
-            ViewGroup vg = (ViewGroup) newPage;
-            if (vg.getChildAt(0) instanceof KeyguardStatusView) {
-                showingStatusWidget = true;
-            }
-        }
-
-        // Disable the status bar clock if we're showing the default status widget
-        if (showingStatusWidget) {
-            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
-        } else {
-            setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
-        }
-
-        // Extend the display timeout if the user switches pages
-        if (mPage != newPageIndex) {
-            mPage = newPageIndex;
-            if (mCallbacks != null) {
-                mCallbacks.onUserActivityTimeoutChanged();
-                mCallbacks.userActivity();
-            }
-        }
-    }
-
-    public long getUserActivityTimeout() {
-        View page = mPager.getPageAt(mPage);
-        if (page instanceof ViewGroup) {
-            ViewGroup vg = (ViewGroup) page;
-            View view = vg.getChildAt(0);
-            if (!(view instanceof KeyguardStatusView)
-                    && !(view instanceof KeyguardMultiUserSelectorView)) {
-                return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
-            }
-        }
-        return -1;
-    }
-
-    public void setCallbacks(Callbacks callbacks) {
-        mCallbacks = callbacks;
-    }
-
-    public interface Callbacks {
-        public void userActivity();
-        public void onUserActivityTimeoutChanged();
-    }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
new file mode 100644
index 0000000..3ccc7ea
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayout {
+    private static final String TAG = "MultiPaneChallengeLayout";
+
+    final int mOrientation;
+    private boolean mIsBouncing;
+
+    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
+    public static final int VERTICAL = LinearLayout.VERTICAL;
+
+    private View mChallengeView;
+    private View mUserSwitcherView;
+    private View mScrimView;
+    private OnBouncerStateChangedListener mBouncerListener;
+
+    private final Rect mTempRect = new Rect();
+
+    private final OnClickListener mScrimClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            hideBouncer();
+        }
+    };
+
+    public MultiPaneChallengeLayout(Context context) {
+        this(context, null);
+    }
+
+    public MultiPaneChallengeLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public MultiPaneChallengeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0);
+        mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_orientation,
+                HORIZONTAL);
+        a.recycle();
+    }
+
+    @Override
+    public boolean isChallengeShowing() {
+        return true;
+    }
+
+    @Override
+    public boolean isChallengeOverlapping() {
+        return false;
+    }
+
+    @Override
+    public void showChallenge(boolean b) {
+    }
+
+    @Override
+    public void showBouncer() {
+        if (mIsBouncing) return;
+        mIsBouncing = true;
+        if (mScrimView != null) {
+            mScrimView.setVisibility(GONE);
+        }
+        if (mBouncerListener != null) {
+            mBouncerListener.onBouncerStateChanged(true);
+        }
+    }
+
+    @Override
+    public void hideBouncer() {
+        if (!mIsBouncing) return;
+        mIsBouncing = false;
+        if (mScrimView != null) {
+            mScrimView.setVisibility(GONE);
+        }
+        if (mBouncerListener != null) {
+            mBouncerListener.onBouncerStateChanged(false);
+        }
+    }
+
+    @Override
+    public boolean isBouncing() {
+        return mIsBouncing;
+    }
+
+    @Override
+    public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
+        mBouncerListener = listener;
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        if (mIsBouncing && child != mChallengeView) {
+            // Clear out of the bouncer if the user tries to move focus outside of
+            // the security challenge view.
+            hideBouncer();
+        }
+        super.requestChildFocus(child, focused);
+    }
+
+    void setScrimView(View scrim) {
+        if (mScrimView != null) {
+            mScrimView.setOnClickListener(null);
+        }
+        mScrimView = scrim;
+        mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
+        mScrimView.setFocusable(true);
+        mScrimView.setOnClickListener(mScrimClickListener);
+    }
+
+    @Override
+    protected void onMeasure(int widthSpec, int heightSpec) {
+        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
+                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
+            throw new IllegalArgumentException(
+                    "MultiPaneChallengeLayout must be measured with an exact size");
+        }
+
+        final int width = MeasureSpec.getSize(widthSpec);
+        final int height = MeasureSpec.getSize(heightSpec);
+        setMeasuredDimension(width, height);
+
+        int widthUsed = 0;
+        int heightUsed = 0;
+
+        // First pass. Find the challenge view and measure the user switcher,
+        // which consumes space in the layout.
+        mChallengeView = null;
+        mUserSwitcherView = null;
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+                if (mChallengeView != null) {
+                    throw new IllegalStateException(
+                            "There may only be one child of type challenge");
+                }
+                mChallengeView = child;
+            } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
+                if (mUserSwitcherView != null) {
+                    throw new IllegalStateException(
+                            "There may only be one child of type userSwitcher");
+                }
+                mUserSwitcherView = child;
+
+                if (child.getVisibility() == GONE) continue;
+
+                int adjustedWidthSpec = widthSpec;
+                int adjustedHeightSpec = heightSpec;
+                if (lp.maxWidth >= 0) {
+                    adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+                            Math.min(lp.maxWidth, MeasureSpec.getSize(widthSpec)),
+                            MeasureSpec.EXACTLY);
+                }
+                if (lp.maxHeight >= 0) {
+                    adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+                            Math.min(lp.maxHeight, MeasureSpec.getSize(heightSpec)),
+                            MeasureSpec.EXACTLY);
+                }
+                // measureChildWithMargins will resolve layout direction for the LayoutParams
+                measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
+
+                // Only subtract out space from one dimension. Favor vertical.
+                // Offset by 1.5x to add some balance along the other edge.
+                if (Gravity.isVertical(lp.gravity)) {
+                    heightUsed += child.getMeasuredHeight() * 1.5f;
+                } else if (Gravity.isHorizontal(lp.gravity)) {
+                    widthUsed += child.getMeasuredWidth() * 1.5f;
+                }
+            } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
+                setScrimView(child);
+                child.measure(widthSpec, heightSpec);
+            }
+        }
+
+        // Second pass. Measure everything that's left.
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER ||
+                    lp.childType == LayoutParams.CHILD_TYPE_SCRIM ||
+                    child.getVisibility() == GONE) {
+                // Don't need to measure GONE children, and the user switcher was already measured.
+                continue;
+            }
+
+            int adjustedWidthSpec;
+            int adjustedHeightSpec;
+            if (lp.centerWithinArea > 0) {
+                if (mOrientation == HORIZONTAL) {
+                    adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+                            (int) ((width - widthUsed) * lp.centerWithinArea + 0.5f),
+                            MeasureSpec.EXACTLY);
+                    adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+                            MeasureSpec.getSize(heightSpec) - heightUsed, MeasureSpec.EXACTLY);
+                } else {
+                    adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+                            MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+                    adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+                            (int) ((height - heightUsed) * lp.centerWithinArea + 0.5f),
+                            MeasureSpec.EXACTLY);
+                }
+            } else {
+                adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+                        MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+                adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+                        MeasureSpec.getSize(heightSpec) - heightUsed, MeasureSpec.EXACTLY);
+            }
+            if (lp.maxWidth >= 0) {
+                adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+                        Math.min(lp.maxWidth, MeasureSpec.getSize(adjustedWidthSpec)),
+                        MeasureSpec.EXACTLY);
+            }
+            if (lp.maxHeight >= 0) {
+                adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+                        Math.min(lp.maxHeight, MeasureSpec.getSize(adjustedHeightSpec)),
+                        MeasureSpec.EXACTLY);
+            }
+
+            measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        final Rect padding = mTempRect;
+        padding.left = getPaddingLeft();
+        padding.top = getPaddingTop();
+        padding.right = getPaddingRight();
+        padding.bottom = getPaddingBottom();
+        final int width = r - l;
+        final int height = b - t;
+
+        // Reserve extra space in layout for the user switcher by modifying
+        // local padding during this layout pass
+        if (mUserSwitcherView != null && mUserSwitcherView.getVisibility() != GONE) {
+            layoutWithGravity(width, height, mUserSwitcherView, padding, true);
+        }
+
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+
+            // We did the user switcher above if we have one.
+            if (child == mUserSwitcherView || child.getVisibility() == GONE) continue;
+
+            if (child == mScrimView) {
+                child.layout(0, 0, width, height);
+                continue;
+            }
+
+            layoutWithGravity(width, height, child, padding, false);
+        }
+    }
+
+    private void layoutWithGravity(int width, int height, View child, Rect padding,
+            boolean adjustPadding) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+        final int gravity = Gravity.getAbsoluteGravity(lp.gravity, getLayoutDirection());
+
+        final boolean fixedLayoutSize = lp.centerWithinArea > 0;
+        final boolean fixedLayoutHorizontal = fixedLayoutSize && mOrientation == HORIZONTAL;
+        final boolean fixedLayoutVertical = fixedLayoutSize && mOrientation == VERTICAL;
+
+        final int adjustedWidth;
+        final int adjustedHeight;
+        if (fixedLayoutHorizontal) {
+            final int paddedWidth = width - padding.left - padding.right;
+            adjustedWidth = (int) (paddedWidth * lp.centerWithinArea + 0.5f);
+            adjustedHeight = height;
+        } else if (fixedLayoutVertical) {
+            final int paddedHeight = height - padding.top - padding.bottom;
+            adjustedWidth = width;
+            adjustedHeight = (int) (paddedHeight * lp.centerWithinArea + 0.5f);
+        } else {
+            adjustedWidth = width;
+            adjustedHeight = height;
+        }
+
+        final boolean isVertical = Gravity.isVertical(gravity);
+        final boolean isHorizontal = Gravity.isHorizontal(gravity);
+        final int childWidth = child.getMeasuredWidth();
+        final int childHeight = child.getMeasuredHeight();
+
+        int left = padding.left;
+        int top = padding.top;
+        int right = left + childWidth;
+        int bottom = top + childHeight;
+        switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+            case Gravity.TOP:
+                top = fixedLayoutVertical ?
+                        padding.top + (adjustedHeight - childHeight) / 2 : padding.top;
+                bottom = top + childHeight;
+                if (adjustPadding && isVertical) {
+                    padding.top = bottom;
+                    padding.bottom += childHeight / 2;
+                }
+                break;
+            case Gravity.BOTTOM:
+                bottom = fixedLayoutVertical
+                        ? height - padding.bottom - (adjustedHeight - childHeight) / 2
+                        : height - padding.bottom;
+                top = bottom - childHeight;
+                if (adjustPadding && isVertical) {
+                    padding.bottom = height - top;
+                    padding.top += childHeight / 2;
+                }
+                break;
+            case Gravity.CENTER_VERTICAL:
+                final int paddedHeight = height - padding.top - padding.bottom;
+                top = padding.top + (paddedHeight - childHeight) / 2;
+                bottom = top + childHeight;
+                break;
+        }
+        switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+            case Gravity.LEFT:
+                left = fixedLayoutHorizontal ?
+                        padding.left + (adjustedWidth - childWidth) / 2 : padding.left;
+                right = left + childWidth;
+                if (adjustPadding && isHorizontal && !isVertical) {
+                    padding.left = right;
+                    padding.right += childWidth / 2;
+                }
+                break;
+            case Gravity.RIGHT:
+                right = fixedLayoutHorizontal
+                        ? width - padding.right - (adjustedWidth - childWidth) / 2
+                        : width - padding.right;
+                left = right - childWidth;
+                if (adjustPadding && isHorizontal && !isVertical) {
+                    padding.right = width - left;
+                    padding.left += childWidth / 2;
+                }
+                break;
+            case Gravity.CENTER_HORIZONTAL:
+                final int paddedWidth = width - padding.left - padding.right;
+                left = (paddedWidth - childWidth) / 2;
+                right = left + childWidth;
+                break;
+        }
+        child.layout(left, top, right, bottom);
+    }
+
+    @Override
+    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs, this);
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
+                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
+                new LayoutParams(p);
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams();
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams;
+    }
+
+    public static class LayoutParams extends MarginLayoutParams {
+
+        public float centerWithinArea = 0;
+
+        public int childType = 0;
+
+        public static final int CHILD_TYPE_NONE = 0;
+        public static final int CHILD_TYPE_WIDGET = 1;
+        public static final int CHILD_TYPE_CHALLENGE = 2;
+        public static final int CHILD_TYPE_USER_SWITCHER = 3;
+        public static final int CHILD_TYPE_SCRIM = 4;
+
+        public int gravity = Gravity.NO_GRAVITY;
+
+        public int maxWidth = -1;
+        public int maxHeight = -1;
+
+        public LayoutParams() {
+            this(WRAP_CONTENT, WRAP_CONTENT);
+        }
+
+        LayoutParams(Context c, AttributeSet attrs, MultiPaneChallengeLayout parent) {
+            super(c, attrs);
+
+            final TypedArray a = c.obtainStyledAttributes(attrs,
+                    R.styleable.MultiPaneChallengeLayout_Layout);
+
+            centerWithinArea = a.getFloat(
+                    R.styleable.MultiPaneChallengeLayout_Layout_layout_centerWithinArea, 0);
+            childType = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_childType,
+                    CHILD_TYPE_NONE);
+            gravity = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_gravity,
+                    Gravity.NO_GRAVITY);
+            maxWidth = a.getDimensionPixelSize(
+                    R.styleable.MultiPaneChallengeLayout_Layout_layout_maxWidth, -1);
+            maxHeight = a.getDimensionPixelSize(
+                    R.styleable.MultiPaneChallengeLayout_Layout_layout_maxHeight, -1);
+
+            // Default gravity settings based on type and parent orientation
+            if (gravity == Gravity.NO_GRAVITY) {
+                if (parent.mOrientation == HORIZONTAL) {
+                    switch (childType) {
+                        case CHILD_TYPE_WIDGET:
+                            gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
+                            break;
+                        case CHILD_TYPE_CHALLENGE:
+                            gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+                            break;
+                        case CHILD_TYPE_USER_SWITCHER:
+                            gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+                            break;
+                    }
+                } else {
+                    switch (childType) {
+                        case CHILD_TYPE_WIDGET:
+                            gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+                            break;
+                        case CHILD_TYPE_CHALLENGE:
+                            gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+                            break;
+                        case CHILD_TYPE_USER_SWITCHER:
+                            gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+                            break;
+                    }
+                }
+            }
+
+            a.recycle();
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(MarginLayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(LayoutParams source) {
+            this((MarginLayoutParams) source);
+
+            centerWithinArea = source.centerWithinArea;
+            childType = source.childType;
+            gravity = source.gravity;
+            maxWidth = source.maxWidth;
+            maxHeight = source.maxHeight;
+        }
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
new file mode 100644
index 0000000..060cc03
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.SpannableStringBuilder;
+import android.text.style.TextAppearanceSpan;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+public class NumPadKey extends Button {
+    // list of "ABC", etc per digit, starting with '0'
+    static String sKlondike[];
+
+    int mDigit = -1;
+    int mTextViewResId;
+    TextView mTextView = null;
+    boolean mEnableHaptics;
+
+    private View.OnClickListener mListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View thisView) {
+            if (mTextView == null) {
+                if (mTextViewResId > 0) {
+                    final View v = NumPadKey.this.getRootView().findViewById(mTextViewResId);
+                    if (v != null && v instanceof TextView) {
+                        mTextView = (TextView) v;
+                    }
+                }
+            }
+            if (mTextView != null) {
+                mTextView.append(String.valueOf(mDigit));
+            }
+            doHapticKeyClick();
+        }
+    };
+
+    public NumPadKey(Context context) {
+        this(context, null);
+    }
+
+    public NumPadKey(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey);
+        mDigit = a.getInt(R.styleable.NumPadKey_digit, mDigit);
+        setTextViewResId(a.getResourceId(R.styleable.NumPadKey_textView, 0));
+
+        setOnClickListener(mListener);
+
+        mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled();
+
+        SpannableStringBuilder builder = new SpannableStringBuilder();
+        builder.append(String.valueOf(mDigit));
+        if (mDigit >= 0) {
+            if (sKlondike == null) {
+                sKlondike = context.getResources().getStringArray(
+                        R.array.lockscreen_num_pad_klondike);
+            }
+            if (sKlondike != null && sKlondike.length > mDigit) {
+                final String extra = sKlondike[mDigit];
+                final int extraLen = extra.length();
+                if (extraLen > 0) {
+                    builder.append(extra);
+                    builder.setSpan(
+                        new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike),
+                        builder.length()-extraLen, builder.length(), 0);
+                }
+            }
+        }
+        setText(builder);
+    }
+
+    public void setTextView(TextView tv) {
+        mTextView = tv;
+    }
+
+    public void setTextViewResId(int resId) {
+        mTextView = null;
+        mTextViewResId = resId;
+    }
+
+    // Cause a VIRTUAL_KEY vibration
+    public void doHapticKeyClick() {
+        if (mEnableHaptics) {
+            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+                    | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+        }
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 86c05b1..3562071 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -18,12 +18,16 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -41,6 +45,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.Scroller;
 
@@ -52,7 +58,7 @@
  * An abstraction of the original Workspace which supports browsing through a
  * sequential list of "pages"
  */
-public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
     private static final String TAG = "WidgetPagedView";
     private static final boolean DEBUG = false;
     protected static final int INVALID_PAGE = -1;
@@ -60,7 +66,7 @@
     // the min drag distance for a fling to register, to prevent random page shifts
     private static final int MIN_LENGTH_FOR_FLING = 25;
 
-    protected static final int PAGE_SNAP_ANIMATION_DURATION = 550;
+    protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
     protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
     protected static final float NANOTIME_DIV = 1000000000.0f;
 
@@ -78,7 +84,9 @@
     private static final int MIN_FLING_VELOCITY = 250;
 
     // We are disabling touch interaction of the widget region for factory ROM. 
-    private static final boolean DISABLE_TOUCH_INTERACTION = true;
+    private static final boolean DISABLE_TOUCH_INTERACTION = false;
+    private static final boolean DISABLE_TOUCH_SIDE_PAGES = true;
+    private static final boolean DISABLE_FLING_TO_DELETE = false;
 
     static final int AUTOMATIC_PAGE_SPACING = -1;
 
@@ -100,7 +108,11 @@
     protected Scroller mScroller;
     private VelocityTracker mVelocityTracker;
 
+    private float mParentDownMotionX;
+    private float mParentDownMotionY;
     private float mDownMotionX;
+    private float mDownMotionY;
+    private float mDownScrollX;
     protected float mLastMotionX;
     protected float mLastMotionXRemainder;
     protected float mLastMotionY;
@@ -114,6 +126,8 @@
     protected final static int TOUCH_STATE_SCROLLING = 1;
     protected final static int TOUCH_STATE_PREV_PAGE = 2;
     protected final static int TOUCH_STATE_NEXT_PAGE = 3;
+    protected final static int TOUCH_STATE_REORDERING = 4;
+
     protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
 
     protected int mTouchState = TOUCH_STATE_REST;
@@ -121,22 +135,13 @@
 
     protected OnLongClickListener mLongClickListener;
 
-    protected boolean mAllowLongPress = true;
-
     protected int mTouchSlop;
     private int mPagingTouchSlop;
     private int mMaximumVelocity;
     private int mMinimumWidth;
     protected int mPageSpacing;
-    protected int mPageLayoutPaddingTop;
-    protected int mPageLayoutPaddingBottom;
-    protected int mPageLayoutPaddingLeft;
-    protected int mPageLayoutPaddingRight;
-    protected int mPageLayoutWidthGap;
-    protected int mPageLayoutHeightGap;
     protected int mCellCountX = 0;
     protected int mCellCountY = 0;
-    protected boolean mCenterPagesVertically;
     protected boolean mAllowOverScroll = true;
     protected int mUnboundedScrollX;
     protected int[] mTempVisiblePagesRange = new int[2];
@@ -162,7 +167,7 @@
     protected boolean mContentIsRefreshable = true;
 
     // If true, modify alpha of neighboring pages as user scrolls left/right
-    protected boolean mFadeInAdjacentScreens = true;
+    protected boolean mFadeInAdjacentScreens = false;
 
     // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
     // to switch to a new page
@@ -188,6 +193,47 @@
     protected static final int sScrollIndicatorFadeOutDuration = 650;
     protected static final int sScrollIndicatorFlashDuration = 650;
 
+    // The viewport whether the pages are to be contained (the actual view may be larger than the
+    // viewport)
+    private Rect mViewport = new Rect();
+
+    // Reordering
+    // We use the min scale to determine how much to expand the actually PagedView measured
+    // dimensions such that when we are zoomed out, the view is not clipped
+    private int REORDERING_DROP_REPOSITION_DURATION = 200;
+    private int REORDERING_REORDER_REPOSITION_DURATION = 350;
+    private int REORDERING_ZOOM_IN_OUT_DURATION = 250;
+    private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 500;
+    private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f;
+    private float mMinScale = 1f;
+    protected View mDragView;
+    private AnimatorSet mZoomInOutAnim;
+    private Runnable mSidePageHoverRunnable;
+    private int mSidePageHoverIndex = -1;
+    // This variable's scope is only for the duration of startReordering() and endReordering()
+    private boolean mReorderingStarted = false;
+    // This variable's scope is for the duration of startReordering() and after the zoomIn()
+    // animation after endReordering()
+    private boolean mIsReordering;
+
+    // Edge swiping
+    private boolean mOnlyAllowEdgeSwipes = false;
+    private boolean mDownEventOnEdge = false;
+    private int mEdgeSwipeRegionSize = 0;
+
+    // Convenience/caching
+    private Matrix mTmpInvMatrix = new Matrix();
+    private float[] mTmpPoint = new float[2];
+
+    // Fling to delete
+    private int FLING_TO_DELETE_FADE_OUT_DURATION = 350;
+    private float FLING_TO_DELETE_FRICTION = 0.035f;
+    // The degrees specifies how much deviation from the up vector to still consider a fling "up"
+    private float FLING_TO_DELETE_MAX_FLING_DEGREES = 35f;
+    private int FLING_TO_DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250;
+    protected int mFlingToDeleteThresholdVelocity = -1400;
+    private boolean mIsFlingingToDelete = false;
+
     public interface PageSwitchListener {
         void onPageSwitch(View newPage, int newPageIndex);
     }
@@ -205,24 +251,15 @@
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.PagedView, defStyle, 0);
         setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
-        mPageLayoutPaddingTop = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutPaddingTop, 0);
-        mPageLayoutPaddingBottom = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutPaddingBottom, 0);
-        mPageLayoutPaddingLeft = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutPaddingLeft, 0);
-        mPageLayoutPaddingRight = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutPaddingRight, 0);
-        mPageLayoutWidthGap = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutWidthGap, 0);
-        mPageLayoutHeightGap = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutHeightGap, 0);
         mScrollIndicatorPaddingLeft =
             a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
         mScrollIndicatorPaddingRight =
-            a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
+                a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
         a.recycle();
 
+        mEdgeSwipeRegionSize =
+                getResources().getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
+
         setHapticFeedbackEnabled(false);
         init();
     }
@@ -235,7 +272,6 @@
         mDirtyPageContent.ensureCapacity(32);
         mScroller = new Scroller(getContext(), new ScrollInterpolator());
         mCurrentPage = 0;
-        mCenterPagesVertically = true;
 
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
@@ -243,12 +279,76 @@
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mDensity = getResources().getDisplayMetrics().density;
 
+        // Scale the fling-to-delete threshold by the density
+        mFlingToDeleteThresholdVelocity =
+                (int) (mFlingToDeleteThresholdVelocity * mDensity);
+
         mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
         mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
         mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
         setOnHierarchyChangeListener(this);
     }
 
+    // Convenience methods to map points from self to parent and vice versa
+    float[] mapPointFromSelfToParent(float x, float y) {
+        mTmpPoint[0] = x;
+        mTmpPoint[1] = y;
+        getMatrix().mapPoints(mTmpPoint);
+        mTmpPoint[0] += getLeft();
+        mTmpPoint[1] += getTop();
+        return mTmpPoint;
+    }
+    float[] mapPointFromParentToSelf(float x, float y) {
+        mTmpPoint[0] = x - getLeft();
+        mTmpPoint[1] = y - getTop();
+        getMatrix().invert(mTmpInvMatrix);
+        mTmpInvMatrix.mapPoints(mTmpPoint);
+        return mTmpPoint;
+    }
+
+    void updateDragViewTranslationDuringDrag() {
+        float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX;
+        float y = mLastMotionY - mDownMotionY;
+        mDragView.setTranslationX(x);
+        mDragView.setTranslationY(y);
+
+        if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y);
+    }
+
+    public void setMinScale(float f) {
+        mMinScale = f;
+        requestLayout();
+    }
+
+    @Override
+    public void setScaleX(float scaleX) {
+        super.setScaleX(scaleX);
+        if (isReordering(true)) {
+            float[] p = mapPointFromParentToSelf(mParentDownMotionX, mParentDownMotionY);
+            mLastMotionX = p[0];
+            mLastMotionY = p[1];
+            updateDragViewTranslationDuringDrag();
+        }
+    }
+
+    // Convenience methods to get the actual width/height of the PagedView (since it is measured
+    // to be larger to account for the minimum possible scale)
+    int getViewportWidth() {
+        return mViewport.width();
+    }
+    int getViewportHeight() {
+        return mViewport.height();
+    }
+
+    // Convenience methods to get the offset ASSUMING that we are centering the pages in the
+    // PagedView both horizontally and vertically
+    int getViewportOffsetX() {
+        return (getMeasuredWidth() - getViewportWidth()) / 2;
+    }
+    int getViewportOffsetY() {
+        return (getMeasuredHeight() - getViewportHeight()) / 2;
+    }
+
     public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
         mPageSwitchListener = pageSwitchListener;
         if (mPageSwitchListener != null) {
@@ -328,6 +428,10 @@
         invalidate();
     }
 
+    public void setOnlyAllowEdgeSwipes(boolean enable) {
+        mOnlyAllowEdgeSwipes = enable;
+    }
+
     protected void notifyPageSwitchListener() {
         if (mPageSwitchListener != null) {
             mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
@@ -400,6 +504,14 @@
 
         mTouchX = x;
         mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+
+        // Update the last motion events when scrolling
+        if (isReordering(true)) {
+            float[] p = mapPointFromParentToSelf(mParentDownMotionX, mParentDownMotionY);
+            mLastMotionX = p[0];
+            mLastMotionY = p[1];
+            updateDragViewTranslationDuringDrag();
+        }
     }
 
     // we moved this functionality to a helper function so SmoothPagedView can reuse it
@@ -454,10 +566,20 @@
             return;
         }
 
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+        // We measure the dimensions of the PagedView to be larger than the pages so that when we
+        // zoom out (and scale down), the view is still contained in the parent
+        View parent = (View) getParent();
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+        // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
+        // viewport, we can be at most one and a half screens offset once we scale down
+        int parentWidthSize = (int) (1.5f * parent.getMeasuredWidth());
+        int parentHeightSize = parent.getMeasuredHeight();
+        int scaledWidthSize = (int) (parentWidthSize / mMinScale);
+        int scaledHeightSize = (int) (parentHeightSize / mMinScale);
+        mViewport.set(0, 0, widthSize, heightSize);
 
         if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -481,13 +603,29 @@
         // The children are given the same width and height as the workspace
         // unless they were set to WRAP_CONTENT
         if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
+        if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize);
+        if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize);
+        if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding);
+        if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding);
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             // disallowing padding in paged view (just pass 0)
             final View child = getPageAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
-            int childWidthMode = MeasureSpec.EXACTLY;
-            int childHeightMode = MeasureSpec.EXACTLY;
+            int childWidthMode;
+            if (lp.width == LayoutParams.WRAP_CONTENT) {
+                childWidthMode = MeasureSpec.AT_MOST;
+            } else {
+                childWidthMode = MeasureSpec.EXACTLY;
+            }
+
+            int childHeightMode;
+            if (lp.height == LayoutParams.WRAP_CONTENT) {
+                childHeightMode = MeasureSpec.AT_MOST;
+            } else {
+                childHeightMode = MeasureSpec.EXACTLY;
+            }
 
             final int childWidthMeasureSpec =
                 MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode);
@@ -496,21 +634,20 @@
 
             child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
         }
-
-        setMeasuredDimension(widthSize, heightSize);
+        setMeasuredDimension(scaledWidthSize, scaledHeightSize);
 
         // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
         // We also wait until we set the measured dimensions before flushing the cache as well, to
         // ensure that the cache is filled with good values.
         invalidateCachedOffsets();
 
-        if (mChildCountOnLastMeasure != getChildCount()) {
+        if (mChildCountOnLastMeasure != getChildCount() && !mIsFlingingToDelete) {
             setCurrentPage(mCurrentPage);
         }
         mChildCountOnLastMeasure = getChildCount();
 
         if (childCount > 0) {
-            if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+            if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", "
                     + getChildWidth(0));
 
             // Calculate the variable page spacing if necessary
@@ -547,19 +684,21 @@
         }
 
         if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
-        final int verticalPadding = getPaddingTop() + getPaddingBottom();
         final int childCount = getChildCount();
-        int childLeft = getRelativeChildOffset(0);
 
+        int offsetX = getViewportOffsetX();
+        int offsetY = getViewportOffsetY();
+
+        // Update the viewport offsets
+        mViewport.offset(offsetX,  offsetY);
+
+        int childLeft = offsetX + getRelativeChildOffset(0);
         for (int i = 0; i < childCount; i++) {
             final View child = getPageAt(i);
+            int childTop = offsetY + getPaddingTop();
             if (child.getVisibility() != View.GONE) {
                 final int childWidth = getScaledMeasuredWidth(child);
                 final int childHeight = child.getMeasuredHeight();
-                int childTop = getPaddingTop();
-                if (mCenterPagesVertically) {
-                    childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
-                }
 
                 if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
                 child.layout(childLeft, childTop,
@@ -577,22 +716,6 @@
     }
 
     protected void screenScrolled(int screenCenter) {
-        if (isScrollingIndicatorEnabled()) {
-            updateScrollingIndicator();
-        }
-        boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
-
-        if (mFadeInAdjacentScreens && !isInOverscroll) {
-            for (int i = 0; i < getChildCount(); i++) {
-                View child = getChildAt(i);
-                if (child != null) {
-                    float scrollProgress = getScrollProgress(screenCenter, child, i);
-                    float alpha = 1 - Math.abs(scrollProgress);
-                    child.setAlpha(alpha);
-                }
-            }
-            invalidate();
-        }
     }
 
     @Override
@@ -606,7 +729,7 @@
 
     @Override
     public void onChildViewRemoved(View parent, View child) {
-        // TODO Auto-generated method stub
+        mForceScreenScrolled = true;
     }
 
     protected void invalidateCachedOffsets() {
@@ -659,7 +782,7 @@
         } else {
             final int padding = getPaddingLeft() + getPaddingRight();
             final int offset = getPaddingLeft() +
-                    (getMeasuredWidth() - padding - getChildWidth(index)) / 2;
+                    (getViewportWidth() - padding - getChildWidth(index)) / 2;
             if (mChildRelativeOffsets != null) {
                 mChildRelativeOffsets[index] = offset;
             }
@@ -676,33 +799,47 @@
         return (int) (maxWidth * mLayoutScale + 0.5f);
     }
 
+    void boundByReorderablePages(boolean isReordering, int[] range) {
+        // Do nothing
+    }
+
+    // TODO: Fix this
     protected void getVisiblePages(int[] range) {
+        range[0] = 0;
+        range[1] = getPageCount() - 1;
+
+        /*
         final int pageCount = getChildCount();
 
         if (pageCount > 0) {
-            final int screenWidth = getMeasuredWidth();
+            final int screenWidth = getViewportWidth();
             int leftScreen = 0;
             int rightScreen = 0;
+            int offsetX = getViewportOffsetX() + getScrollX();
             View currPage = getPageAt(leftScreen);
             while (leftScreen < pageCount - 1 &&
                     currPage.getX() + currPage.getWidth() -
-                    currPage.getPaddingRight() < getScrollX()) {
+                    currPage.getPaddingRight() < offsetX) {
                 leftScreen++;
                 currPage = getPageAt(leftScreen);
             }
             rightScreen = leftScreen;
             currPage = getPageAt(rightScreen + 1);
             while (rightScreen < pageCount - 1 &&
-                    currPage.getX() - currPage.getPaddingLeft() < getScrollX() + screenWidth) {
+                    currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) {
                 rightScreen++;
                 currPage = getPageAt(rightScreen + 1);
             }
-            range[0] = leftScreen;
-            range[1] = rightScreen;
+
+            // TEMP: this is a hacky way to ensure that animations to new pages are not clipped
+            // because we don't draw them while scrolling?
+            range[0] = Math.max(0, leftScreen - 1);
+            range[1] = Math.min(rightScreen + 1, getChildCount() - 1);
         } else {
             range[0] = -1;
             range[1] = -1;
         }
+        */
     }
 
     protected boolean shouldDrawChild(View child) {
@@ -711,7 +848,7 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        int halfScreenSize = getMeasuredWidth() / 2;
+        int halfScreenSize = getViewportWidth() / 2;
         // mOverScrollX is equal to getScrollX() when we're within the normal scroll range.
         // Otherwise it is equal to the scaled overscroll position.
         int screenCenter = mOverScrollX + halfScreenSize;
@@ -728,6 +865,7 @@
         final int pageCount = getChildCount();
         if (pageCount > 0) {
             getVisiblePages(mTempVisiblePagesRange);
+            boundByReorderablePages(isReordering(false), mTempVisiblePagesRange);
             final int leftScreen = mTempVisiblePagesRange[0];
             final int rightScreen = mTempVisiblePagesRange[1];
             if (leftScreen != -1 && rightScreen != -1) {
@@ -737,13 +875,20 @@
                 canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
                         getScrollY() + getBottom() - getTop());
 
-                for (int i = getChildCount() - 1; i >= 0; i--) {
+                // Draw all the children, leaving the drag view for last
+                for (int i = pageCount - 1; i >= 0; i--) {
                     final View v = getPageAt(i);
+                    if (v == mDragView) continue;
                     if (mForceDrawAllChildrenNextFrame ||
                                (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
                         drawChild(canvas, v, drawingTime);
                     }
                 }
+                // Draw the drag view on top (if there is one)
+                if (mDragView != null) {
+                    drawChild(canvas, mDragView, drawingTime);
+                }
+
                 mForceDrawAllChildrenNextFrame = false;
                 canvas.restore();
             }
@@ -836,31 +981,17 @@
     }
 
     /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        if (disallowIntercept) {
-            // We need to make sure to cancel our long press if
-            // a scrollable widget takes over touch events
-            final View currentPage = getPageAt(mCurrentPage);
-            currentPage.cancelLongPress();
-        }
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-    }
-
-    /**
      * Return true if a tap at (x, y) should trigger a flip to the previous page.
      */
     protected boolean hitsPreviousPage(float x, float y) {
-        return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+        return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
     }
 
     /**
      * Return true if a tap at (x, y) should trigger a flip to the next page.
      */
     protected boolean hitsNextPage(float x, float y) {
-        return  (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+        return  (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
     }
 
     @Override
@@ -912,12 +1043,23 @@
                 final float y = ev.getY();
                 // Remember location of down touch
                 mDownMotionX = x;
+                mDownMotionY = y;
+                mDownScrollX = getScrollX();
                 mLastMotionX = x;
                 mLastMotionY = y;
+                float[] p = mapPointFromSelfToParent(x, y);
+                mParentDownMotionX = p[0];
+                mParentDownMotionY = p[1];
                 mLastMotionXRemainder = 0;
                 mTotalMotionX = 0;
                 mActivePointerId = ev.getPointerId(0);
-                mAllowLongPress = true;
+
+                // Determine if the down event is within the threshold to be an edge swipe
+                int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+                int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+                if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+                    mDownEventOnEdge = true;
+                }
 
                 /*
                  * If being flinged and user touches the screen, initiate drag;
@@ -935,12 +1077,14 @@
 
                 // check if this can be the beginning of a tap on the side of the pages
                 // to scroll the current page
-                if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
-                    if (getChildCount() > 0) {
-                        if (hitsPreviousPage(x, y)) {
-                            mTouchState = TOUCH_STATE_PREV_PAGE;
-                        } else if (hitsNextPage(x, y)) {
-                            mTouchState = TOUCH_STATE_NEXT_PAGE;
+                if (!DISABLE_TOUCH_SIDE_PAGES) {
+                    if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
+                        if (getChildCount() > 0) {
+                            if (hitsPreviousPage(x, y)) {
+                                mTouchState = TOUCH_STATE_PREV_PAGE;
+                            } else if (hitsNextPage(x, y)) {
+                                mTouchState = TOUCH_STATE_NEXT_PAGE;
+                            }
                         }
                     }
                 }
@@ -949,10 +1093,7 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mTouchState = TOUCH_STATE_REST;
-                mAllowLongPress = false;
-                mActivePointerId = INVALID_POINTER;
-                releaseVelocityTracker();
+                resetTouchState();
                 break;
 
             case MotionEvent.ACTION_POINTER_UP:
@@ -982,9 +1123,17 @@
          * of the down event.
          */
         final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+
         if (pointerIndex == -1) {
             return;
         }
+
+        // If we're only allowing edge swipes, we break out early if the down event wasn't
+        // at the edge.
+        if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) {
+            return;
+        }
+
         final float x = ev.getX(pointerIndex);
         final float y = ev.getY(pointerIndex);
         final int xDiff = (int) Math.abs(x - mLastMotionX);
@@ -1002,38 +1151,27 @@
                 mTotalMotionX += Math.abs(mLastMotionX - x);
                 mLastMotionX = x;
                 mLastMotionXRemainder = 0;
-                mTouchX = getScrollX();
+                mTouchX = getViewportOffsetX() + getScrollX();
                 mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
                 pageBeginMoving();
             }
-            // Either way, cancel any pending longpress
-            cancelCurrentPageLongPress();
         }
     }
 
-    protected void cancelCurrentPageLongPress() {
-        if (mAllowLongPress) {
-            mAllowLongPress = false;
-            // Try canceling the long press. It could also have been scheduled
-            // by a distant descendant, so use the mAllowLongPress flag to block
-            // everything
-            final View currentPage = getPageAt(mCurrentPage);
-            if (currentPage != null) {
-                currentPage.cancelLongPress();
-            }
-        }
+    protected float getMaxScrollProgress() {
+        return 1.0f;
     }
 
     protected float getScrollProgress(int screenCenter, View v, int page) {
-        final int halfScreenSize = getMeasuredWidth() / 2;
+        final int halfScreenSize = getViewportWidth() / 2;
 
         int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
         int delta = screenCenter - (getChildOffset(page) -
                 getRelativeChildOffset(page) + halfScreenSize);
 
         float scrollProgress = delta / (totalDistance * 1.0f);
-        scrollProgress = Math.min(scrollProgress, 1.0f);
-        scrollProgress = Math.max(scrollProgress, -1.0f);
+        scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
+        scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress());
         return scrollProgress;
     }
 
@@ -1045,7 +1183,7 @@
     }
 
     protected void acceleratedOverScroll(float amount) {
-        int screenSize = getMeasuredWidth();
+        int screenSize = getViewportWidth();
 
         // We want to reach the max over scroll effect when the user has
         // over scrolled half the size of the screen
@@ -1070,7 +1208,7 @@
     }
 
     protected void dampedOverScroll(float amount) {
-        int screenSize = getMeasuredWidth();
+        int screenSize = getViewportWidth();
 
         float f = (amount / screenSize);
 
@@ -1130,9 +1268,22 @@
 
             // Remember where the motion event started
             mDownMotionX = mLastMotionX = ev.getX();
+            mDownMotionY = mLastMotionY = ev.getY();
+            mDownScrollX = getScrollX();
+            float[] p = mapPointFromSelfToParent(mLastMotionX, mLastMotionY);
+            mParentDownMotionX = p[0];
+            mParentDownMotionY = p[1];
             mLastMotionXRemainder = 0;
             mTotalMotionX = 0;
             mActivePointerId = ev.getPointerId(0);
+
+            // Determine if the down event is within the threshold to be an edge swipe
+            int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+            int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+            if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+                mDownEventOnEdge = true;
+            }
+
             if (mTouchState == TOUCH_STATE_SCROLLING) {
                 pageBeginMoving();
             }
@@ -1164,6 +1315,107 @@
                 } else {
                     awakenScrollBars();
                 }
+            } else if (mTouchState == TOUCH_STATE_REORDERING) {
+                // Update the last motion position
+                mLastMotionX = ev.getX();
+                mLastMotionY = ev.getY();
+
+                // Update the parent down so that our zoom animations take this new movement into
+                // account
+                float[] pt = mapPointFromSelfToParent(mLastMotionX, mLastMotionY);
+                mParentDownMotionX = pt[0];
+                mParentDownMotionY = pt[1];
+                updateDragViewTranslationDuringDrag();
+
+                // Find the closest page to the touch point
+                final int dragViewIndex = indexOfChild(mDragView);
+                int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE *
+                    getViewportWidth());
+                int leftBufferEdge = (int) (mapPointFromSelfToParent(mViewport.left, 0)[0]
+                        + bufferSize);
+                int rightBufferEdge = (int) (mapPointFromSelfToParent(mViewport.right, 0)[0]
+                        - bufferSize);
+
+                if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge);
+                if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge);
+                if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
+                if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
+                if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
+                if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
+
+                float parentX = mParentDownMotionX;
+                int pageIndexToSnapTo = -1;
+                if (parentX < leftBufferEdge && dragViewIndex > 0) {
+                    pageIndexToSnapTo = dragViewIndex - 1;
+                } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) {
+                    pageIndexToSnapTo = dragViewIndex + 1;
+                }
+
+                final int pageUnderPointIndex = pageIndexToSnapTo;
+                if (pageUnderPointIndex > -1) {
+                    mTempVisiblePagesRange[0] = 0;
+                    mTempVisiblePagesRange[1] = getPageCount() - 1;
+                    boundByReorderablePages(true, mTempVisiblePagesRange);
+                    if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
+                            pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
+                            pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
+                        mSidePageHoverIndex = pageUnderPointIndex;
+                        mSidePageHoverRunnable = new Runnable() {
+                            @Override
+                            public void run() {
+                                // Update the down scroll position to account for the fact that the
+                                // current page is moved
+                                mDownScrollX = getChildOffset(pageUnderPointIndex)
+                                        - getRelativeChildOffset(pageUnderPointIndex);
+
+                                // Setup the scroll to the correct page before we swap the views
+                                snapToPage(pageUnderPointIndex);
+
+                                // For each of the pages between the paged view and the drag view,
+                                // animate them from the previous position to the new position in
+                                // the layout (as a result of the drag view moving in the layout)
+                                int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
+                                int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
+                                        dragViewIndex + 1 : pageUnderPointIndex;
+                                int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
+                                        dragViewIndex - 1 : pageUnderPointIndex;
+                                for (int i = lowerIndex; i <= upperIndex; ++i) {
+                                    View v = getChildAt(i);
+                                    // dragViewIndex < pageUnderPointIndex, so after we remove the
+                                    // drag view all subsequent views to pageUnderPointIndex will
+                                    // shift down.
+                                    int oldX = getViewportOffsetX() + getChildOffset(i);
+                                    int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);
+
+                                    // Animate the view translation from its old position to its new
+                                    // position
+                                    AnimatorSet anim = (AnimatorSet) v.getTag();
+                                    if (anim != null) {
+                                        anim.cancel();
+                                    }
+
+                                    v.setTranslationX(oldX - newX);
+                                    anim = new AnimatorSet();
+                                    anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
+                                    anim.playTogether(
+                                            ObjectAnimator.ofFloat(v, "translationX", 0f));
+                                    anim.start();
+                                    v.setTag(anim);
+                                }
+
+                                removeView(mDragView);
+                                onRemoveView(mDragView);
+                                addView(mDragView, pageUnderPointIndex);
+                                onAddView(mDragView, pageUnderPointIndex);
+                                mSidePageHoverIndex = -1;
+                            }
+                        };
+                        postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
+                    }
+                } else {
+                    removeCallbacks(mSidePageHoverRunnable);
+                    mSidePageHoverIndex = -1;
+                }
             } else {
                 determineScrollingStart(ev);
             }
@@ -1232,21 +1484,29 @@
                 } else {
                     snapToDestination();
                 }
+            } else if (mTouchState == TOUCH_STATE_REORDERING) {
+                if (!DISABLE_FLING_TO_DELETE) {
+                    // Check the velocity and see if we are flinging-to-delete
+                    PointF flingToDeleteVector = isFlingingToDelete();
+                    if (flingToDeleteVector != null) {
+                        onFlingToDelete(flingToDeleteVector);
+                    }
+                }
             } else {
                 onUnhandledTap(ev);
             }
-            mTouchState = TOUCH_STATE_REST;
-            mActivePointerId = INVALID_POINTER;
-            releaseVelocityTracker();
+
+            // Remove the callback to wait for the side page hover timeout
+            removeCallbacks(mSidePageHoverRunnable);
+            // End any intermediate reordering states
+            resetTouchState();
             break;
 
         case MotionEvent.ACTION_CANCEL:
             if (mTouchState == TOUCH_STATE_SCROLLING) {
                 snapToDestination();
             }
-            mTouchState = TOUCH_STATE_REST;
-            mActivePointerId = INVALID_POINTER;
-            releaseVelocityTracker();
+            resetTouchState();
             break;
 
         case MotionEvent.ACTION_POINTER_UP:
@@ -1257,6 +1517,20 @@
         return true;
     }
 
+    //public abstract void onFlingToDelete(View v);
+    public abstract void onRemoveView(View v);
+    public abstract void onAddView(View v, int index);
+
+    private void resetTouchState() {
+        releaseVelocityTracker();
+        endReordering();
+        mTouchState = TOUCH_STATE_REST;
+        mActivePointerId = INVALID_POINTER;
+        mDownEventOnEdge = false;
+    }
+
+    protected void onUnhandledTap(MotionEvent ev) {}
+
     @Override
     public boolean onGenericMotionEvent(MotionEvent event) {
         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -1319,8 +1593,6 @@
         }
     }
 
-    protected void onUnhandledTap(MotionEvent ev) {}
-
     @Override
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
@@ -1352,16 +1624,28 @@
         return (minWidth > measuredWidth) ? minWidth : measuredWidth;
     }
 
+    int getPageNearestToPoint(float x) {
+        int index = 0;
+        for (int i = 0; i < getChildCount(); ++i) {
+            if (x < getChildAt(i).getRight() - getScrollX()) {
+                return index;
+            } else {
+                index++;
+            }
+        }
+        return Math.min(index, getChildCount() - 1);
+    }
+
     int getPageNearestToCenterOfScreen() {
         int minDistanceFromScreenCenter = Integer.MAX_VALUE;
         int minDistanceFromScreenCenterIndex = -1;
-        int screenCenter = getScrollX() + (getMeasuredWidth() / 2);
+        int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2);
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; ++i) {
             View layout = (View) getPageAt(i);
             int childWidth = getScaledMeasuredWidth(layout);
             int halfChildWidth = (childWidth / 2);
-            int childCenter = getChildOffset(i) + halfChildWidth;
+            int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
             int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
             if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
                 minDistanceFromScreenCenter = distanceFromScreenCenter;
@@ -1397,11 +1681,11 @@
 
     protected void snapToPageWithVelocity(int whichPage, int velocity) {
         whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
-        int halfScreenSize = getMeasuredWidth() / 2;
+        int halfScreenSize = getViewportWidth() / 2;
 
         if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
         if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
-                + getMeasuredWidth() + ", " + getChildWidth(whichPage));
+                + getViewportWidth() + ", " + getChildWidth(whichPage));
         final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
         int delta = newX - mUnboundedScrollX;
         int duration = 0;
@@ -1435,20 +1719,29 @@
     protected void snapToPage(int whichPage) {
         snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
     }
+    protected void snapToPageImmediately(int whichPage) {
+        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
+    }
 
     protected void snapToPage(int whichPage, int duration) {
+        snapToPage(whichPage, duration, false);
+    }
+    protected void snapToPage(int whichPage, int duration, boolean immediate) {
         whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
 
         if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
-        if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+        if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getViewportWidth() + ", "
                 + getChildWidth(whichPage));
         int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
         final int sX = mUnboundedScrollX;
         final int delta = newX - sX;
-        snapToPage(whichPage, delta, duration);
+        snapToPage(whichPage, delta, duration, immediate);
     }
 
     protected void snapToPage(int whichPage, int delta, int duration) {
+        snapToPage(whichPage, delta, duration, false);
+    }
+    protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) {
         mNextPage = whichPage;
 
         View focusedChild = getFocusedChild();
@@ -1459,7 +1752,9 @@
 
         pageBeginMoving();
         awakenScrollBars(duration);
-        if (duration == 0) {
+        if (immediate) {
+            duration = 0;
+        } else if (duration == 0) {
             duration = Math.abs(delta);
         }
 
@@ -1467,6 +1762,12 @@
         mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
 
         notifyPageSwitchListener();
+
+        // Trigger a compute() to finish switching pages if necessary
+        if (immediate) {
+            computeScroll();
+        }
+
         invalidate();
     }
 
@@ -1500,21 +1801,6 @@
         return result;
     }
 
-    /**
-     * @return True is long presses are still allowed for the current touch
-     */
-    public boolean allowLongPress() {
-        return mAllowLongPress;
-    }
-
-    /**
-     * Set true to allow long-press events to be triggered, usually checked by
-     * {@link Launcher} to accept or block dpad-initiated long-presses.
-     */
-    public void setAllowLongPress(boolean allowLongPress) {
-        mAllowLongPress = allowLongPress;
-    }
-
     public static class SavedState extends BaseSavedState {
         int currentPage = -1;
 
@@ -1653,7 +1939,7 @@
         if (!isScrollingIndicatorEnabled()) return;
         if (mScrollIndicator == null) return;
         int numPages = getChildCount();
-        int pageWidth = getMeasuredWidth();
+        int pageWidth = getViewportWidth();
         int lastChildIndex = Math.max(0, getChildCount() - 1);
         int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
         int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
@@ -1675,6 +1961,302 @@
         mScrollIndicator.setTranslationX(indicatorPos);
     }
 
+    // Animate the drag view back to the original position
+    void animateChildrenToOriginalPosition() {
+        if (mDragView != null) {
+            AnimatorSet anim = new AnimatorSet();
+            anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
+            anim.playTogether(
+                    ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
+                    ObjectAnimator.ofFloat(mDragView, "translationY", 0f));
+            anim.start();
+        }
+    }
+
+    // "Zooms out" the PagedView to reveal more side pages
+    boolean zoomOut() {
+        if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+            mZoomInOutAnim.cancel();
+        }
+
+        if (!(getScaleX() < 1f || getScaleY() < 1f)) {
+            mZoomInOutAnim = new AnimatorSet();
+            mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+            mZoomInOutAnim.playTogether(
+                    ObjectAnimator.ofFloat(this, "scaleX", mMinScale),
+                    ObjectAnimator.ofFloat(this, "scaleY", mMinScale));
+            mZoomInOutAnim.start();
+            return true;
+        }
+        return false;
+    }
+
+    protected void onStartReordering() {
+        // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
+        mTouchState = TOUCH_STATE_REORDERING;
+        mIsReordering = true;
+
+        // We must invalidate to trigger a redraw to update the layers such that the drag view
+        // is always drawn on top
+        invalidate();
+    }
+
+    protected void onEndReordering() {
+        mIsReordering = false;
+    }
+
+    public boolean startReordering() {
+        int dragViewIndex = getPageNearestToCenterOfScreen();
+        mTempVisiblePagesRange[0] = 0;
+        mTempVisiblePagesRange[1] = getPageCount() - 1;
+        boundByReorderablePages(true, mTempVisiblePagesRange);
+        mReorderingStarted = true;
+
+        // Check if we are within the reordering range
+        if (mTempVisiblePagesRange[0] <= dragViewIndex &&
+                dragViewIndex <= mTempVisiblePagesRange[1]) {
+            if (zoomOut()) {
+                // Find the drag view under the pointer
+                mDragView = getChildAt(dragViewIndex);
+
+                onStartReordering();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    boolean isReordering(boolean testTouchState) {
+        boolean state = mIsReordering;
+        if (testTouchState) {
+            state &= (mTouchState == TOUCH_STATE_REORDERING);
+        }
+        return state;
+    }
+    void endReordering() {
+        // For simplicity, we call endReordering sometimes even if reordering was never started.
+        // In that case, we don't want to do anything.
+        if (!mReorderingStarted) return;
+        mReorderingStarted = false;
+        Runnable onCompleteRunnable = new Runnable() {
+            @Override
+            public void run() {
+                onEndReordering();
+            }
+        };
+        zoomIn(onCompleteRunnable);
+
+        // If we haven't flung-to-delete the current child, then we just animate the drag view
+        // back into position
+        if (!mIsFlingingToDelete) {
+            // Snap to the current page
+            snapToDestination();
+
+            animateChildrenToOriginalPosition();
+        }
+    }
+
+    // "Zooms in" the PagedView to highlight the current page
+    boolean zoomIn(final Runnable onCompleteRunnable) {
+        if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+            mZoomInOutAnim.cancel();
+        }
+        if (getScaleX() < 1f || getScaleY() < 1f) {
+            mZoomInOutAnim = new AnimatorSet();
+            mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+            mZoomInOutAnim.playTogether(
+                    ObjectAnimator.ofFloat(this, "scaleX", 1f),
+                    ObjectAnimator.ofFloat(this, "scaleY", 1f));
+            mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    mDragView = null;
+                }
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mDragView = null;
+                    if (onCompleteRunnable != null) {
+                        onCompleteRunnable.run();
+                    }
+                }
+            });
+            mZoomInOutAnim.start();
+            return true;
+        } else {
+            if (onCompleteRunnable != null) {
+                onCompleteRunnable.run();
+            }
+        }
+        return false;
+    }
+
+    /*
+     * Flinging to delete - IN PROGRESS
+     */
+    private PointF isFlingingToDelete() {
+        ViewConfiguration config = ViewConfiguration.get(getContext());
+        mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
+
+        if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+            // Do a quick dot product test to ensure that we are flinging upwards
+            PointF vel = new PointF(mVelocityTracker.getXVelocity(),
+                    mVelocityTracker.getYVelocity());
+            PointF upVec = new PointF(0f, -1f);
+            float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
+                    (vel.length() * upVec.length()));
+            if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) {
+                return vel;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Creates an animation from the current drag view along its current velocity vector.
+     * For this animation, the alpha runs for a fixed duration and we update the position
+     * progressively.
+     */
+    private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
+        private View mDragView;
+        private PointF mVelocity;
+        private Rect mFrom;
+        private long mPrevTime;
+        private float mFriction;
+
+        private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
+
+        public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from,
+                long startTime, float friction) {
+            mDragView = dragView;
+            mVelocity = vel;
+            mFrom = from;
+            mPrevTime = startTime;
+            mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction);
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            float t = ((Float) animation.getAnimatedValue()).floatValue();
+            long curTime = AnimationUtils.currentAnimationTimeMillis();
+
+            mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
+            mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
+
+            mDragView.setTranslationX(mFrom.left);
+            mDragView.setTranslationY(mFrom.top);
+            mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
+
+            mVelocity.x *= mFriction;
+            mVelocity.y *= mFriction;
+            mPrevTime = curTime;
+        }
+    };
+
+    public void onFlingToDelete(PointF vel) {
+        final long startTime = AnimationUtils.currentAnimationTimeMillis();
+
+        // NOTE: Because it takes time for the first frame of animation to actually be
+        // called and we expect the animation to be a continuation of the fling, we have
+        // to account for the time that has elapsed since the fling finished.  And since
+        // we don't have a startDelay, we will always get call to update when we call
+        // start() (which we want to ignore).
+        final TimeInterpolator tInterpolator = new TimeInterpolator() {
+            private int mCount = -1;
+            private long mStartTime;
+            private float mOffset;
+            /* Anonymous inner class ctor */ {
+                mStartTime = startTime;
+            }
+
+            @Override
+            public float getInterpolation(float t) {
+                if (mCount < 0) {
+                    mCount++;
+                } else if (mCount == 0) {
+                    mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
+                            mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION);
+                    mCount++;
+                }
+                return Math.min(1f, mOffset + t);
+            }
+        };
+
+        final Rect from = new Rect();
+        final View dragView = mDragView;
+        from.left = (int) dragView.getTranslationX();
+        from.top = (int) dragView.getTranslationY();
+        AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel,
+                from, startTime, FLING_TO_DELETE_FRICTION);
+
+        final Runnable onAnimationEndRunnable = new Runnable() {
+            @Override
+            public void run() {
+                int dragViewIndex = indexOfChild(dragView);
+                // Setup the scroll to the correct page before we swap the views
+                snapToPageImmediately(dragViewIndex - 1);
+
+                // For each of the pages around the drag view, animate them from the previous
+                // position to the new position in the layout (as a result of the drag view moving
+                // in the layout)
+                // NOTE: We can make an assumption here because we have side-bound pages that we
+                //       will always have pages to animate in from the left
+                int lowerIndex = 0;
+                int upperIndex = dragViewIndex - 1;
+                for (int i = lowerIndex; i <= upperIndex; ++i) {
+                    View v = getChildAt(i);
+                    // dragViewIndex < pageUnderPointIndex, so after we remove the
+                    // drag view all subsequent views to pageUnderPointIndex will
+                    // shift down.
+                    int oldX = 0;
+                    if (i == 0) {
+                        oldX = -(getViewportOffsetX() + getChildOffset(i));
+                    } else {
+                        oldX = getViewportOffsetX() + getChildOffset(i - 1);
+                    }
+                    int newX = getViewportOffsetX() + getChildOffset(i);
+
+                    // Animate the view translation from its old position to its new
+                    // position
+                    AnimatorSet anim = (AnimatorSet) v.getTag();
+                    if (anim != null) {
+                        anim.cancel();
+                    }
+
+                    v.setTranslationX(oldX - newX);
+                    anim = new AnimatorSet();
+                    anim.setDuration(FLING_TO_DELETE_SLIDE_IN_SIDE_PAGE_DURATION);
+                    anim.playTogether(
+                            ObjectAnimator.ofFloat(v, "translationX", 0f));
+                    anim.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mIsFlingingToDelete = false;
+                        }
+                    });
+                    anim.start();
+                    v.setTag(anim);
+                }
+
+                removeView(dragView);
+                onRemoveView(dragView);
+            }
+        };
+
+        // Create and start the animation
+        ValueAnimator mDropAnim = new ValueAnimator();
+        mDropAnim.setInterpolator(tInterpolator);
+        mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION);
+        mDropAnim.setFloatValues(0f, 1f);
+        mDropAnim.addUpdateListener(updateCb);
+        mDropAnim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                onAnimationEndRunnable.run();
+            }
+        });
+        mDropAnim.start();
+        mIsFlingingToDelete = true;
+    }
+
     /* Accessibility */
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
new file mode 100644
index 0000000..506b79d
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
@@ -0,0 +1,960 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+import com.android.internal.R;
+
+/**
+ * This layout handles interaction with the sliding security challenge views
+ * that overlay/resize other keyguard contents.
+ */
+public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
+    private static final String TAG = "SlidingChallengeLayout";
+    private static final boolean DEBUG = false;
+
+    // Drawn to show the drag handle in closed state; crossfades to the challenge view
+    // when challenge is fully visible
+    private Drawable mHandleDrawable;
+    private Drawable mFrameDrawable;
+    private Drawable mDragIconDrawable;
+
+    // Initialized during measurement from child layoutparams
+    private View mChallengeView;
+    private View mScrimView;
+
+    // Range: 0 (fully hidden) to 1 (fully visible)
+    private float mChallengeOffset = 1.f;
+    private boolean mChallengeShowing = true;
+    private boolean mIsBouncing = false;
+
+    private final Scroller mScroller;
+    private int mScrollState;
+    private OnChallengeScrolledListener mScrollListener;
+    private OnBouncerStateChangedListener mBouncerListener;
+
+    public static final int SCROLL_STATE_IDLE = 0;
+    public static final int SCROLL_STATE_DRAGGING = 1;
+    public static final int SCROLL_STATE_SETTLING = 2;
+
+    private static final int MAX_SETTLE_DURATION = 600; // ms
+
+    // ID of the pointer in charge of a current drag
+    private int mActivePointerId = INVALID_POINTER;
+    private static final int INVALID_POINTER = -1;
+
+    // True if the user is currently dragging the slider
+    private boolean mDragging;
+    // True if the user may not drag until a new gesture begins
+    private boolean mBlockDrag;
+
+    private VelocityTracker mVelocityTracker;
+    private int mMinVelocity;
+    private int mMaxVelocity;
+    private float mGestureStartY; // where did you touch the screen to start this gesture?
+    private int mGestureStartChallengeBottom; // where was the challenge at that time?
+    private int mDragHandleSize; // handle hitrect extension into the challenge view
+    private int mDragHandleHeadroom; // extend the handle's hitrect this far above the line
+    private int mDragHandleEdgeSlop;
+    private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
+                                       // that should remain on-screen
+    float mHandleAlpha;
+    float mFrameAlpha;
+    private ObjectAnimator mHandleAnimation;
+    private ObjectAnimator mFrameAnimation;
+
+    static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA =
+            new FloatProperty<SlidingChallengeLayout>("handleAlpha") {
+        @Override
+        public void setValue(SlidingChallengeLayout view, float value) {
+            view.mHandleAlpha = value;
+            view.invalidate();
+        }
+
+        @Override
+        public Float get(SlidingChallengeLayout view) {
+            return view.mHandleAlpha;
+        }
+    };
+
+    static final Property<SlidingChallengeLayout, Float> FRAME_ALPHA =
+            new FloatProperty<SlidingChallengeLayout>("frameAlpha") {
+        @Override
+        public void setValue(SlidingChallengeLayout view, float value) {
+            if (view.mFrameDrawable != null) {
+                view.mFrameAlpha = value;
+                view.mFrameDrawable.setAlpha((int) (value * 0xFF));
+                view.mFrameDrawable.invalidateSelf();
+            }
+        }
+
+        @Override
+        public Float get(SlidingChallengeLayout view) {
+            return view.mFrameAlpha;
+        }
+    };
+
+    private static final int DRAG_HANDLE_DEFAULT_SIZE = 32; // dp
+    private static final int HANDLE_ANIMATE_DURATION = 200; // ms
+
+    // True if at least one layout pass has happened since the view was attached.
+    private boolean mHasLayout;
+
+    private static final Interpolator sMotionInterpolator = new Interpolator() {
+        public float getInterpolation(float t) {
+            t -= 1.0f;
+            return t * t * t * t * t + 1.0f;
+        }
+    };
+
+    private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
+        public float getInterpolation(float t) {
+            return t * t;
+        }
+    };
+
+    private final Runnable mEndScrollRunnable = new Runnable () {
+        public void run() {
+            completeChallengeScroll();
+        }
+    };
+
+    private final OnClickListener mScrimClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            hideBouncer();
+        }
+    };
+
+    /**
+     * Listener interface that reports changes in scroll state of the challenge area.
+     */
+    public interface OnChallengeScrolledListener {
+        /**
+         * The scroll state itself changed.
+         *
+         * <p>scrollState will be one of the following:</p>
+         *
+         * <ul>
+         * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
+         * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
+         * the challenge area.</li>
+         * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
+         * into place.</li>
+         * </ul>
+         *
+         * <p>Do not perform expensive operations (e.g. layout)
+         * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
+         *
+         * @param scrollState The new scroll state of the challenge area.
+         */
+        public void onScrollStateChanged(int scrollState);
+
+        /**
+         * The precise position of the challenge area has changed.
+         *
+         * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
+         * result in a requestLayout anywhere in your view hierarchy as a result of this call.
+         * It may be called during drawing.</p>
+         *
+         * @param scrollPosition New relative position of the challenge area.
+         *                       1.f = fully visible/ready to be interacted with.
+         *                       0.f = fully invisible/inaccessible to the user.
+         * @param challengeTop Position of the top edge of the challenge view in px in the
+         *                     SlidingChallengeLayout's coordinate system.
+         */
+        public void onScrollPositionChanged(float scrollPosition, int challengeTop);
+    }
+
+    public SlidingChallengeLayout(Context context) {
+        this(context, null);
+    }
+
+    public SlidingChallengeLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.SlidingChallengeLayout, defStyle, 0);
+        setDragDrawables(a.getDrawable(R.styleable.SlidingChallengeLayout_dragHandle),
+                a.getDrawable(R.styleable.SlidingChallengeLayout_dragIcon));
+
+        a.recycle();
+
+        mScroller = new Scroller(context, sMotionInterpolator);
+
+        final ViewConfiguration vc = ViewConfiguration.get(context);
+        mMinVelocity = vc.getScaledMinimumFlingVelocity();
+        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
+
+        mDragHandleEdgeSlop = getResources().getDimensionPixelSize(
+                R.dimen.kg_edge_swipe_region_size);
+
+        setWillNotDraw(false);
+    }
+
+    public void setDragDrawables(Drawable handle, Drawable icon) {
+        final float density = getResources().getDisplayMetrics().density;
+        final int defaultSize = (int) (DRAG_HANDLE_DEFAULT_SIZE * density + 0.5f);
+        final int handleHeight = handle != null ? handle.getIntrinsicHeight() : 0;
+        final int iconHeight = icon != null ? icon.getIntrinsicHeight() : 0;
+        mDragHandleSize = Math.max(handleHeight > 0 ? handleHeight : defaultSize,
+                iconHeight > 0 ? iconHeight : defaultSize);
+
+        // top half of the lock icon, plus another 25% to be sure
+        mDragHandleHeadroom = (int) (iconHeight * 0.75f);
+        mChallengeBottomBound = (mDragHandleSize + mDragHandleHeadroom + handleHeight) / 2;
+
+        mHandleDrawable = handle;
+        mDragIconDrawable = icon;
+    }
+
+    public void setDragIconDrawable(Drawable d) {
+        mDragIconDrawable = d;
+    }
+
+    public void showHandle(boolean visible) {
+        if (visible) {
+            if (mHandleAnimation != null) {
+                mHandleAnimation.cancel();
+                mHandleAnimation = null;
+            }
+            mHandleAlpha = 1.f;
+            invalidate();
+        } else {
+            animateHandle(false);
+        }
+    }
+
+    void animateHandle(boolean visible) {
+        if (mHandleAnimation != null) {
+            mHandleAnimation.cancel();
+            mHandleAnimation = null;
+        }
+        final float targetAlpha = visible ? 1.f : 0.f;
+        if (targetAlpha == mHandleAlpha) {
+            return;
+        }
+        mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha);
+        mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
+        mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
+        mHandleAnimation.start();
+    }
+
+    void animateFrame(boolean visible, 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) {
+            return;
+        }
+
+        mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha);
+        mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
+        mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
+        mFrameAnimation.start();
+    }
+
+    private void sendInitialListenerUpdates() {
+        if (mScrollListener != null) {
+            int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
+            mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
+            mScrollListener.onScrollStateChanged(mScrollState);
+        }
+    }
+
+    public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
+        mScrollListener = listener;
+        if (mHasLayout) {
+            sendInitialListenerUpdates();
+        }
+    }
+
+    public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
+        mBouncerListener = listener;
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        mHasLayout = false;
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        removeCallbacks(mEndScrollRunnable);
+        mHasLayout = false;
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        if (mIsBouncing && child != mChallengeView) {
+            // Clear out of the bouncer if the user tries to move focus outside of
+            // the security challenge view.
+            hideBouncer();
+        }
+        super.requestChildFocus(child, focused);
+    }
+
+    // We want the duration of the page snap animation to be influenced by the distance that
+    // the screen has to travel, however, we don't want this duration to be effected in a
+    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
+    // of travel has on the overall snap duration.
+    float distanceInfluenceForSnapDuration(float f) {
+        f -= 0.5f; // center the values about 0.
+        f *= 0.3f * Math.PI / 2.0f;
+        return (float) Math.sin(f);
+    }
+
+    void setScrollState(int state) {
+        if (mScrollState != state) {
+            mScrollState = state;
+
+            animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
+            animateFrame(state != SCROLL_STATE_IDLE, false);
+            if (mScrollListener != null) {
+                mScrollListener.onScrollStateChanged(state);
+            }
+        }
+    }
+
+    void completeChallengeScroll() {
+        setChallengeShowing(mChallengeOffset != 0);
+        setScrollState(SCROLL_STATE_IDLE);
+    }
+
+    void setScrimView(View scrim) {
+        if (mScrimView != null) {
+            mScrimView.setOnClickListener(null);
+        }
+        mScrimView = scrim;
+        mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
+        mScrimView.setFocusable(true);
+        mScrimView.setOnClickListener(mScrimClickListener);
+    }
+
+    /**
+     * Animate the bottom edge of the challenge view to the given position.
+     *
+     * @param y desired final position for the bottom edge of the challenge view in px
+     * @param velocity velocity in
+     */
+    void animateChallengeTo(int y, int velocity) {
+        if (mChallengeView == null) {
+            // Nothing to do.
+            return;
+        }
+        final int sy = mChallengeView.getBottom();
+        final int dy = y - sy;
+        if (dy == 0) {
+            completeChallengeScroll();
+            return;
+        }
+
+        setScrollState(SCROLL_STATE_SETTLING);
+
+        final int childHeight = mChallengeView.getHeight();
+        final int halfHeight = childHeight / 2;
+        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
+        final float distance = halfHeight + halfHeight *
+                distanceInfluenceForSnapDuration(distanceRatio);
+
+        int duration = 0;
+        velocity = Math.abs(velocity);
+        if (velocity > 0) {
+            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
+        } else {
+            final float childDelta = (float) Math.abs(dy) / childHeight;
+            duration = (int) ((childDelta + 1) * 100);
+        }
+        duration = Math.min(duration, MAX_SETTLE_DURATION);
+
+        mScroller.startScroll(0, sy, 0, dy, duration);
+        postInvalidateOnAnimation();
+    }
+
+    private void setChallengeShowing(boolean showChallenge) {
+        mChallengeShowing = showChallenge;
+    }
+
+    /**
+     * @return true if the challenge is at all visible.
+     */
+    public boolean isChallengeShowing() {
+        return mChallengeShowing;
+    }
+
+    @Override
+    public boolean isChallengeOverlapping() {
+        return mChallengeShowing;
+    }
+
+    @Override
+    public boolean isBouncing() {
+        return mIsBouncing;
+    }
+
+    @Override
+    public void showBouncer() {
+        if (mIsBouncing) return;
+        showChallenge(true);
+        mIsBouncing = true;
+        if (mScrimView != null) {
+            mScrimView.setVisibility(VISIBLE);
+        }
+        animateFrame(true, true);
+        if (mBouncerListener != null) {
+            mBouncerListener.onBouncerStateChanged(true);
+        }
+    }
+
+    @Override
+    public void hideBouncer() {
+        if (!mIsBouncing) return;
+        setChallengeShowing(false);
+        mIsBouncing = false;
+        if (mScrimView != null) {
+            mScrimView.setVisibility(GONE);
+        }
+        animateFrame(false, false);
+        if (mBouncerListener != null) {
+            mBouncerListener.onBouncerStateChanged(false);
+        }
+    }
+
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
+        // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
+        // If there are one or more pointers in the challenge view before we take over
+        // touch events, onInterceptTouchEvent will set mBlockDrag.
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+
+        final int action = ev.getActionMasked();
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mGestureStartY = ev.getY();
+                mBlockDrag = false;
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                resetTouch();
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final int count = ev.getPointerCount();
+                for (int i = 0; i < count; i++) {
+                    final float x = ev.getX(i);
+                    final float y = ev.getY(i);
+
+                    if (!mIsBouncing &&
+                            (isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
+                            (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
+                            mActivePointerId == INVALID_POINTER) {
+                        mActivePointerId = ev.getPointerId(i);
+                        mGestureStartY = ev.getY();
+                        mGestureStartChallengeBottom = getChallengeBottom();
+                        mDragging = true;
+                    } else if (isInChallengeView(x, y)) {
+                        mBlockDrag = true;
+                    }
+                }
+                break;
+        }
+
+        if (mBlockDrag) {
+            mActivePointerId = INVALID_POINTER;
+            mDragging = false;
+        }
+
+        return mDragging;
+    }
+
+    private void resetTouch() {
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+        mActivePointerId = INVALID_POINTER;
+        mDragging = mBlockDrag = false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+
+        final int action = ev.getActionMasked();
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mBlockDrag = false;
+                mGestureStartY = ev.getY();
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+                if (mDragging) {
+                    showChallenge(0);
+                }
+                resetTouch();
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
+                    break;
+                }
+            case MotionEvent.ACTION_UP:
+                if (mDragging) {
+                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
+                    showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
+                }
+                resetTouch();
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                if (!mDragging && !mBlockDrag && !mIsBouncing) {
+                    final int count = ev.getPointerCount();
+                    for (int i = 0; i < count; i++) {
+                        final float x = ev.getX(i);
+                        final float y = ev.getY(i);
+
+                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
+                                (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
+                                && mActivePointerId == INVALID_POINTER) {
+                            mGestureStartY = y;
+                            mActivePointerId = ev.getPointerId(i);
+                            mGestureStartChallengeBottom = getChallengeBottom();
+                            mDragging = true;
+                            break;
+                        }
+                    }
+                }
+                // Not an else; this can be set above.
+                if (mDragging) {
+                    // No-op if already in this state, but set it here in case we arrived
+                    // at this point from either intercept or the above.
+                    setScrollState(SCROLL_STATE_DRAGGING);
+
+                    final int index = ev.findPointerIndex(mActivePointerId);
+                    if (index < 0) {
+                        // Oops, bogus state. We lost some touch events somewhere.
+                        // Just drop it with no velocity and let things settle.
+                        resetTouch();
+                        showChallenge(0);
+                        return true;
+                    }
+                    final float y = ev.getY(index);
+                    final float pos = Math.min(y - mGestureStartY,
+                            getLayoutBottom() - mChallengeBottomBound);
+
+                    moveChallengeTo(mGestureStartChallengeBottom + (int) pos);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * We only want to add additional vertical space to the drag handle when the panel is fully
+     * closed.
+     */
+    private int getDragHandleHeadroom() {
+        return isChallengeShowing() ? 0 : mDragHandleHeadroom;
+    }
+
+    private boolean isInChallengeView(float x, float y) {
+        if (mChallengeView == null) return false;
+
+        return x >= mChallengeView.getLeft() && y >= mChallengeView.getTop() &&
+                x < mChallengeView.getRight() && y < mChallengeView.getBottom();
+    }
+
+    private boolean isInDragHandle(float x, float y) {
+        if (mChallengeView == null) return false;
+
+        return x >= mDragHandleEdgeSlop &&
+                y >= mChallengeView.getTop() - getDragHandleHeadroom() &&
+                x < getWidth() - mDragHandleEdgeSlop &&
+                y < mChallengeView.getTop() + mDragHandleSize;
+    }
+
+    private boolean crossedDragHandle(float x, float y, float initialY) {
+        final int challengeTop = mChallengeView.getTop();
+        return  x >= 0 &&
+                x < getWidth() &&
+                initialY < (challengeTop - getDragHandleHeadroom()) &&
+                y > challengeTop + mDragHandleSize;
+    }
+
+    @Override
+    protected void onMeasure(int widthSpec, int heightSpec) {
+        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
+                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
+            throw new IllegalArgumentException(
+                    "SlidingChallengeLayout must be measured with an exact size");
+        }
+
+        final int width = MeasureSpec.getSize(widthSpec);
+        final int height = MeasureSpec.getSize(heightSpec);
+        setMeasuredDimension(width, height);
+
+        // Find one and only one challenge view.
+        final View oldChallengeView = mChallengeView;
+        mChallengeView = null;
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+                if (mChallengeView != null) {
+                    throw new IllegalStateException(
+                            "There may only be one child with layout_isChallenge=\"true\"");
+                }
+                mChallengeView = child;
+                if (mChallengeView != oldChallengeView) {
+                    mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
+                }
+                // We're going to play silly games with the frame's background drawable later.
+                mFrameDrawable = mChallengeView.getBackground();
+            } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
+                setScrimView(child);
+            }
+
+            if (child.getVisibility() == GONE) continue;
+
+            measureChildWithMargins(child, widthSpec, 0, heightSpec, 0);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        final int paddingLeft = getPaddingLeft();
+        final int paddingTop = getPaddingTop();
+        final int paddingRight = getPaddingRight();
+        final int paddingBottom = getPaddingBottom();
+        final int width = r - l;
+        final int height = b - t;
+
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+
+            if (child.getVisibility() == GONE) continue;
+
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+                // Challenge views pin to the bottom, offset by a portion of their height,
+                // and center horizontally.
+                final int center = (paddingLeft + width - paddingRight) / 2;
+                final int childWidth = child.getMeasuredWidth();
+                final int childHeight = child.getMeasuredHeight();
+                final int left = center - childWidth / 2;
+                final int layoutBottom = height - paddingBottom - lp.bottomMargin;
+                // We use the top of the challenge view to position the handle, so
+                // we never want less than the handle size showing at the bottom.
+                final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound)
+                        * (1 - mChallengeOffset));
+                child.setAlpha(mChallengeOffset / 2 + 0.5f);
+                child.layout(left, bottom - childHeight, left + childWidth, bottom);
+            } else {
+                // Non-challenge views lay out from the upper left, layered.
+                child.layout(paddingLeft + lp.leftMargin,
+                        paddingTop + lp.topMargin,
+                        paddingLeft + child.getMeasuredWidth(),
+                        paddingTop + child.getMeasuredHeight());
+            }
+        }
+
+        if (!mHasLayout) {
+            // We want to trigger the initial listener updates outside of layout pass,
+            // in case the listeners trigger requestLayout().
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    sendInitialListenerUpdates();
+                }
+            });
+            if (mFrameDrawable != null) {
+                mFrameDrawable.setAlpha(0);
+            }
+            mHasLayout = true;
+        }
+    }
+
+    public void computeScroll() {
+        super.computeScroll();
+
+        if (!mScroller.isFinished()) {
+            if (mChallengeView == null) {
+                // Can't scroll if the view is missing.
+                Log.e(TAG, "Challenge view missing in computeScroll");
+                mScroller.abortAnimation();
+                return;
+            }
+
+            mScroller.computeScrollOffset();
+            moveChallengeTo(mScroller.getCurrY());
+
+            if (mScroller.isFinished()) {
+                post(mEndScrollRunnable);
+            }
+        }
+    }
+
+    @Override
+    public void draw(Canvas c) {
+        super.draw(c);
+
+        final Paint debugPaint;
+        if (DEBUG) {
+            debugPaint = new Paint();
+            debugPaint.setColor(0x40FF00CC);
+            // show the isInDragHandle() rect
+            c.drawRect(mDragHandleEdgeSlop,
+                    mChallengeView.getTop() - getDragHandleHeadroom(),
+                    getWidth() - mDragHandleEdgeSlop,
+                    mChallengeView.getTop() + mDragHandleSize,
+                    debugPaint);
+        }
+
+        if (mChallengeView != null && mHandleAlpha > 0 && mHandleDrawable != null) {
+            final int top = mChallengeView.getTop();
+            final int handleHeight = mHandleDrawable.getIntrinsicHeight();
+            final int challengeLeft = mChallengeView.getLeft();
+            final int challengeRight = mChallengeView.getRight();
+            mHandleDrawable.setBounds(challengeLeft, top, challengeRight, top + handleHeight);
+            mHandleDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
+            mHandleDrawable.draw(c);
+
+            if (DEBUG) {
+                // now show the actual drag handle
+                debugPaint.setStyle(Paint.Style.STROKE);
+                debugPaint.setStrokeWidth(1);
+                debugPaint.setColor(0xFF80FF00);
+                c.drawRect(challengeLeft, top, challengeRight, top + handleHeight, debugPaint);
+            }
+
+            if (mDragIconDrawable != null) {
+                final int iconWidth = mDragIconDrawable.getIntrinsicWidth();
+                final int iconHeight = mDragIconDrawable.getIntrinsicHeight();
+                final int iconLeft = (challengeLeft + challengeRight - iconWidth) / 2;
+                final int iconTop = top + (handleHeight - iconHeight) / 2;
+                mDragIconDrawable.setBounds(iconLeft, iconTop, iconLeft + iconWidth,
+                        iconTop + iconHeight);
+                mDragIconDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
+                mDragIconDrawable.draw(c);
+
+                if (DEBUG) {
+                    debugPaint.setColor(0xFF00FF00);
+                    c.drawRect(iconLeft, iconTop, iconLeft + iconWidth,
+                        iconTop + iconHeight, debugPaint);
+                }
+            }
+        }
+    }
+
+    /**
+     * Move the bottom edge of mChallengeView to a new position and notify the listener
+     * if it represents a change in position. Changes made through this method will
+     * be stable across layout passes. If this method is called before first layout of
+     * this SlidingChallengeLayout it will have no effect.
+     *
+     * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
+     * @return true if the challenge view was moved
+     */
+    private boolean moveChallengeTo(int bottom) {
+        if (mChallengeView == null || !mHasLayout) {
+            return false;
+        }
+
+        final int layoutBottom = getLayoutBottom();
+        final int challengeHeight = mChallengeView.getHeight();
+
+        bottom = Math.max(layoutBottom,
+                Math.min(bottom, layoutBottom + challengeHeight - mChallengeBottomBound));
+
+        float offset = 1.f - (float) (bottom - layoutBottom) /
+                (challengeHeight - mChallengeBottomBound);
+        mChallengeOffset = offset;
+        if (offset > 0 && !mChallengeShowing) {
+            setChallengeShowing(true);
+        }
+
+        mChallengeView.layout(mChallengeView.getLeft(),
+                bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
+
+        mChallengeView.setAlpha(offset / 2 + 0.5f);
+        if (mScrollListener != null) {
+            mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
+        }
+        postInvalidateOnAnimation();
+        return true;
+    }
+
+    /**
+     * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with
+     * the bottom edge of mChallengeView when the challenge is fully opened.
+     */
+    private int getLayoutBottom() {
+        final int bottomMargin = (mChallengeView == null)
+                ? 0
+                : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
+        final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
+        return layoutBottom;
+    }
+
+    /**
+     * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'.
+     */
+    private int getChallengeBottom() {
+        if (mChallengeView == null) return 0;
+
+        return mChallengeView.getBottom();
+    }
+
+    /**
+     * Show or hide the challenge view, animating it if necessary.
+     * @param show true to show, false to hide
+     */
+    public void showChallenge(boolean show) {
+        showChallenge(show, 0);
+        if (!show) {
+            // Block any drags in progress so that callers can use this to disable dragging
+            // for other touch interactions.
+            mBlockDrag = true;
+        }
+    }
+
+    private void showChallenge(int velocity) {
+        boolean show = false;
+        if (Math.abs(velocity) > mMinVelocity) {
+            show = velocity < 0;
+        } else {
+            show = mChallengeOffset >= 0.5f;
+        }
+        showChallenge(show, velocity);
+    }
+
+    private void showChallenge(boolean show, int velocity) {
+        if (mChallengeView == null) {
+            setChallengeShowing(false);
+            return;
+        }
+
+        if (mHasLayout) {
+            final int layoutBottom = getLayoutBottom();
+            animateChallengeTo(show ? layoutBottom :
+                    layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
+        }
+    }
+
+    @Override
+    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
+                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
+                new LayoutParams(p);
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams();
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams;
+    }
+
+    public static class LayoutParams extends MarginLayoutParams {
+        public int childType = CHILD_TYPE_NONE;
+        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 LayoutParams() {
+            this(MATCH_PARENT, WRAP_CONTENT);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(MarginLayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(LayoutParams source) {
+            super(source);
+
+            childType = source.childType;
+        }
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+
+            final TypedArray a = c.obtainStyledAttributes(attrs,
+                    R.styleable.SlidingChallengeLayout_Layout);
+            childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
+                    CHILD_TYPE_NONE);
+            a.recycle();
+        }
+    }
+}
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index f960833..440f8e1 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -130,12 +130,14 @@
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         
-        mTimeTickSender = PendingIntent.getBroadcast(context, 0,
+        mTimeTickSender = PendingIntent.getBroadcastAsUser(context, 0,
                 new Intent(Intent.ACTION_TIME_TICK).addFlags(
-                        Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
+                        Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0,
+                        UserHandle.ALL);
         Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
+        mDateChangeSender = PendingIntent.getBroadcastAsUser(context, 0, intent,
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
         
         // now that we have initied the driver schedule the alarm
         mClockReceiver= new ClockReceiver();
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index dcbf3f5..b38b016 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1434,12 +1434,9 @@
                 set.add(pkg.packageName);
                 if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
 
-                // If we've never seen this app before, schedule a backup for it
-                if (!mEverStoredApps.contains(pkg.packageName)) {
-                    if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
-                            + " never backed up; scheduling");
-                    dataChangedImpl(pkg.packageName);
-                }
+                // Schedule a backup for it on general principles
+                if (DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
+                dataChangedImpl(pkg.packageName);
             }
         }
     }
@@ -1472,8 +1469,10 @@
             // Found it.  Remove this one package from the bookkeeping, and
             // if it's the last participating app under this uid we drop the
             // (now-empty) set as well.
+            // Note that we deliberately leave it 'known' in the "ever backed up"
+            // bookkeeping so that its current-dataset data will be retrieved
+            // if the app is subsequently reinstalled
             if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
-            removeEverBackedUp(packageName);
             set.remove(packageName);
             mPendingBackups.remove(packageName);
         }
@@ -5441,7 +5440,8 @@
 
         long restoreSet = getAvailableRestoreToken(packageName);
         if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
-                + " token=" + Integer.toHexString(token));
+                + " token=" + Integer.toHexString(token)
+                + " restoreSet=" + Long.toHexString(restoreSet));
 
         if (mAutoRestore && mProvisioned && restoreSet != 0) {
             // okay, we're going to attempt a restore of this package from this restore set.
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index 079f723..6ff33d7 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -1,5 +1,17 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.server;
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 679a22a..0e51c47 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1561,7 +1561,12 @@
                 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString);
                 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]);
                 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode());
-                mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
                 return true;
             }
         }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 37dee19..eb833eb 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -405,7 +405,9 @@
             mLastLocation.clear();
             for (LocationProviderInterface p : mProviders) {
                 updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
-                p.switchUser(userId);
+                if (!LocationManager.FUSED_PROVIDER.equals(p.getName())) {
+                    p.switchUser(userId);
+                }
             }
             mCurrentUserId = userId;
             updateProvidersLocked();
@@ -664,9 +666,27 @@
         mProvidersByName.remove(provider.getName());
     }
 
+    /**
+     * Returns true if the specified UID is SYSTEM_UID or matches the current user.
+     *
+     * @param uid the uid
+     * @return true if uid is SYSTEM_UID or matches the current user
+     */
+    private boolean isCurrentUserOrSystemLocked(int uid) {
+        return uid == Process.SYSTEM_UID || UserHandle.getUserId(uid) == mCurrentUserId;
+    }
 
-    private boolean isAllowedBySettingsLocked(String provider, int userId) {
-        if (userId != mCurrentUserId) {
+    /**
+     * Returns the first UID in the current user's range.
+     *
+     * @return the first UID in the current user's range
+     */
+    private int getCurrentUidBaseLocked() {
+        return UserHandle.getUid(mCurrentUserId, 0);
+    }
+
+    private boolean isAllowedBySettingsLocked(String provider, int uid) {
+        if (!isCurrentUserOrSystemLocked(uid)) {
             return false;
         }
         if (mEnabledProviders.contains(provider)) {
@@ -675,6 +695,10 @@
         if (mDisabledProviders.contains(provider)) {
             return false;
         }
+        if (uid == Process.SYSTEM_UID) {
+            return true;
+        }
+
         // Use system settings
         ContentResolver resolver = mContext.getContentResolver();
 
@@ -828,8 +852,8 @@
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         ArrayList<String> out;
-        int callingUserId = UserHandle.getCallingUserId();
-        long identity = Binder.clearCallingIdentity();
+        final int callingUid = Binder.getCallingUid();
+        final long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
                 out = new ArrayList<String>(mProviders.size());
@@ -839,7 +863,7 @@
                         continue;
                     }
                     if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
-                        if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
+                        if (enabledOnly && !isAllowedBySettingsLocked(name, callingUid)) {
                             continue;
                         }
                         if (criteria != null && !LocationProvider.propertiesMeetCriteria(
@@ -915,7 +939,7 @@
             LocationProviderInterface p = mProviders.get(i);
             boolean isEnabled = p.isEnabled();
             String name = p.getName();
-            boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId);
+            boolean shouldBeEnabled = isAllowedBySettingsLocked(name, getCurrentUidBaseLocked());
             if (isEnabled && !shouldBeEnabled) {
                 updateProviderListenersLocked(name, false, mCurrentUserId);
                 changesMade = true;
@@ -943,7 +967,7 @@
             final int N = records.size();
             for (int i = 0; i < N; i++) {
                 UpdateRecord record = records.get(i);
-                if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
+                if (isCurrentUserOrSystemLocked(record.mReceiver.mUid)) {
                     // Sends a notification message to the receiver
                     if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
                         if (deadReceivers == null) {
@@ -982,7 +1006,7 @@
 
         if (records != null) {
             for (UpdateRecord record : records) {
-                if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                if (isCurrentUserOrSystemLocked(record.mReceiver.mUid)) {
                     LocationRequest locationRequest = record.mRequest;
                     providerRequest.locationRequests.add(locationRequest);
                     if (locationRequest.getInterval() < providerRequest.interval) {
@@ -1000,7 +1024,7 @@
                 // under that threshold.
                 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
                 for (UpdateRecord record : records) {
-                    if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                    if (isCurrentUserOrSystemLocked(record.mReceiver.mUid)) {
                         LocationRequest locationRequest = record.mRequest;
                         if (locationRequest.getInterval() <= thresholdInterval) {
                             worksource.add(record.mReceiver.mUid);
@@ -1223,7 +1247,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
+        boolean isProviderEnabled = isAllowedBySettingsLocked(name, uid);
         if (isProviderEnabled) {
             applyRequirementsLocked(name);
         } else {
@@ -1278,9 +1302,10 @@
         }
 
         // update provider
+        int currentUidBase = getCurrentUidBaseLocked();
         for (String provider : providers) {
             // If provider is already disabled, don't need to do anything
-            if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) {
+            if (!isAllowedBySettingsLocked(provider, currentUidBase)) {
                 continue;
             }
 
@@ -1298,7 +1323,8 @@
                 request.getProvider());
         // no need to sanitize this request, as only the provider name is used
 
-        long identity = Binder.clearCallingIdentity();
+        final int callingUid = Binder.getCallingUid();
+        final long identity = Binder.clearCallingIdentity();
         try {
             if (mBlacklist.isBlacklisted(packageName)) {
                 if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
@@ -1314,7 +1340,9 @@
                 LocationProviderInterface provider = mProvidersByName.get(name);
                 if (provider == null) return null;
 
-                if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null;
+                if (!isAllowedBySettingsLocked(name, callingUid)) {
+                    return null;
+                }
 
                 Location location = mLastLocation.get(name);
                 if (location == null) {
@@ -1471,13 +1499,14 @@
                 provider);
         if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
 
-        long identity = Binder.clearCallingIdentity();
+        final int callingUid = Binder.getCallingUid();
+        final long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
                 LocationProviderInterface p = mProvidersByName.get(provider);
                 if (p == null) return false;
 
-                return isAllowedBySettingsLocked(provider, mCurrentUserId);
+                return isAllowedBySettingsLocked(provider, callingUid);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -1616,10 +1645,10 @@
             Receiver receiver = r.mReceiver;
             boolean receiverDead = false;
 
-            int receiverUserId = UserHandle.getUserId(receiver.mUid);
-            if (receiverUserId != mCurrentUserId) {
+            final int receiverUid = receiver.mUid;
+            if (!isCurrentUserOrSystemLocked(receiverUid)) {
                 if (D) {
-                    Log.d(TAG, "skipping loc update for background user " + receiverUserId +
+                    Log.d(TAG, "skipping loc update for background uid " + receiverUid +
                             " (current user: " + mCurrentUserId + ", app: " +
                             receiver.mPackageName + ")");
                 }
@@ -1716,7 +1745,7 @@
         }
 
         synchronized (mLock) {
-            if (isAllowedBySettingsLocked(provider, mCurrentUserId)) {
+            if (isAllowedBySettingsLocked(provider, getCurrentUidBaseLocked())) {
                 handleLocationChangedLocked(location, passive);
             }
         }
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7c482f5..6b277c7 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -558,8 +558,9 @@
     public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
         synchronized (mLock) {
             // Automation service is not bound, so pretend it died to perform clean up.
-            if (mUiAutomationService != null
-                    && mUiAutomationService.mServiceInterface == serviceClient) {
+            if (mUiAutomationService != null && mUiAutomationService.mServiceInterface != null
+                    && serviceClient != null && mUiAutomationService.mServiceInterface
+                            .asBinder() == serviceClient.asBinder()) {
                 mUiAutomationService.binderDied();
             }
         }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index b9f5b5b..749dc66 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -602,6 +602,15 @@
         }
     }
 
+    void updateOptionsLocked(ActivityOptions options) {
+        if (options != null) {
+            if (pendingOptions != null) {
+                pendingOptions.abort();
+            }
+            pendingOptions = options;
+        }
+    }
+
     void applyOptionsLocked() {
         if (pendingOptions != null) {
             final int animationType = pendingOptions.getAnimationType();
@@ -653,6 +662,12 @@
         }
     }
 
+    ActivityOptions takeOptionsLocked() {
+        ActivityOptions opts = pendingOptions;
+        pendingOptions = null;
+        return opts;
+    }
+
     void removeUriPermissionsLocked() {
         if (uriPermissions != null) {
             uriPermissions.removeUriPermissionsLocked();
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 4bcb339..4546dc3 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1963,6 +1963,8 @@
         int taskTopI = -1;
         int replyChainEnd = -1;
         int lastReparentPos = -1;
+        ActivityOptions topOptions = null;
+        boolean canMoveOptions = true;
         for (int i=mHistory.size()-1; i>=-1; i--) {
             ActivityRecord below = i >= 0 ? mHistory.get(i) : null;
             
@@ -2048,6 +2050,7 @@
                         }
                         int dstPos = 0;
                         ThumbnailHolder curThumbHolder = target.thumbHolder;
+                        boolean gotOptions = !canMoveOptions;
                         for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
                             p = mHistory.get(srcPos);
                             if (p.finishing) {
@@ -2057,6 +2060,13 @@
                                     + " out to target's task " + target.task);
                             p.setTask(target.task, curThumbHolder, false);
                             curThumbHolder = p.thumbHolder;
+                            canMoveOptions = false;
+                            if (!gotOptions && topOptions == null) {
+                                topOptions = p.takeOptionsLocked();
+                                if (topOptions != null) {
+                                    gotOptions = true;
+                                }
+                            }
                             if (DEBUG_ADD_REMOVE) {
                                 RuntimeException here = new RuntimeException("here");
                                 here.fillInStackTrace();
@@ -2101,11 +2111,19 @@
                             replyChainEnd = targetI;
                         }
                         ActivityRecord p = null;
+                        boolean gotOptions = !canMoveOptions;
                         for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
                             p = mHistory.get(srcPos);
                             if (p.finishing) {
                                 continue;
                             }
+                            canMoveOptions = false;
+                            if (!gotOptions && topOptions == null) {
+                                topOptions = p.takeOptionsLocked();
+                                if (topOptions != null) {
+                                    gotOptions = true;
+                                }
+                            }
                             if (finishActivityLocked(p, srcPos,
                                     Activity.RESULT_CANCELED, null, "reset", false)) {
                                 replyChainEnd--;
@@ -2245,7 +2263,17 @@
             target = below;
             targetI = i;
         }
-        
+
+        if (topOptions != null) {
+            // If we got some ActivityOptions from an activity on top that
+            // was removed from the task, propagate them to the new real top.
+            if (taskTop != null) {
+                taskTop.updateOptionsLocked(topOptions);
+            } else {
+                topOptions.abort();
+            }
+        }
+
         return taskTop;
     }
     
@@ -2296,6 +2324,10 @@
                     if (r.finishing) {
                         continue;
                     }
+                    ActivityOptions opts = r.takeOptionsLocked();
+                    if (opts != null) {
+                        ret.updateOptionsLocked(opts);
+                    }
                     if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
                             null, "clear", false)) {
                         i--;
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index e76bf44..247d8a0 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -17,6 +17,7 @@
 package com.android.server.display;
 
 import android.util.DisplayMetrics;
+import android.view.Display;
 import android.view.Surface;
 
 import libcore.util.Objects;
@@ -138,6 +139,17 @@
      */
     public int rotation = Surface.ROTATION_0;
 
+    /**
+     * Display type.
+     */
+    public int type;
+
+    /**
+     * Display address, or null if none.
+     * Interpretation varies by display type.
+     */
+    public String address;
+
     public void setAssumedDensityForExternalDisplay(int width, int height) {
         densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
         // Technically, these values should be smaller than the apparent density
@@ -162,7 +174,9 @@
                 && yDpi == other.yDpi
                 && flags == other.flags
                 && touch == other.touch
-                && rotation == other.rotation;
+                && rotation == other.rotation
+                && type == other.type
+                && Objects.equal(address, other.address);
     }
 
     @Override
@@ -181,6 +195,8 @@
         flags = other.flags;
         touch = other.touch;
         rotation = other.rotation;
+        type = other.type;
+        address = other.address;
     }
 
     // For debugging purposes
@@ -191,6 +207,8 @@
                 + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
                 + ", touch " + touchToString(touch) + flagsToString(flags)
                 + ", rotation " + rotation
+                + ", type " + Display.typeToString(type)
+                + ", address " + address
                 + "}";
     }
 
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index 919733d..7a104d7 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.util.DisplayMetrics;
+import android.view.Display;
 
 /**
  * Provides a fake default display for headless systems.
@@ -63,6 +64,7 @@
                 mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
                         | DisplayDeviceInfo.FLAG_SECURE
                         | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+                mInfo.type = Display.TYPE_BUILT_IN;
                 mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
             }
             return mInfo;
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index d6c5248..b37d57f 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,6 +22,7 @@
 import android.os.Looper;
 import android.os.SystemProperties;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.DisplayEventReceiver;
 import android.view.Surface;
 import android.view.Surface.PhysicalDisplayInfo;
@@ -127,21 +128,25 @@
                 mInfo.height = mPhys.height;
                 mInfo.refreshRate = mPhys.refreshRate;
 
-                // Assume that all built-in displays have secure output (eg. HDCP) and
+                // Assume that all built-in displays that have secure output (eg. HDCP) also
                 // support compositing from gralloc protected buffers.
-                mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
-                        | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+                if (mPhys.secure) {
+                    mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
+                            | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+                }
 
                 if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_built_in_display_name);
                     mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
                             | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+                    mInfo.type = Display.TYPE_BUILT_IN;
                     mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
                     mInfo.xDpi = mPhys.xDpi;
                     mInfo.yDpi = mPhys.yDpi;
                     mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
                 } else {
+                    mInfo.type = Display.TYPE_HDMI;
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_hdmi_display_name);
                     mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index aa7ea82..1583137 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -186,6 +186,11 @@
             if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
                 mBaseDisplayInfo.flags |= Display.FLAG_SUPPORTS_PROTECTED_BUFFERS;
             }
+            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SECURE) != 0) {
+                mBaseDisplayInfo.flags |= Display.FLAG_SECURE;
+            }
+            mBaseDisplayInfo.type = deviceInfo.type;
+            mBaseDisplayInfo.address = deviceInfo.address;
             mBaseDisplayInfo.name = deviceInfo.name;
             mBaseDisplayInfo.appWidth = deviceInfo.width;
             mBaseDisplayInfo.appHeight = deviceInfo.height;
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
index c35fd98..36e9f74 100644
--- a/services/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -27,6 +27,7 @@
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.Slog;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.Surface;
 
@@ -240,6 +241,7 @@
                 mInfo.xDpi = mDensityDpi;
                 mInfo.yDpi = mDensityDpi;
                 mInfo.flags = 0;
+                mInfo.type = Display.TYPE_OVERLAY;
                 mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
             }
             return mInfo;
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 97fc3e6..45fff30 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -39,6 +39,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
+import android.view.Display;
 import android.view.Surface;
 
 import java.io.PrintWriter;
@@ -281,20 +282,22 @@
             scheduleStatusChangedBroadcastLocked();
         }
 
+        boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0;
         int deviceFlags = 0;
-        if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
+        if (secure) {
             deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
-        }
-        if (mSupportsProtectedBuffers) {
-            deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+            if (mSupportsProtectedBuffers) {
+                deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+            }
         }
 
         float refreshRate = 60.0f; // TODO: get this for real
 
         String name = display.getFriendlyDisplayName();
-        IBinder displayToken = Surface.createDisplay(name, false);
+        String address = display.getDeviceAddress();
+        IBinder displayToken = Surface.createDisplay(name, secure);
         mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
-                refreshRate, deviceFlags, surface);
+                refreshRate, deviceFlags, address, surface);
         sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
 
         scheduleUpdateNotificationLocked();
@@ -514,12 +517,13 @@
         private final int mHeight;
         private final float mRefreshRate;
         private final int mFlags;
+        private final String mAddress;
 
         private Surface mSurface;
         private DisplayDeviceInfo mInfo;
 
         public WifiDisplayDevice(IBinder displayToken, String name,
-                int width, int height, float refreshRate, int flags,
+                int width, int height, float refreshRate, int flags, String address,
                 Surface surface) {
             super(WifiDisplayAdapter.this, displayToken);
             mName = name;
@@ -527,6 +531,7 @@
             mHeight = height;
             mRefreshRate = refreshRate;
             mFlags = flags;
+            mAddress = address;
             mSurface = surface;
         }
 
@@ -554,6 +559,8 @@
                 mInfo.height = mHeight;
                 mInfo.refreshRate = mRefreshRate;
                 mInfo.flags = mFlags;
+                mInfo.type = Display.TYPE_WIFI;
+                mInfo.address = mAddress;
                 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
                 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
             }
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index fb93d05..e05442b 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -26,6 +26,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -75,6 +76,7 @@
     private static final String ATTR_SERIAL_NO = "serialNumber";
     private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
     private static final String ATTR_PARTIAL = "partial";
+    private static final String ATTR_USER_VERSION = "version";
     private static final String TAG_USERS = "users";
     private static final String TAG_USER = "user";
 
@@ -84,6 +86,8 @@
 
     private static final int MIN_USER_ID = 10;
 
+    private static final int USER_VERSION = 1;
+
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
     private final Context mContext;
@@ -104,6 +108,7 @@
     // This resets on a reboot. Otherwise it keeps incrementing so that user ids are
     // not reused in quick succession
     private int mNextUserId = MIN_USER_ID;
+    private int mUserVersion = 0;
 
     private static UserManagerService sInstance;
 
@@ -432,12 +437,17 @@
                 if (lastSerialNumber != null) {
                     mNextSerialNumber = Integer.parseInt(lastSerialNumber);
                 }
+                String versionNumber = parser.getAttributeValue(null, ATTR_USER_VERSION);
+                if (versionNumber != null) {
+                    mUserVersion = Integer.parseInt(versionNumber);
+                }
             }
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
                     String id = parser.getAttributeValue(null, ATTR_ID);
                     UserInfo user = readUser(Integer.parseInt(id));
+
                     if (user != null) {
                         mUsers.put(user.id, user);
                         if (user.isGuest()) {
@@ -450,6 +460,7 @@
                 }
             }
             updateUserIdsLocked();
+            upgradeIfNecessary();
         } catch (IOException ioe) {
             fallbackToSingleUserLocked();
         } catch (XmlPullParserException pe) {
@@ -464,9 +475,35 @@
         }
     }
 
+    /**
+     * This fixes an incorrect initialization of user name for the owner.
+     * TODO: Remove in the next release.
+     */
+    private void upgradeIfNecessary() {
+        int userVersion = mUserVersion;
+        if (userVersion < 1) {
+            // Assign a proper name for the owner, if not initialized correctly before
+            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+            if ("Primary".equals(user.name)) {
+                user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
+                writeUserLocked(user);
+            }
+            userVersion = 1;
+        }
+
+        if (userVersion < USER_VERSION) {
+            Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
+                    + USER_VERSION);
+        } else {
+            mUserVersion = userVersion;
+            writeUserListLocked();
+        }
+    }
+
     private void fallbackToSingleUserLocked() {
         // Create the primary user
-        UserInfo primary = new UserInfo(0, "Primary", null,
+        UserInfo primary = new UserInfo(0, 
+                mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
                 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
         mUsers.put(0, primary);
         mNextSerialNumber = MIN_USER_ID;
@@ -547,6 +584,7 @@
 
             serializer.startTag(null, TAG_USERS);
             serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
+            serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
 
             for (int i = 0; i < mUsers.size(); i++) {
                 UserInfo user = mUsers.valueAt(i);
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 4abd8f5..317fec0 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -648,10 +648,10 @@
                 mUsingScreenAutoBrightness = false;
             }
             if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
-                // Dim slowly by at least some minimum amount.
+                // Dim quickly by at least some minimum amount.
                 target = Math.min(target - SCREEN_DIM_MINIMUM_REDUCTION,
                         mScreenBrightnessDimConfig);
-                slow = true;
+                slow = false;
             } else if (wasDim) {
                 // Brighten quickly.
                 slow = false;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 03a0434..039319d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -1566,9 +1566,17 @@
                                 WifiP2pManager.ERROR);
                     }
                     break;
-                /* The supplicant misses the group removed event at times and just
-                 * sends a network disconnect event */
-                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+                /* We do not listen to NETWORK_DISCONNECTION_EVENT for group removal
+                 * handling since supplicant actually tries to reconnect after a temporary
+                 * disconnect until group idle time out. Eventually, a group removal event
+                 * will come when group has been removed.
+                 *
+                 * When there are connectivity issues during temporary disconnect, the application
+                 * will also just remove the group.
+                 *
+                 * Treating network disconnection as group removal causes race conditions since
+                 * supplicant would still maintain the group at that stage.
+                 */
                 case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
                     if (DBG) logd(getName() + " group removed");
                     handleGroupRemoved();