am c665e363: am 0d2adfbb: Potential fix for a couple bugs

* commit 'c665e3637b19b4d49b88d037adf5d101ab39f354':
diff --git a/Android.mk b/Android.mk
index 6e8365f..110117b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -36,8 +36,7 @@
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
 
-# STOPSHIP(kennyguy): change to 21 once the L SDK is baked.
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 21
 
 LOCAL_PACKAGE_NAME := Launcher3
 #LOCAL_CERTIFICATE := shared
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3633c8c..591d9b6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3">
-    <uses-sdk android:targetSdkVersion="19" android:minSdkVersion="16"/>
+    <uses-sdk android:targetSdkVersion="21" android:minSdkVersion="16"/>
 
     <permission
         android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -167,18 +167,18 @@
         <!-- Intent received used to install shortcuts from other applications -->
         <receiver
             android:name="com.android.launcher3.InstallShortcutReceiver"
-            android:permission="com.android.launcher3.permission.INSTALL_SHORTCUT">
+            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
             <intent-filter>
-                <action android:name="com.android.launcher3.action.INSTALL_SHORTCUT" />
+                <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
             </intent-filter>
         </receiver>
 
         <!-- Intent received used to uninstall shortcuts from other applications -->
         <receiver
             android:name="com.android.launcher3.UninstallShortcutReceiver"
-            android:permission="com.android.launcher3.permission.UNINSTALL_SHORTCUT">
+            android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">
             <intent-filter>
-                <action android:name="com.android.launcher3.action.UNINSTALL_SHORTCUT" />
+                <action android:name="com.android.launcher.action.UNINSTALL_SHORTCUT" />
             </intent-filter>
         </receiver>
 
diff --git a/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png
new file mode 100755
index 0000000..d9ad51c
--- /dev/null
+++ b/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png
Binary files differ
diff --git a/WallpaperPicker/src/com/android/launcher3/Partner.java b/WallpaperPicker/src/com/android/launcher3/Partner.java
deleted file mode 100644
index 1753997..0000000
--- a/WallpaperPicker/src/com/android/launcher3/Partner.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.util.Pair;
-
-import java.io.File;
-
-/**
- * Utilities to discover and interact with partner customizations. There can
- * only be one set of customizations on a device, and it must be bundled with
- * the system.
- */
-public class Partner {
-    /** Marker action used to discover partner */
-    private static final String
-            ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
-
-    public static final String RESOURCE_FOLDER = "partner_folder";
-    public static final String RESOURCE_WALLPAPERS = "partner_wallpapers";
-    public static final String RESOURCE_DEFAULT_LAYOUT = "partner_default_layout";
-
-    public static final String RESOURCE_DEFAULT_WALLPAPER_HIDDEN = "default_wallpapper_hidden";
-    public static final String RESOURCE_SYSTEM_WALLPAPER_DIR = "system_wallpaper_directory";
-
-    public static final String RESOURCE_REQUIRE_FIRST_RUN_FLOW = "requires_first_run_flow";
-
-    private static boolean sSearched = false;
-    private static Partner sPartner;
-
-    /**
-     * Find and return partner details, or {@code null} if none exists.
-     */
-    public static synchronized Partner get(PackageManager pm) {
-        if (!sSearched) {
-            Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
-            if (apkInfo != null) {
-                sPartner = new Partner(apkInfo.first, apkInfo.second);
-            }
-            sSearched = true;
-        }
-        return sPartner;
-    }
-
-    private final String mPackageName;
-    private final Resources mResources;
-
-    private Partner(String packageName, Resources res) {
-        mPackageName = packageName;
-        mResources = res;
-    }
-
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    public Resources getResources() {
-        return mResources;
-    }
-
-    public boolean hasDefaultLayout() {
-        int defaultLayout = getResources().getIdentifier(Partner.RESOURCE_DEFAULT_LAYOUT,
-                "xml", getPackageName());
-        return defaultLayout != 0;
-    }
-
-    public boolean hasFolder() {
-        int folder = getResources().getIdentifier(Partner.RESOURCE_FOLDER,
-                "xml", getPackageName());
-        return folder != 0;
-    }
-
-    public boolean hideDefaultWallpaper() {
-        int resId = getResources().getIdentifier(RESOURCE_DEFAULT_WALLPAPER_HIDDEN, "bool",
-                getPackageName());
-        return resId != 0 && getResources().getBoolean(resId);
-    }
-
-    public File getWallpaperDirectory() {
-        int resId = getResources().getIdentifier(RESOURCE_SYSTEM_WALLPAPER_DIR, "string",
-                getPackageName());
-        return (resId != 0) ? new File(getResources().getString(resId)) : null;
-    }
-
-    public boolean requiresFirstRunFlow() {
-        int resId = getResources().getIdentifier(RESOURCE_REQUIRE_FIRST_RUN_FLOW, "bool",
-                getPackageName());
-        return resId != 0 && getResources().getBoolean(resId);
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index cb13291..0728537 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -925,7 +925,7 @@
         Partner partner = Partner.get(pm);
         if (partner != null) {
             final Resources partnerRes = partner.getResources();
-            final int resId = partnerRes.getIdentifier(Partner.RESOURCE_WALLPAPERS, "array",
+            final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array",
                     partner.getPackageName());
             if (resId != 0) {
                 addWallpapers(bundled, partnerRes, partner.getPackageName(), resId);
diff --git a/res/drawable-hdpi/custom_content_page.png b/res/drawable-hdpi/custom_content_page.png
deleted file mode 100644
index 9eef50c..0000000
--- a/res/drawable-hdpi/custom_content_page.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel.9.png b/res/drawable-hdpi/screenpanel.9.png
index eed0f2c..f7ae011 100644
--- a/res/drawable-hdpi/screenpanel.9.png
+++ b/res/drawable-hdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel_hover.9.png b/res/drawable-hdpi/screenpanel_hover.9.png
index 2cea8a4..ac8e83d 100644
--- a/res/drawable-hdpi/screenpanel_hover.9.png
+++ b/res/drawable-hdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-mdpi/custom_content_page.png b/res/drawable-mdpi/custom_content_page.png
deleted file mode 100644
index cc4005d..0000000
--- a/res/drawable-mdpi/custom_content_page.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel.9.png b/res/drawable-mdpi/screenpanel.9.png
index 6f8b7e6..c2779fc 100644
--- a/res/drawable-mdpi/screenpanel.9.png
+++ b/res/drawable-mdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel_hover.9.png b/res/drawable-mdpi/screenpanel_hover.9.png
index 8a94984..70b3078 100644
--- a/res/drawable-mdpi/screenpanel_hover.9.png
+++ b/res/drawable-mdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/custom_content_page.png b/res/drawable-xhdpi/custom_content_page.png
deleted file mode 100644
index e1da91c..0000000
--- a/res/drawable-xhdpi/custom_content_page.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel.9.png b/res/drawable-xhdpi/screenpanel.9.png
index 2d70d7a..53a7812 100644
--- a/res/drawable-xhdpi/screenpanel.9.png
+++ b/res/drawable-xhdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel_hover.9.png b/res/drawable-xhdpi/screenpanel_hover.9.png
index 0032fff..a2e200f 100644
--- a/res/drawable-xhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel.9.png b/res/drawable-xxhdpi/screenpanel.9.png
index 7ed058e..2d13954 100644
--- a/res/drawable-xxhdpi/screenpanel.9.png
+++ b/res/drawable-xxhdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel_hover.9.png b/res/drawable-xxhdpi/screenpanel_hover.9.png
index 24d2266..369fc44 100644
--- a/res/drawable-xxhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xxhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/layout-land/longpress_cling.xml b/res/layout-land/longpress_cling.xml
index d1b30a8..93bbc07 100644
--- a/res/layout-land/longpress_cling.xml
+++ b/res/layout-land/longpress_cling.xml
@@ -4,6 +4,7 @@
     android:id="@+id/longpress_cling"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="@color/cling_scrim_background"
     android:orientation="vertical" >
 
     <Space
diff --git a/res/layout-land/migration_cling.xml b/res/layout-land/migration_cling.xml
index 4765524..307cba8 100644
--- a/res/layout-land/migration_cling.xml
+++ b/res/layout-land/migration_cling.xml
@@ -41,8 +41,9 @@
     </FrameLayout>
 
     <LinearLayout
-        android:layout_width="280dp"
+        android:layout_width="@dimen/cling_migration_content_width"
         android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/cling_migration_content_margin"
         android:orientation="vertical"
         android:paddingLeft="24dp"
         android:paddingRight="24dp" >
diff --git a/res/layout-port/longpress_cling.xml b/res/layout-port/longpress_cling.xml
index d0f1ab7..8e35f5c 100644
--- a/res/layout-port/longpress_cling.xml
+++ b/res/layout-port/longpress_cling.xml
@@ -3,7 +3,8 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
     android:id="@+id/longpress_cling"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
+    android:layout_height="match_parent"
+    android:background="@color/cling_scrim_background" >
 
     <FrameLayout
         android:id="@+id/cling_content"
diff --git a/res/layout-port/migration_cling.xml b/res/layout-port/migration_cling.xml
index 932a9f4..dde8dbc 100644
--- a/res/layout-port/migration_cling.xml
+++ b/res/layout-port/migration_cling.xml
@@ -42,9 +42,10 @@
             android:src="@drawable/ic_migration" />
 
         <LinearLayout
-            android:layout_width="280dp"
+            android:layout_width="@dimen/cling_migration_content_width"
             android:layout_height="wrap_content"
             android:layout_below="@+id/ic_cling_migration"
+            android:layout_marginStart="@dimen/cling_migration_content_margin"
             android:orientation="vertical"
             android:paddingLeft="24dp"
             android:paddingRight="24dp" >
diff --git a/res/layout-sw600dp-port/longpress_cling.xml b/res/layout-sw600dp-port/longpress_cling.xml
index 090adc5..b42d697 100644
--- a/res/layout-sw600dp-port/longpress_cling.xml
+++ b/res/layout-sw600dp-port/longpress_cling.xml
@@ -4,6 +4,7 @@
     android:id="@+id/longpress_cling"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="@color/cling_scrim_background"
     android:orientation="vertical" >
 
     <Space
diff --git a/res/layout/custom_content_page_indicator_marker.xml b/res/layout/custom_content_page_indicator_marker.xml
deleted file mode 100644
index 8fe3f8f..0000000
--- a/res/layout/custom_content_page_indicator_marker.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.launcher3.PageIndicatorMarker
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-    android:layout_width="16dp"
-    android:layout_height="16dp"
-    android:layout_gravity="center_vertical">
-    <ImageView
-        android:id="@+id/inactive"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:src="@drawable/custom_content_page"
-        />
-    <ImageView
-        android:id="@+id/active"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:src="@drawable/custom_content_page"
-        android:alpha="0"
-        android:scaleX="0.5"
-        android:scaleY="0.5"
-        />
-</com.android.launcher3.PageIndicatorMarker>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 68f476d..28679be 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -29,5 +29,7 @@
     <dimen name="cling_migration_logo_width">274dp</dimen>
     <dimen name="cling_migration_bg_size">600dp</dimen>
     <dimen name="cling_migration_bg_shift">-300dp</dimen>
+    <dimen name="cling_migration_content_margin">64dp</dimen>
+    <dimen name="cling_migration_content_width">280dp</dimen>
 
 </resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 9ae155b..8be9964 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -25,4 +25,9 @@
      the drag view should be offset from the position of the original view. -->
     <dimen name="dragViewOffsetX">0dp</dimen>
     <dimen name="dragViewOffsetY">0dp</dimen>
+
+<!-- Cling -->
+    <dimen name="cling_migration_content_margin">96dp</dimen>
+    <dimen name="cling_migration_content_width">320dp</dimen>
+
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 7ddb1e4..29837ea 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -22,6 +22,7 @@
          over the delete target or the info target -->
     <color name="delete_target_hover_tint">#DAFF0000</color>
     <color name="info_target_hover_tint">#DA0099CC</color>
+    <color name="cling_scrim_background">#80000000</color>
 
     <color name="bubble_dark_background">#20000000</color>
     <color name="focused_background">#80c6c5c5</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index befaf37..2c9e689 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -33,6 +33,8 @@
     <dimen name="cling_migration_logo_width">165dp</dimen>
     <dimen name="cling_migration_bg_size">400dp</dimen>
     <dimen name="cling_migration_bg_shift">-200dp</dimen>
+    <dimen name="cling_migration_content_margin">16dp</dimen>
+    <dimen name="cling_migration_content_width">280dp</dimen>
 
 <!-- Workspace -->
     <dimen name="workspace_max_gap">16dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 82b9819..56a205f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -41,21 +41,14 @@
         <item name="android:background">@null</item>
         <item name="android:textColor">@color/quantum_panel_text_color</item>
         <item name="android:drawablePadding">@dimen/dynamic_grid_icon_drawable_padding</item>
-        <item name="android:shadowRadius">2.0</item>
-        <item name="android:shadowDx">0</item>
-        <item name="android:shadowDy">2</item>
-        <item name="android:shadowColor">@color/quantum_panel_text_shadow_color</item>
+        <item name="android:shadowRadius">0</item>
         <item name="customShadows">false</item>
     </style>
 
     <style name="WorkspaceIcon.Folder">
         <item name="android:background">@null</item>
         <item name="android:textColor">@color/quantum_panel_text_color</item>
-        <item name="android:shadowColor">@color/quantum_panel_text_shadow_color</item>
-        <item name="android:shadowRadius">2.0</item>
-        <item name="android:shadowDx">0</item>
-        <item name="android:shadowDy">2</item>
-
+        <item name="android:shadowRadius">0</item>
         <item name="customShadows">false</item>
     </style>
 
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index a92bff1..1bd2907 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -366,7 +366,11 @@
                     // This code triggers requestLayout so must be posted outside of the
                     // layout pass.
                     public void run() {
-                        if (isAttachedToWindow()) {
+                        boolean attached = true;
+                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+                            attached = isAttachedToWindow();
+                        }
+                        if (attached) {
                             setDataIsReady();
                             onDataReady(getMeasuredWidth(), getMeasuredHeight());
                         }
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 4ea7b3d..00f0cf3 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -57,7 +57,7 @@
 
     /** Marker action used to discover a package which defines launcher customization */
     static final String ACTION_LAUNCHER_CUSTOMIZATION =
-            "com.android.launcher3.action.LAUNCHER_CUSTOMIZATION";
+            "android.autoinstalls.config.action.PLAY_AUTO_INSTALL";
 
     private static final String LAYOUT_RES = "default_layout";
 
@@ -298,7 +298,7 @@
                 return -1;
             }
 
-            mValues.put(Favorites.RESTORED, 1);
+            mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINTALL_ICON);
             final Intent intent = new Intent(Intent.ACTION_MAIN, null)
                 .addCategory(Intent.CATEGORY_LAUNCHER)
                 .setComponent(new ComponentName(packageName, className))
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d83f81d..a368796 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -26,7 +26,6 @@
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.KeyEvent;
@@ -50,10 +49,6 @@
     private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
     static final float PADDING_V = 3.0f;
 
-    private static final String TAG = "BubbleTextView";
-
-    private static final boolean DEBUG = false;
-
     private HolographicOutlineHelper mOutlineHelper;
     private Bitmap mPressedBackground;
 
@@ -118,6 +113,11 @@
 
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
             boolean setDefaultPadding) {
+        applyFromShortcutInfo(info, iconCache, setDefaultPadding, false);
+    }
+
+    public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
+            boolean setDefaultPadding, boolean promiseStateChanged) {
         Bitmap b = info.getIcon(iconCache);
         LauncherAppState app = LauncherAppState.getInstance();
 
@@ -135,8 +135,8 @@
         setText(info.title);
         setTag(info);
 
-        if (info.wasPromise) {
-            applyState();
+        if (promiseStateChanged || info.isPromise()) {
+            applyState(promiseStateChanged);
         }
     }
 
@@ -377,31 +377,13 @@
         mLongPressHelper.cancelLongPress();
     }
 
-    public void applyState() {
+    public void applyState(boolean promiseStateChanged) {
         if (getTag() instanceof ShortcutInfo) {
             ShortcutInfo info = (ShortcutInfo) getTag();
-            final int state = info.getState();
-
-            final int progressLevel;
-            if (DEBUG) Log.d(TAG, "applying icon state: " + state);
-
-            switch(state) {
-                case ShortcutInfo.PACKAGE_STATE_DEFAULT:
-                    progressLevel = 100;
-                    break;
-
-                case ShortcutInfo.PACKAGE_STATE_INSTALLING:
-                    setText(R.string.package_state_installing);
-                    progressLevel = info.getProgress();
-                    break;
-
-                case ShortcutInfo.PACKAGE_STATE_ERROR:
-                case ShortcutInfo.PACKAGE_STATE_UNKNOWN:
-                default:
-                    progressLevel = 0;
-                    setText(R.string.package_state_unknown);
-                    break;
-            }
+            final boolean isPromise = info.isPromise();
+            final int progressLevel = isPromise ?
+                    ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
+                            info.getInstallProgress() : 0)) : 100;
 
             Drawable[] drawables = getCompoundDrawables();
             Drawable top = drawables[1];
@@ -415,12 +397,9 @@
                 }
 
                 preloadDrawable.setLevel(progressLevel);
-                if ((state == ShortcutInfo.PACKAGE_STATE_DEFAULT) && info.wasPromise) {
-                    // Clear the promise flag as it is no longer different than a normal shortcut,
-                    // once the animation has been run.
-                    info.wasPromise = !preloadDrawable.maybePerformFinishedAnimation();
+                if (promiseStateChanged) {
+                    preloadDrawable.maybePerformFinishedAnimation();
                 }
-
             }
         }
     }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index d0b3f43..0ff1ef4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -959,10 +959,7 @@
     }
 
     public void setShortcutAndWidgetAlpha(float alpha) {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            getChildAt(i).setAlpha(alpha);
-        }
+        mShortcutsAndWidgets.setAlpha(alpha);
     }
 
     public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 8af2a7f..daf5556 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -69,7 +69,7 @@
     float numRows;
     float numColumns;
     float numHotseatIcons;
-    private float iconSize;
+    float iconSize;
     private float iconTextSize;
     private int iconDrawablePaddingOriginalPx;
     private float hotseatIconSize;
@@ -130,6 +130,9 @@
 
     float dragViewScale;
 
+    int allAppsShortEdgeCount = -1;
+    int allAppsLongEdgeCount = -1;
+
     private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
 
     DeviceProfile(String n, float w, float h, float r, float c,
@@ -152,6 +155,9 @@
         defaultNoAllAppsLayoutId = dnalId;
     }
 
+    DeviceProfile() {
+    }
+
     DeviceProfile(Context context,
                   ArrayList<DeviceProfile> profiles,
                   float minWidth, float minHeight,
@@ -218,6 +224,7 @@
             points.add(new DeviceProfileQuery(p, p.iconSize));
         }
         iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
+
         // AllApps uses the original non-scaled icon size
         allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
 
@@ -240,6 +247,9 @@
         // Hotseat
         hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
 
+        // If the partner customization apk contains any grid overrides, apply them
+        applyPartnerDeviceProfileOverrides(context, dm);
+
         // Calculate the remaining vars
         updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
         updateAvailableDimensions(context);
@@ -247,6 +257,33 @@
     }
 
     /**
+     * Apply any Partner customization grid overrides.
+     *
+     * Currently we support: all apps row / column count.
+     */
+    private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) {
+        Partner p = Partner.get(ctx.getPackageManager());
+        if (p != null) {
+            DeviceProfile partnerDp = p.getDeviceProfileOverride(dm);
+            if (partnerDp != null) {
+                if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) {
+                    numRows = partnerDp.numRows;
+                    numColumns = partnerDp.numColumns;
+                }
+                if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) {
+                    allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount;
+                    allAppsLongEdgeCount = partnerDp.allAppsLongEdgeCount;
+                }
+                if (partnerDp.iconSize > 0) {
+                    iconSize = partnerDp.iconSize;
+                    // AllApps uses the original non-scaled icon size
+                    allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
+                }
+            }
+        }
+    }
+
+    /**
      * Determine the exact visual footprint of the all apps button, taking into account scaling
      * and internal padding of the drawable.
      */
@@ -380,12 +417,17 @@
         int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount);
         int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount);
 
-        allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
-                (allAppsCellHeightPx + allAppsCellPaddingPx);
-        allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
-        allAppsNumCols = (availableWidthPx) /
-                (allAppsCellWidthPx + allAppsCellPaddingPx);
-        allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
+        if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) {
+            allAppsNumRows = isLandscape ? allAppsShortEdgeCount : allAppsLongEdgeCount;
+            allAppsNumCols = isLandscape ? allAppsLongEdgeCount : allAppsShortEdgeCount;
+        } else {
+            allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
+                    (allAppsCellHeightPx + allAppsCellPaddingPx);
+            allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
+            allAppsNumCols = (availableWidthPx) /
+                    (allAppsCellWidthPx + allAppsCellPaddingPx);
+            allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
+        }
     }
 
     void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 7c9e77e..ff02bbb 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -113,12 +113,12 @@
 
     @Override
     public int getIntrinsicWidth() {
-        return getBounds().width();
+        return mBitmap.getWidth();
     }
 
     @Override
     public int getIntrinsicHeight() {
-        return getBounds().height();
+        return mBitmap.getHeight();
     }
 
     @Override
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index a27e519..1890af4 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -461,7 +461,7 @@
 
         Animator openFolderAnim = null;
         final Runnable onCompleteRunnable;
-        if (!Utilities.isLmp()) {
+        if (!Utilities.isLmpOrAbove()) {
             positionAndSizeAsIcon();
             centerAboutIcon();
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 76a85ca..bb71d77 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -29,7 +29,9 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
@@ -61,7 +63,7 @@
     // Empty class name is used for storing package default entry.
     private static final String EMPTY_CLASS_NAME = ".";
 
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private static class CacheEntry {
         public Bitmap icon;
@@ -228,7 +230,8 @@
             Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
             while (it.hasNext()) {
                 final CacheEntry e = it.next().getValue();
-                if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
+                if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx
+                        || e.icon.getHeight() < grid.iconSizePx)) {
                     it.remove();
                 }
             }
@@ -254,18 +257,16 @@
         return getIcon(intent, null, user, true);
     }
 
-    public Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
+    private Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
         synchronized (mCache) {
-            LauncherActivityInfoCompat launcherActInfo =
-                    mLauncherApps.resolveActivity(intent, user);
             ComponentName component = intent.getComponent();
-
             // null info means not installed, but if we have a component from the intent then
             // we should still look in the cache for restored app icons.
             if (component == null) {
                 return getDefaultIcon(user);
             }
 
+            LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);
             CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
             if (title != null) {
                 entry.title = title;
@@ -275,6 +276,32 @@
         }
     }
 
+    /**
+     * Fill in "shortcutInfo" with the icon and label for "info."
+     */
+    public void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, UserHandleCompat user,
+            boolean usePkgIcon) {
+        synchronized (mCache) {
+            ComponentName component = intent.getComponent();
+            // null info means not installed, but if we have a component from the intent then
+            // we should still look in the cache for restored app icons.
+            if (component == null) {
+                shortcutInfo.setIcon(getDefaultIcon(user));
+                shortcutInfo.title = "";
+                shortcutInfo.usingFallbackIcon = true;
+            } else {
+                LauncherActivityInfoCompat launcherActInfo =
+                        mLauncherApps.resolveActivity(intent, user);
+                CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
+
+                shortcutInfo.setIcon(entry.icon);
+                shortcutInfo.title = entry.title;
+                shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
+            }
+        }
+    }
+
+
     public Bitmap getDefaultIcon(UserHandleCompat user) {
         if (!mDefaultIcons.containsKey(user)) {
             mDefaultIcons.put(user, makeDefaultIcon(user));
@@ -332,10 +359,11 @@
                     if (usePackageIcon) {
                         CacheEntry packageEntry = getEntryForPackage(
                                 componentName.getPackageName(), user);
-                        if (packageEntry != null && packageEntry.icon != null) {
+                        if (packageEntry != null) {
                             if (DEBUG) Log.d(TAG, "using package default icon for " +
                                     componentName.toShortString());
                             entry.icon = packageEntry.icon;
+                            entry.title = packageEntry.title;
                         }
                     }
                     if (entry.icon == null) {
@@ -350,6 +378,24 @@
     }
 
     /**
+     * Adds a default package entry in the cache. This entry is not persisted and will be removed
+     * when the cache is flushed.
+     */
+    public void cachePackageInstallInfo(String packageName, UserHandleCompat user,
+            Bitmap icon, CharSequence title) {
+        remove(packageName, user);
+
+        CacheEntry entry = getEntryForPackage(packageName, user);
+        if (!TextUtils.isEmpty(title)) {
+            entry.title = title;
+        }
+        if (icon != null) {
+            entry.icon = Utilities.createIconBitmap(
+                    new BitmapDrawable(mContext.getResources(), icon), mContext);
+        }
+    }
+
+    /**
      * Gets an entry for the package, which can be used as a fallback entry for various components.
      */
     private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
@@ -358,6 +404,7 @@
         CacheEntry entry = mCache.get(cacheKey);
         if (entry == null) {
             entry = new CacheEntry();
+            entry.title = "";
             mCache.put(cacheKey, entry);
 
             try {
@@ -469,7 +516,7 @@
                 Log.w(TAG, "failed to decode pre-load icon for " + key);
             }
         } catch (FileNotFoundException e) {
-            if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key, e);
+            if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key);
         } catch (IOException e) {
             Log.w(TAG, "failed to read pre-load icon for: " + key, e);
         } finally {
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 8f96f74..09b77f7 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -146,10 +146,6 @@
         throw new RuntimeException("Unexpected Intent");
     }
 
-    protected Intent getRestoredIntent() {
-        throw new RuntimeException("Unexpected Intent");
-    }
-
     /**
      * Write the fields of this item to the DB
      * 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ffce116..42ec4fb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -59,6 +59,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -72,6 +73,7 @@
 import android.text.method.TextKeyListener;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
@@ -499,18 +501,7 @@
             showIntroScreen();
         } else {
             showFirstRunActivity();
-        }
-
-        // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
-        // on the device, then we always show the first run cling experience (or if there is no
-        // launcher2). Otherwise, we prompt the user upon started for migration
-        LauncherClings launcherClings = new LauncherClings(this);
-        if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
-            if (mModel.canMigrateFromOldLauncherDb(this)) {
-                launcherClings.showMigrationCling();
-            } else {
-                launcherClings.showLongPressCling(false);
-            }
+            showFirstRunClings();
         }
     }
 
@@ -698,19 +689,20 @@
         }
     }
 
-    /**
-     * Copied from View -- the View version of the method isn't called
-     * anywhere else in our process and only exists for API level 17+,
-     * so it's ok to keep our own version with no API requirement.
-     */
     public static int generateViewId() {
-        for (;;) {
-            final int result = sNextGeneratedId.get();
-            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
-            int newValue = result + 1;
-            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
-            if (sNextGeneratedId.compareAndSet(result, newValue)) {
-                return result;
+        if (Build.VERSION.SDK_INT >= 17) {
+            return View.generateViewId();
+        } else {
+            // View.generateViewId() is not available. The following fallback logic is a copy
+            // of its implementation.
+            for (;;) {
+                final int result = sNextGeneratedId.get();
+                // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+                int newValue = result + 1;
+                if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+                if (sNextGeneratedId.compareAndSet(result, newValue)) {
+                    return result;
+                }
             }
         }
     }
@@ -731,8 +723,13 @@
      * a configuration step, this allows the proper animations to run after other transitions.
      */
     private long completeAdd(PendingAddArguments args) {
+        long screenId = args.screenId;
+        if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+            // When the screen id represents an actual screen (as opposed to a rank) we make sure
+            // that the drop page actually exists.
+            screenId = ensurePendingDropLayoutExists(args.screenId);
+        }
 
-        long screenId = ensurePendingDropLayoutExists(args.screenId);
         switch (args.requestCode) {
             case REQUEST_CREATE_SHORTCUT:
                 completeAddShortcut(args.intent, args.container, screenId, args.cellX,
@@ -824,7 +821,12 @@
                 }
             } else {
                 if (!workspaceLocked) {
-                    mPendingAddInfo.screenId = ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
+                    if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                        // When the screen id represents an actual screen (as opposed to a rank)
+                        // we make sure that the drop page actually exists.
+                        mPendingAddInfo.screenId =
+                                ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
+                    }
                     final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
 
                     dropLayout.setDropPending(true);
@@ -1133,7 +1135,9 @@
     @Override
     public Object onRetainNonConfigurationInstance() {
         // Flag the loader to stop early before switching
-        mModel.stopLoader();
+        if (mModel.isCurrentCallbacks(this)) {
+            mModel.stopLoader();
+        }
         if (mAppsCustomizeContent != null) {
             mAppsCustomizeContent.surrender();
         }
@@ -1660,7 +1664,7 @@
         // TODO(sansid): use the APIs directly when compiling against L sdk.
         // Currently we use reflection to access the flags and the API to set the transparency
         // on the System bars.
-        if (Utilities.isLmp()) {
+        if (Utilities.isLmpOrAbove()) {
             try {
                 getWindow().getAttributes().systemUiVisibility |=
                         (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
@@ -1996,8 +2000,13 @@
 
         // Stop callbacks from LauncherModel
         LauncherAppState app = (LauncherAppState.getInstance());
-        mModel.stopLoader();
-        app.setLauncher(null);
+
+        // It's possible to receive onDestroy after a new Launcher activity has
+        // been created. In this case, don't interfere with the new Launcher.
+        if (mModel.isCurrentCallbacks(this)) {
+            mModel.stopLoader();
+            app.setLauncher(null);
+        }
 
         try {
             mAppWidgetHost.stopListening();
@@ -2555,7 +2564,7 @@
 
     private void showBrokenAppInstallDialog(final String packageName,
             DialogInterface.OnClickListener onSearchClickListener) {
-        new AlertDialog.Builder(this)
+        new AlertDialog.Builder(new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault))
             .setTitle(R.string.abandoned_promises_title)
             .setMessage(R.string.abandoned_promise_explanation)
             .setPositiveButton(R.string.abandoned_search, onSearchClickListener)
@@ -2600,9 +2609,11 @@
         }
 
         // Check for abandoned promise
-        if (shortcut.isAbandoned() && v instanceof BubbleTextView) {
+        if ((v instanceof BubbleTextView)
+                && shortcut.isPromise()
+                && !shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE)) {
             showBrokenAppInstallDialog(
-                    shortcut.getRestoredIntent().getComponent().getPackageName(),
+                    shortcut.getTargetComponent().getPackageName(),
                     new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int id) {
                             startAppShortcutOrInfoActivity(v);
@@ -2817,7 +2828,7 @@
 
             Bundle optsBundle = null;
             if (useLaunchAnimation) {
-                ActivityOptions opts = Utilities.isLmp() ?
+                ActivityOptions opts = Utilities.isLmpOrAbove() ?
                         ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) :
                         ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
                 optsBundle = opts.toBundle();
@@ -2928,7 +2939,7 @@
 
         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
                 scaleX, scaleY);
-        if (Utilities.isLmp()) {
+        if (Utilities.isLmpOrAbove()) {
             oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
         }
         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
@@ -3202,7 +3213,7 @@
             mStateAnimation = null;
         }
 
-        boolean material = Utilities.isLmp();
+        boolean material = Utilities.isLmpOrAbove();
 
         final Resources res = getResources();
 
@@ -3228,7 +3239,10 @@
             mAppsCustomizeTabHost.setContentTypeImmediate(contentType);
         }
 
-        if (animated) {
+        // If for some reason our views aren't initialized, don't animate
+        boolean initialized = getAllAppsButton() != null;
+
+        if (animated && initialized) {
             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
             final AppsCustomizePagedView content = (AppsCustomizePagedView)
                     toView.findViewById(R.id.apps_customize_pane_content);
@@ -3373,9 +3387,6 @@
             final AnimatorSet stateAnimation = mStateAnimation;
             final Runnable startAnimRunnable = new Runnable() {
                 public void run() {
-                    if (!toView.isAttachedToWindow()) {
-                        return;
-                    }
                     // Check that mStateAnimation hasn't changed while
                     // we waited for a layout/draw pass
                     if (mStateAnimation != stateAnimation)
@@ -3384,10 +3395,16 @@
                     dispatchOnLauncherTransitionStart(toView, animated, false);
 
                     revealView.setAlpha(initAlpha);
-                    if (Utilities.isLmp()) {
+                    if (Utilities.isLmpOrAbove()) {
                         for (int i = 0; i < layerViews.size(); i++) {
                             View v = layerViews.get(i);
-                            if (v != null) v.buildLayer();
+                            if (v != null) {
+                                boolean attached = true;
+                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+                                    attached = v.isAttachedToWindow();
+                                }
+                                if (attached) v.buildLayer();
+                            }
                         }
                     }
                     mStateAnimation.start();
@@ -3433,7 +3450,7 @@
             mStateAnimation = null;
         }
 
-        boolean material = Utilities.isLmp();
+        boolean material = Utilities.isLmpOrAbove();
         Resources res = getResources();
 
         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
@@ -3458,7 +3475,10 @@
                     toState, animated, layerViews);
         }
 
-        if (animated) {
+        // If for some reason our views aren't initialized, don't animate
+        boolean initialized = getAllAppsButton() != null;
+
+        if (animated && initialized) {
             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
             if (workspaceAnim != null) {
                 mStateAnimation.play(workspaceAnim);
@@ -3468,6 +3488,15 @@
                     fromView.findViewById(R.id.apps_customize_pane_content);
 
             final View page = content.getPageAt(content.getNextPage());
+
+            // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases
+            int count = content.getChildCount();
+            for (int i = 0; i < count; i++) {
+                View child = content.getChildAt(i);
+                if (child != page) {
+                    child.setVisibility(View.INVISIBLE);
+                }
+            }
             final View revealView = fromView.findViewById(R.id.fake_page);
 
             // hideAppsCustomizeHelper is called in some cases when it is already hidden
@@ -3614,6 +3643,21 @@
                         page.setLayerType(View.LAYER_TYPE_NONE, null);
                     }
                     content.setPageBackgroundsVisible(true);
+                    // Unhide side pages
+                    int count = content.getChildCount();
+                    for (int i = 0; i < count; i++) {
+                        View child = content.getChildAt(i);
+                        child.setVisibility(View.VISIBLE);
+                    }
+
+                    // Reset page transforms
+                    if (page != null) {
+                        page.setTranslationX(0);
+                        page.setTranslationY(0);
+                        page.setAlpha(1);
+                    }
+                    content.setCurrentPage(content.getNextPage());
+
                     mAppsCustomizeContent.updateCurrentPageScroll();
                 }
             });
@@ -3621,9 +3665,6 @@
             final AnimatorSet stateAnimation = mStateAnimation;
             final Runnable startAnimRunnable = new Runnable() {
                 public void run() {
-                    if (!fromView.isAttachedToWindow()) {
-                        return;
-                    }
                     // Check that mStateAnimation hasn't changed while
                     // we waited for a layout/draw pass
                     if (mStateAnimation != stateAnimation)
@@ -3631,10 +3672,16 @@
                     dispatchOnLauncherTransitionStart(fromView, animated, false);
                     dispatchOnLauncherTransitionStart(toView, animated, false);
 
-                    if (Utilities.isLmp()) {
+                    if (Utilities.isLmpOrAbove()) {
                         for (int i = 0; i < layerViews.size(); i++) {
                             View v = layerViews.get(i);
-                            if (v != null) v.buildLayer();
+                            if (v != null) {
+                                boolean attached = true;
+                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+                                    attached = v.isAttachedToWindow();
+                                }
+                                if (attached) v.buildLayer();
+                            }
                         }
                     }
                     mStateAnimation.start();
@@ -4563,6 +4610,7 @@
             mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
         }
         PackageInstallerCompat.getInstance(this).onFinishBind();
+        mModel.recheckRestoredItems(this);
     }
 
     private void sendLoadingCompleteBroadcastIfNecessary() {
@@ -4672,6 +4720,24 @@
     }
 
     /**
+     * Packages were restored
+     */
+    public void bindAppsRestored(final ArrayList<AppInfo> apps) {
+        Runnable r = new Runnable() {
+            public void run() {
+                bindAppsRestored(apps);
+            }
+        };
+        if (waitUntilResume(r)) {
+            return;
+        }
+
+        if (mWorkspace != null) {
+            mWorkspace.updateShortcutsAndWidgets(apps);
+        }
+    }
+
+    /**
      * Update the state of a package, typically related to install state.
      *
      * Implementation of the method from LauncherModel.Callbacks.
@@ -4684,6 +4750,18 @@
     }
 
     /**
+     * Update the label and icon of all the icons in a package
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    @Override
+    public void updatePackageBadge(String packageName) {
+        if (mWorkspace != null) {
+            mWorkspace.updatePackageBadge(packageName, UserHandleCompat.myUserHandle());
+        }
+    }
+
+    /**
      * A package was uninstalled.  We take both the super set of packageNames
      * in addition to specific applications to remove, the reason being that
      * this can be called when a package is updated as well.  In that scenario,
@@ -4940,10 +5018,12 @@
                 @Override
                 public void run() {
                     mDragLayer.dismissOverlayView();
+                    showFirstRunClings();
                 }
             }, ACTIVITY_START_DELAY);
         } else {
             mDragLayer.dismissOverlayView();
+            showFirstRunClings();
         }
         changeWallpaperVisiblity(true);
     }
@@ -4954,6 +5034,20 @@
         editor.apply();
     }
 
+    private void showFirstRunClings() {
+        // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
+        // on the device, then we always show the first run cling experience (or if there is no
+        // launcher2). Otherwise, we prompt the user upon started for migration
+        LauncherClings launcherClings = new LauncherClings(this);
+        if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
+            if (mModel.canMigrateFromOldLauncherDb(this)) {
+                launcherClings.showMigrationCling();
+            } else {
+                launcherClings.showLongPressCling(true);
+            }
+        }
+    }
+
     void showWorkspaceSearchAndHotseat() {
         if (mWorkspace != null) mWorkspace.setAlpha(1f);
         if (mHotseat != null) mHotseat.setAlpha(1f);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4ab4e4b..246278f 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -38,7 +38,7 @@
     private static final String TAG = "LauncherAppState";
     private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
 
-    private static final boolean DEBUG = true;  // STOPSHIP(cwren) temporary for debugging
+    private static final boolean DEBUG = false;
 
     private final AppFilter mAppFilter;
     private final BuildInfo mBuildInfo;
@@ -161,7 +161,7 @@
         return mModel;
     }
 
-    IconCache getIconCache() {
+    public IconCache getIconCache() {
         return mIconCache;
     }
 
@@ -260,4 +260,11 @@
     public void setPackageState(ArrayList<PackageInstallInfo> installInfo) {
         mModel.setPackageState(installInfo);
     }
+
+    /**
+     * Updates the icons and label of all icons for the provided package name.
+     */
+    public void updatePackageBadge(String packageName) {
+        mModel.updatePackageBadge(packageName);
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 50528b1..5c6535a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -46,6 +46,11 @@
     public static final int FLAG_UI_NOT_READY = 4;
 
     /**
+     * Indicates that the widget restore has started.
+     */
+    public static final int FLAG_RESTORE_STARTED = 8;
+
+    /**
      * Indicates that the widget hasn't been instantiated yet.
      */
     static final int NO_ID = -1;
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 7dd8cde..c20c693 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -17,13 +17,16 @@
 package com.android.launcher3;
 
 import android.app.backup.BackupAgentHelper;
+import android.app.backup.BackupDataInput;
 import android.app.backup.BackupManager;
-import android.app.backup.SharedPreferencesBackupHelper;
 import android.content.Context;
-import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
 import android.provider.Settings;
 import android.util.Log;
 
+import java.io.IOException;
+
 public class LauncherBackupAgentHelper extends BackupAgentHelper {
 
     private static final String TAG = "LauncherBackupAgentHelper";
@@ -54,7 +57,7 @@
         // modifies the file outside the normal codepaths, so it looks like another
         // process.  This forces a reload of the file, in case this process persists.
         String spKey = LauncherAppState.getSharedPreferencesKey();
-        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
+        getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
         super.onDestroy();
     }
 
@@ -71,4 +74,21 @@
         addHelper(LauncherBackupHelper.LAUNCHER_PREFIX,
                 new LauncherBackupHelper(this, restoreEnabled));
     }
+
+    @Override
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+            throws IOException {
+        super.onRestore(data, appVersionCode, newState);
+
+        // If no favorite was migrated, clear the data and start fresh.
+        final Cursor c = getContentResolver().query(
+                LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, null, null, null, null);
+        boolean hasData = c.moveToNext();
+        c.close();
+
+        if (!hasData) {
+            if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
+            LauncherAppState.getLauncherProvider().createEmptyDB();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 1ea562b..201f3e9 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -148,11 +148,12 @@
 
     private HashMap<ComponentName, AppWidgetProviderInfo> mWidgetMap;
 
-    private ArrayList<Key> mKeys;
+    private final ArrayList<Key> mKeys;
 
     public LauncherBackupHelper(Context context, boolean restoreEnabled) {
         mContext = context;
         mRestoreEnabled = restoreEnabled;
+        mKeys = new ArrayList<Key>();
     }
 
     private void dataChanged() {
@@ -218,9 +219,6 @@
     @Override
     public void restoreEntity(BackupDataInputStream data) {
         if (VERBOSE) Log.v(TAG, "restoreEntity");
-        if (mKeys == null) {
-            mKeys = new ArrayList<Key>();
-        }
         byte[] buffer = new byte[512];
             String backupKey = data.getKey();
             int dataSize = data.size();
@@ -354,7 +352,7 @@
         try {
             ContentResolver cr = mContext.getContentResolver();
             ContentValues values = unpackFavorite(buffer, 0, dataSize);
-            cr.insert(Favorites.CONTENT_URI, values);
+            cr.insert(Favorites.CONTENT_URI_NO_NOTIFICATION, values);
         } catch (InvalidProtocolBufferNanoException e) {
             Log.e(TAG, "failed to decode favorite", e);
         }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5e8e2ad..c64506d 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -54,12 +54,14 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 
 import java.lang.ref.WeakReference;
 import java.net.URISyntaxException;
+import java.security.InvalidParameterException;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -83,7 +85,8 @@
 public class LauncherModel extends BroadcastReceiver
         implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
     static final boolean DEBUG_LOADERS = false;
-    private static final boolean DEBUG_RECEIVER = true;  // STOPSHIP(cwren) temporary for debugging
+    private static final boolean DEBUG_RECEIVER = false;
+    private static final boolean REMOVE_UNRESTORED_ICONS = true;
 
     static final String TAG = "Launcher.Model";
 
@@ -195,7 +198,9 @@
                                   ArrayList<ItemInfo> addAnimated,
                                   ArrayList<AppInfo> addedApps);
         public void bindAppsUpdated(ArrayList<AppInfo> apps);
+        public void bindAppsRestored(ArrayList<AppInfo> apps);
         public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
+        public void updatePackageBadge(String packageName);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
                         ArrayList<AppInfo> appInfos, UserHandleCompat user);
         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
@@ -345,6 +350,19 @@
         mHandler.post(r);
     }
 
+    public void updatePackageBadge(final String packageName) {
+        // Process the updated package badge
+        Runnable r = new Runnable() {
+            public void run() {
+                Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
+                if (callbacks != null) {
+                    callbacks.updatePackageBadge(packageName);
+                }
+            }
+        };
+        mHandler.post(r);
+    }
+
     public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
         final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
 
@@ -359,7 +377,7 @@
         Iterator<AppInfo> iter = allAppsApps.iterator();
         while (iter.hasNext()) {
             ItemInfo a = iter.next();
-            if (LauncherModel.appWasRestored(ctx, a.getIntent(), a.user)) {
+            if (LauncherModel.appWasPromise(ctx, a.getIntent(), a.user)) {
                 restoredAppsFinal.add((AppInfo) a);
             }
         }
@@ -428,7 +446,7 @@
                         if (LauncherModel.shortcutExists(context, name, launchIntent)) {
                             // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
                             if (a instanceof AppInfo &&
-                                    LauncherModel.appWasRestored(context, launchIntent, a.user)) {
+                                    LauncherModel.appWasPromise(context, launchIntent, a.user)) {
                                 restoredAppsFinal.add((AppInfo) a);
                             }
                             continue;
@@ -884,33 +902,14 @@
     }
 
     /**
-     * Returns true if the shortcuts already exists in the database.
-     * we identify a shortcut by the component name of the intent
-     * and the user.
+     * Returns true if the promise shortcuts with the same package name exists on the workspace.
      */
-    static boolean appWasRestored(Context context, Intent intent, UserHandleCompat user) {
-        final ContentResolver cr = context.getContentResolver();
+    static boolean appWasPromise(Context context, Intent intent, UserHandleCompat user) {
         final ComponentName component = intent.getComponent();
         if (component == null) {
             return false;
         }
-        String componentName = component.flattenToString();
-        String shortName = component.flattenToShortString();
-        long serialNumber = UserManagerCompat.getInstance(context)
-                .getSerialNumberForUser(user);
-        final String where = "(intent glob \"*component=" + componentName + "*\" or " +
-                "intent glob \"*component=" + shortName + "*\")" +
-                "and restored = 1 and profileId = " + serialNumber;
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{"intent", "restored", "profileId"}, where, null, null);
-        boolean result = false;
-        try {
-            result = c.moveToFirst();
-        } finally {
-            c.close();
-        }
-        Log.d(TAG, "shortcutWasRestored is " + result + " for " + componentName);
-        return result;
+        return !getItemsByPackageName(component.getPackageName(), user).isEmpty();
     }
 
     /**
@@ -1077,19 +1076,23 @@
                 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
     }
 
-    /**
-     * Removes all the items from the database corresponding to the specified package.
-     */
-    static void deletePackageFromDatabase(Context context, final String pn,
-            final UserHandleCompat user) {
+    private static ArrayList<ItemInfo> getItemsByPackageName(
+            final String pn, final UserHandleCompat user) {
         ItemInfoFilter filter  = new ItemInfoFilter() {
             @Override
             public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
                 return cn.getPackageName().equals(pn) && info.user.equals(user);
             }
         };
-        ArrayList<ItemInfo> infos = filterItemInfos(sBgItemsIdMap.values(), filter);
-        deleteItemsFromDatabase(context, infos);
+        return filterItemInfos(sBgItemsIdMap.values(), filter);
+    }
+
+    /**
+     * Removes all the items from the database corresponding to the specified package.
+     */
+    static void deletePackageFromDatabase(Context context, final String pn,
+            final UserHandleCompat user) {
+        deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
     }
 
     /**
@@ -1383,6 +1386,10 @@
         return isLaunching;
     }
 
+    public boolean isCurrentCallbacks(Callbacks callbacks) {
+        return (mCallbacks != null && mCallbacks.get() == callbacks);
+    }
+
     public void startLoader(boolean isLaunching, int synchronousBindPage) {
         startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
     }
@@ -1898,10 +1905,12 @@
 
             synchronized (sBgLock) {
                 clearSBgDataStructures();
+                final HashSet<String> installingPkgs = PackageInstallerCompat
+                        .getInstance(mContext).updateAndGetActiveSessionCache();
 
                 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
                 final ArrayList<Long> restoredRows = new ArrayList<Long>();
-                final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
+                final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION;
                 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
                 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
 
@@ -1971,6 +1980,7 @@
                                 intentDescription = c.getString(intentIndex);
                                 long serialNumber = c.getInt(profileIdIndex);
                                 user = mUserManager.getUserForSerialNumber(serialNumber);
+                                int promiseType = c.getInt(restoredIndex);
                                 if (user == null) {
                                     // User has been deleted remove the item.
                                     itemsToRemove.add(id);
@@ -1992,17 +2002,58 @@
                                                 restored = false;
                                             }
                                         } else if (validPkg) {
-                                            // The app is installed but the component is no
-                                            // longer available.
-                                            Launcher.addDumpLog(TAG,
-                                                    "Invalid component removed: " + cn, true);
-                                            itemsToRemove.add(id);
-                                            continue;
+                                            intent = null;
+                                            if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
+                                                // We allow auto install apps to have their intent
+                                                // updated after an install.
+                                                intent = manager.getLaunchIntentForPackage(
+                                                        cn.getPackageName());
+                                                if (intent != null) {
+                                                    ContentValues values = new ContentValues();
+                                                    values.put(LauncherSettings.Favorites.INTENT,
+                                                            intent.toUri(0));
+                                                    String where = BaseColumns._ID + "= ?";
+                                                    String[] args = {Long.toString(id)};
+                                                    contentResolver.update(contentUri, values, where, args);
+                                                }
+                                            }
+
+                                            if (intent == null) {
+                                                // The app is installed but the component is no
+                                                // longer available.
+                                                Launcher.addDumpLog(TAG,
+                                                        "Invalid component removed: " + cn, true);
+                                                itemsToRemove.add(id);
+                                                continue;
+                                            } else {
+                                                // no special handling necessary for this item
+                                                restoredRows.add(id);
+                                                restored = false;
+                                            }
                                         } else if (restored) {
                                             // Package is not yet available but might be
                                             // installed later.
                                             Launcher.addDumpLog(TAG,
                                                     "package not yet restored: " + cn, true);
+
+                                            if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
+                                                // Restore has started once.
+                                            } else if (installingPkgs.contains(cn.getPackageName())) {
+                                                // App restore has started. Update the flag
+                                                promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
+                                                ContentValues values = new ContentValues();
+                                                values.put(LauncherSettings.Favorites.RESTORED,
+                                                        promiseType);
+                                                String where = BaseColumns._ID + "= ?";
+                                                String[] args = {Long.toString(id)};
+                                                contentResolver.update(contentUri, values, where, args);
+
+                                            } else if (REMOVE_UNRESTORED_ICONS) {
+                                                Launcher.addDumpLog(TAG,
+                                                        "Unrestored package removed: " + cn, true);
+                                                itemsToRemove.add(id);
+                                                continue;
+                                            }
                                         } else if (isSdCardReady) {
                                             // Do not wait for external media load anymore.
                                             // Log the invalid package, and remove it
@@ -2024,6 +2075,10 @@
                                             allowMissingTarget = true;
                                             // Add the icon on the workspace anyway.
                                         }
+                                    } else if (cn == null) {
+                                        // For shortcuts with no component, keep them as they are
+                                        restoredRows.add(id);
+                                        restored = false;
                                     }
                                 } catch (URISyntaxException e) {
                                     Launcher.addDumpLog(TAG,
@@ -2036,7 +2091,7 @@
                                         Launcher.addDumpLog(TAG,
                                                 "constructing info for partially restored package",
                                                 true);
-                                        info = getRestoredItemInfo(c, titleIndex, intent);
+                                        info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
                                         intent = getRestoredItemIntent(c, context, intent);
                                     } else {
                                         // Don't restore items for other profiles.
@@ -2206,6 +2261,19 @@
                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                                 component);
                                         appWidgetInfo.restoreStatus = restoreStatus;
+
+                                        if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
+                                            // Restore has started once.
+                                        } else if (installingPkgs.contains(component.getPackageName())) {
+                                            // App restore has started. Update the flag
+                                            appWidgetInfo.restoreStatus |=
+                                                    LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+                                        } else if (REMOVE_UNRESTORED_ICONS) {
+                                            Launcher.addDumpLog(TAG,
+                                                    "Unrestored widget removed: " + component, true);
+                                            itemsToRemove.add(id);
+                                            continue;
+                                        }
                                     }
 
                                     appWidgetInfo.id = id;
@@ -2234,19 +2302,17 @@
                                         break;
                                     }
 
-                                    if (isProviderReady) {
-                                        String providerName = provider.provider.flattenToString();
-                                        if (!providerName.equals(savedProvider) ||
-                                                (appWidgetInfo.restoreStatus != restoreStatus)) {
-                                            ContentValues values = new ContentValues();
-                                            values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
-                                                    providerName);
-                                            values.put(LauncherSettings.Favorites.RESTORED,
-                                                    appWidgetInfo.restoreStatus);
-                                            String where = BaseColumns._ID + "= ?";
-                                            String[] args = {Long.toString(id)};
-                                            contentResolver.update(contentUri, values, where, args);
-                                        }
+                                    String providerName = appWidgetInfo.providerName.flattenToString();
+                                    if (!providerName.equals(savedProvider) ||
+                                            (appWidgetInfo.restoreStatus != restoreStatus)) {
+                                        ContentValues values = new ContentValues();
+                                        values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+                                                providerName);
+                                        values.put(LauncherSettings.Favorites.RESTORED,
+                                                appWidgetInfo.restoreStatus);
+                                        String where = BaseColumns._ID + "= ?";
+                                        String[] args = {Long.toString(id)};
+                                        contentResolver.update(contentUri, values, where, args);
                                     }
                                     sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
                                     sBgAppWidgets.add(appWidgetInfo);
@@ -2271,7 +2337,7 @@
 
                 if (itemsToRemove.size() > 0) {
                     ContentProviderClient client = contentResolver.acquireContentProviderClient(
-                            LauncherSettings.Favorites.CONTENT_URI);
+                            contentUri);
                     // Remove dead items
                     for (long id : itemsToRemove) {
                         if (DEBUG_LOADERS) {
@@ -2289,7 +2355,7 @@
 
                 if (restoredRows.size() > 0) {
                     ContentProviderClient updater = contentResolver.acquireContentProviderClient(
-                            LauncherSettings.Favorites.CONTENT_URI);
+                            contentUri);
                     // Update restored items that no longer require special handling
                     try {
                         StringBuilder selectionBuilder = new StringBuilder();
@@ -2299,7 +2365,7 @@
                         selectionBuilder.append(")");
                         ContentValues values = new ContentValues();
                         values.put(LauncherSettings.Favorites.RESTORED, 0);
-                        updater.update(LauncherSettings.Favorites.CONTENT_URI,
+                        updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
                                 values, selectionBuilder.toString(), null);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Could not update restored rows");
@@ -2879,11 +2945,69 @@
                                 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
                     }
                 }
-            sPendingPackages.clear();
+                sPendingPackages.clear();
             }
         }
     }
 
+    /**
+     * Workaround to re-check unrestored items, in-case they were installed but the Package-ADD
+     * runnable was missed by the launcher.
+     */
+    public void recheckRestoredItems(final Context context) {
+        Runnable r = new Runnable() {
+
+            @Override
+            public void run() {
+                LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+                HashSet<String> installedPackages = new HashSet<String>();
+                UserHandleCompat user = UserHandleCompat.myUserHandle();
+                synchronized(sBgLock) {
+                    for (ItemInfo info : sBgItemsIdMap.values()) {
+                        if (info instanceof ShortcutInfo) {
+                            ShortcutInfo si = (ShortcutInfo) info;
+                            if (si.isPromise() && si.getTargetComponent() != null
+                                    && launcherApps.isPackageEnabledForProfile(
+                                            si.getTargetComponent().getPackageName(), user)) {
+                                installedPackages.add(si.getTargetComponent().getPackageName());
+                            }
+                        } else if (info instanceof LauncherAppWidgetInfo) {
+                            LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) info;
+                            if (widget.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+                                    && launcherApps.isPackageEnabledForProfile(
+                                            widget.providerName.getPackageName(), user)) {
+                                installedPackages.add(widget.providerName.getPackageName());
+                            }
+                        }
+                    }
+                }
+
+                if (!installedPackages.isEmpty()) {
+                    final ArrayList<AppInfo> restoredApps = new ArrayList<AppInfo>();
+                    for (String pkg : installedPackages) {
+                        for (LauncherActivityInfoCompat info : launcherApps.getActivityList(pkg, user)) {
+                            restoredApps.add(new AppInfo(context, info, user, mIconCache, null));
+                        }
+                    }
+
+                    final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
+                    if (!restoredApps.isEmpty()) {
+                        mHandler.post(new Runnable() {
+                            public void run() {
+                                Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+                                if (callbacks == cb && cb != null) {
+                                    callbacks.bindAppsRestored(restoredApps);
+                                }
+                            }
+                        });
+                    }
+
+                }
+            }
+        };
+        sWorker.post(r);
+    }
+
     private class PackageUpdatedTask implements Runnable {
         int mOp;
         String[] mPackages;
@@ -3101,21 +3225,31 @@
      * Make an ShortcutInfo object for a restored application or shortcut item that points
      * to a package that is not yet installed on the system.
      */
-    public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent) {
+    public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
+            int promiseType) {
         final ShortcutInfo info = new ShortcutInfo();
-        if (cursor != null) {
-            info.title =  cursor.getString(titleIndex);
-        } else {
-            info.title = "";
-        }
         info.user = UserHandleCompat.myUserHandle();
+        mIconCache.getTitleAndIcon(info, intent, info.user, true);
+
+        if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
+            String title = (cursor != null) ? cursor.getString(titleIndex) : null;
+            if (!TextUtils.isEmpty(title)) {
+                info.title = title;
+            }
+            info.status = ShortcutInfo.FLAG_RESTORED_ICON;
+        } else if  ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
+            if (TextUtils.isEmpty(info.title)) {
+                info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
+            }
+            info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
+        } else {
+            throw new InvalidParameterException("Invalid restoreType " + promiseType);
+        }
+
         info.contentDescription = mUserManager.getBadgedLabelForUser(
                 info.title.toString(), info.user);
-        info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user, false));
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-        info.restoredIntent = intent;
-        info.wasPromise = true;
-        info.setState(ShortcutInfo.PACKAGE_STATE_UNKNOWN);
+        info.promisedIntent = intent;
         return info;
     }
 
@@ -3230,20 +3364,14 @@
         for (ItemInfo i : infos) {
             if (i instanceof ShortcutInfo) {
                 ShortcutInfo info = (ShortcutInfo) i;
-                ComponentName cn = info.intent.getComponent();
-                if (info.restoredIntent != null) {
-                    cn = info.restoredIntent.getComponent();
-                }
+                ComponentName cn = info.getTargetComponent();
                 if (cn != null && f.filterItem(null, info, cn)) {
                     filtered.add(info);
                 }
             } else if (i instanceof FolderInfo) {
                 FolderInfo info = (FolderInfo) i;
                 for (ShortcutInfo s : info.contents) {
-                    ComponentName cn = s.intent.getComponent();
-                    if (s.restoredIntent != null) {
-                        cn = s.restoredIntent.getComponent();
-                    }
+                    ComponentName cn = s.getTargetComponent();
                     if (cn != null && f.filterItem(info, s, cn)) {
                         filtered.add(s);
                     }
@@ -3287,7 +3415,7 @@
                 return true;
             }
             // placeholder shortcuts get special treatment, let them through too.
-            if (info.getRestoredIntent() != null) {
+            if (info.isPromise()) {
                 return true;
             }
         }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 842e0b0..6cc1688 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -308,6 +308,13 @@
     }
 
     /**
+     * Clears all the data for a fresh start.
+     */
+    synchronized public void createEmptyDB() {
+        mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
+    }
+
+    /**
      * Loads the default workspace based on the following priority scheme:
      *   1) From a package provided by play store
      *   2) From a partner configuration APK, already in the system image
@@ -327,7 +334,7 @@
                 final Partner partner = Partner.get(getContext().getPackageManager());
                 if (partner != null && partner.hasDefaultLayout()) {
                     final Resources partnerRes = partner.getResources();
-                    int workspaceResId = partnerRes.getIdentifier(Partner.RESOURCE_DEFAULT_LAYOUT,
+                    int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                             "xml", partner.getPackageName());
                     if (workspaceResId != 0) {
                         loader = new SimpleWorkspaceLoader(mOpenHelper, partnerRes, workspaceResId);
@@ -908,7 +915,14 @@
             // This shouldn't happen -- throw our hands up in the air and start over.
             Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
                     ". Wiping databse.");
+            createEmptyDB(db);
+        }
 
+
+        /**
+         * Clears all the data for a fresh start.
+         */
+        public void createEmptyDB(SQLiteDatabase db) {
             db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
             db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
             onCreate(db);
@@ -1276,14 +1290,16 @@
                     try {
                         int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
                         values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
-                        if (appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
-                            return true;
+                        if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
+                            return false;
                         }
                     } catch (RuntimeException e) {
                         Log.e(TAG, "Failed to initialize external widget", e);
+                        return false;
                     }
+                } else {
+                    return false;
                 }
-                return false;
             }
 
             // Add screen id if not present
@@ -1509,7 +1525,7 @@
                         final Partner partner = Partner.get(mPackageManager);
                         if (partner != null) {
                             final Resources partnerRes = partner.getResources();
-                            final int resId = partnerRes.getIdentifier(Partner.RESOURCE_FOLDER,
+                            final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
                                     "xml", partner.getPackageName());
                             if (resId != 0) {
                                 final XmlResourceParser partnerParser = partnerRes.getXml(resId);
@@ -1539,24 +1555,11 @@
         }
 
         /**
-         * Parse folder starting at current {@link XmlPullParser} location.
+         * Parse folder items starting at {@link XmlPullParser} location. Allow recursive
+         * includes of items.
          */
-        private boolean loadFolder(SQLiteDatabase db, ContentValues values, Resources res,
-                XmlResourceParser parser) throws IOException, XmlPullParserException {
-            final String title;
-            final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
-            if (titleResId != 0) {
-                title = res.getString(titleResId);
-            } else {
-                title = mContext.getResources().getString(R.string.folder_name);
-            }
-
-            values.put(LauncherSettings.Favorites.TITLE, title);
-            long folderId = addFolder(db, values);
-            boolean added = folderId >= 0;
-
-            ArrayList<Long> folderItems = new ArrayList<Long>();
-
+        private void addToFolder(SQLiteDatabase db, Resources res, XmlResourceParser parser,
+                ArrayList<Long> folderItems, long folderId) throws IOException, XmlPullParserException {
             int type;
             int folderDepth = parser.getDepth();
             while ((type = parser.next()) != XmlPullParser.END_TAG ||
@@ -1586,10 +1589,33 @@
                     if (id >= 0) {
                         folderItems.add(id);
                     }
+                } else if (TAG_INCLUDE.equals(tag) && folderId >= 0) {
+                    addToFolder(db, res, parser, folderItems, folderId);
                 } else {
                     throw new RuntimeException("Folders can contain only shortcuts");
                 }
             }
+        }
+
+        /**
+         * Parse folder starting at current {@link XmlPullParser} location.
+         */
+        private boolean loadFolder(SQLiteDatabase db, ContentValues values, Resources res,
+                XmlResourceParser parser) throws IOException, XmlPullParserException {
+            final String title;
+            final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
+            if (titleResId != 0) {
+                title = res.getString(titleResId);
+            } else {
+                title = mContext.getResources().getString(R.string.folder_name);
+            }
+
+            values.put(LauncherSettings.Favorites.TITLE, title);
+            long folderId = addFolder(db, values);
+            boolean added = folderId >= 0;
+
+            ArrayList<Long> folderItems = new ArrayList<Long>();
+            addToFolder(db, res, parser, folderItems, folderId);
 
             // We can only have folders with >= 2 items, so we need to remove the
             // folder and clean up if less than 2 items were included, or some
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 0434faf..48fc0c9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -594,8 +594,11 @@
 
     private void updatePageIndicator() {
         // Update the page indicator (when we aren't reordering)
-        if (mPageIndicator != null && !isReordering(false)) {
-            mPageIndicator.setActiveMarker(getNextPage());
+        if (mPageIndicator != null) {
+            mPageIndicator.setContentDescription(getPageIndicatorDescription());
+            if (!isReordering(false)) {
+                mPageIndicator.setActiveMarker(getNextPage());
+            }
         }
     }
     protected void pageBeginMoving() {
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
new file mode 100644
index 0000000..e191319
--- /dev/null
+++ b/src/com/android/launcher3/Partner.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.File;
+
+/**
+ * Utilities to discover and interact with partner customizations. There can
+ * only be one set of customizations on a device, and it must be bundled with
+ * the system.
+ */
+public class Partner {
+
+    static final String TAG = "Launcher.Partner";
+
+    /** Marker action used to discover partner */
+    private static final String
+            ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
+
+    public static final String RES_FOLDER = "partner_folder";
+    public static final String RES_WALLPAPERS = "partner_wallpapers";
+    public static final String RES_DEFAULT_LAYOUT = "partner_default_layout";
+
+    public static final String RES_DEFAULT_WALLPAPER_HIDDEN = "default_wallpapper_hidden";
+    public static final String RES_SYSTEM_WALLPAPER_DIR = "system_wallpaper_directory";
+
+    public static final String RES_REQUIRE_FIRST_RUN_FLOW = "requires_first_run_flow";
+
+    /** These resources are used to override the device profile  */
+    public static final String RES_GRID_AA_SHORT_EDGE_COUNT = "grid_aa_short_edge_count";
+    public static final String RES_GRID_AA_LONG_EDGE_COUNT = "grid_aa_long_edge_count";
+    public static final String RES_GRID_NUM_ROWS = "grid_num_rows";
+    public static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
+    public static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
+
+    private static boolean sSearched = false;
+    private static Partner sPartner;
+
+    /**
+     * Find and return partner details, or {@code null} if none exists.
+     */
+    public static synchronized Partner get(PackageManager pm) {
+        if (!sSearched) {
+            Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
+            if (apkInfo != null) {
+                sPartner = new Partner(apkInfo.first, apkInfo.second);
+            }
+            sSearched = true;
+        }
+        return sPartner;
+    }
+
+    private final String mPackageName;
+    private final Resources mResources;
+
+    private Partner(String packageName, Resources res) {
+        mPackageName = packageName;
+        mResources = res;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public Resources getResources() {
+        return mResources;
+    }
+
+    public boolean hasDefaultLayout() {
+        int defaultLayout = getResources().getIdentifier(Partner.RES_DEFAULT_LAYOUT,
+                "xml", getPackageName());
+        return defaultLayout != 0;
+    }
+
+    public boolean hasFolder() {
+        int folder = getResources().getIdentifier(Partner.RES_FOLDER,
+                "xml", getPackageName());
+        return folder != 0;
+    }
+
+    public boolean hideDefaultWallpaper() {
+        int resId = getResources().getIdentifier(RES_DEFAULT_WALLPAPER_HIDDEN, "bool",
+                getPackageName());
+        return resId != 0 && getResources().getBoolean(resId);
+    }
+
+    public File getWallpaperDirectory() {
+        int resId = getResources().getIdentifier(RES_SYSTEM_WALLPAPER_DIR, "string",
+                getPackageName());
+        return (resId != 0) ? new File(getResources().getString(resId)) : null;
+    }
+
+    public boolean requiresFirstRunFlow() {
+        int resId = getResources().getIdentifier(RES_REQUIRE_FIRST_RUN_FLOW, "bool",
+                getPackageName());
+        return resId != 0 && getResources().getBoolean(resId);
+    }
+
+    public DeviceProfile getDeviceProfileOverride(DisplayMetrics dm) {
+        boolean containsProfileOverrides = false;
+
+        DeviceProfile dp = new DeviceProfile();
+
+        // We initialize customizable fields to be invalid
+        dp.numRows = -1;
+        dp.numColumns = -1;
+        dp.allAppsShortEdgeCount = -1;
+        dp.allAppsLongEdgeCount = -1;
+
+        try {
+            int resId = getResources().getIdentifier(RES_GRID_NUM_ROWS,
+                    "integer", getPackageName());
+            if (resId > 0) {
+                containsProfileOverrides = true;
+                dp.numRows = getResources().getInteger(resId);
+            }
+
+            resId = getResources().getIdentifier(RES_GRID_NUM_COLUMNS,
+                    "integer", getPackageName());
+            if (resId > 0) {
+                containsProfileOverrides = true;
+                dp.numColumns = getResources().getInteger(resId);
+            }
+
+            resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT,
+                    "integer", getPackageName());
+            if (resId > 0) {
+                containsProfileOverrides = true;
+                dp.allAppsShortEdgeCount = getResources().getInteger(resId);
+            }
+
+            resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT,
+                    "integer", getPackageName());
+            if (resId > 0) {
+                containsProfileOverrides = true;
+                dp.allAppsLongEdgeCount = getResources().getInteger(resId);
+            }
+
+            resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP,
+                    "dimen", getPackageName());
+            if (resId > 0) {
+                containsProfileOverrides = true;
+                int px = getResources().getDimensionPixelSize(resId);
+                dp.iconSize = DynamicGrid.dpiFromPx(px, dm);
+            }
+        } catch (Resources.NotFoundException ex) {
+            Log.e(TAG, "Invalid Partner grid resource!", ex);
+        }
+        return containsProfileOverrides ? dp : null;
+    }
+}
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
index 1b2d5a4..2972c4f 100644
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -189,11 +189,10 @@
 
     /**
      * Runs the finish animation if it is has not been run after last level change.
-     * @return true if the animation was run.
      */
-    public boolean maybePerformFinishedAnimation() {
+    public void maybePerformFinishedAnimation() {
         if (mAnimationProgress > ANIMATION_PROGRESS_STOPPED) {
-            return false;
+            return;
         }
         if (mAnimator != null) {
             mAnimator.cancel();
@@ -202,7 +201,6 @@
         mAnimator = ObjectAnimator.ofFloat(this, "animationProgress",
                 ANIMATION_PROGRESS_STARTED, ANIMATION_PROGRESS_COMPLETED);
         mAnimator.start();
-        return true;
     }
 
     public void setAnimationProgress(float progress) {
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 612b0a5..daf3434 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -32,17 +33,31 @@
  */
 public class ShortcutInfo extends ItemInfo {
 
-    /** {@link #mState} meaning this package is not installed, and there is no other information. */
-    public static final int PACKAGE_STATE_UNKNOWN = -2;
+    public static final int DEFAULT = 0;
 
-    /** {@link #mState} meaning this package is not installed, because installation failed. */
-    public static final int PACKAGE_STATE_ERROR = -1;
+    /**
+     * The shortcut was restored from a backup and it not ready to be used. This is automatically
+     * set during backup/restore
+     */
+    public static final int FLAG_RESTORED_ICON = 1;
 
-    /** {@link #mState} meaning this package is installed.  This is the typical case. */
-    public static final int PACKAGE_STATE_DEFAULT = 0;
+    /**
+     * The icon was added as an auto-install app, and is not ready to be used. This flag can't
+     * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
+     * parsing.
+     */
+    public static final int FLAG_AUTOINTALL_ICON = 2;
 
-    /** {@link #mState} meaning some external entity has promised to install this package. */
-    public static final int PACKAGE_STATE_INSTALLING = 1;
+    /**
+     * The icon is being installed. If {@link FLAG_RESTORED_ICON} or {@link FLAG_AUTOINTALL_ICON}
+     * is set, then the icon is either being installed or is in a broken state.
+     */
+    public static final int FLAG_INSTALL_SESSION_ACTIVE = 4;
+
+    /**
+     * Indicates that the widget restore has started.
+     */
+    public static final int FLAG_RESTORE_STARTED = 8;
 
     /**
      * The intent used to start the application.
@@ -78,29 +93,29 @@
      */
     boolean isDisabled = false;
 
-    /**
-     * The installation state of the package that this shortcut represents.
-     */
-    protected int mState;
+    int status;
 
     /**
      * The installation progress [0-100] of the package that this shortcut represents.
      */
-    protected int mProgress;
+    private int mInstallProgress;
 
+    /**
+     * Refer {@link AppInfo#firstInstallTime}.
+     */
     long firstInstallTime;
+
+    /**
+     * TODO move this to {@link status}
+     */
     int flags = 0;
 
     /**
      * If this shortcut is a placeholder, then intent will be a market intent for the package, and
      * this will hold the original intent from the database.  Otherwise, null.
+     * Refer {@link #FLAG_RESTORE_PENDING}, {@link #FLAG_INSTALL_PENDING}
      */
-    Intent restoredIntent;
-
-    /**
-     * This is set once to indicate that it was a promise info at some point of its life.
-     */
-    boolean wasPromise = false;
+    Intent promisedIntent;
 
     ShortcutInfo() {
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
@@ -110,21 +125,6 @@
         return intent;
     }
 
-    protected Intent getRestoredIntent() {
-        return restoredIntent;
-    }
-
-    /**
-     * Overwrite placeholder data with restored data, or do nothing if this is not a placeholder.
-     */
-    public void restore() {
-        if (restoredIntent != null) {
-            intent = restoredIntent;
-            restoredIntent = null;
-            mState = PACKAGE_STATE_DEFAULT;
-        }
-    }
-
     ShortcutInfo(Intent intent, CharSequence title, CharSequence contentDescription,
             Bitmap icon, UserHandleCompat user) {
         this();
@@ -149,6 +149,7 @@
         flags = info.flags;
         firstInstallTime = info.firstInstallTime;
         user = info.user;
+        status = info.status;
     }
 
     /** TODO: Remove this.  It's only called by ApplicationInfo.makeShortcut. */
@@ -173,7 +174,7 @@
     }
 
     public void updateIcon(IconCache iconCache) {
-        mIcon = iconCache.getIcon(intent, user);
+        mIcon = iconCache.getIcon(promisedIntent != null ? promisedIntent : intent, user);
         usingFallbackIcon = iconCache.isDefaultIcon(mIcon, user);
     }
 
@@ -184,7 +185,7 @@
         String titleStr = title != null ? title.toString() : null;
         values.put(LauncherSettings.BaseLauncherColumns.TITLE, titleStr);
 
-        String uri = restoredIntent != null ? restoredIntent.toUri(0)
+        String uri = promisedIntent != null ? promisedIntent.toUri(0)
                 : (intent != null ? intent.toUri(0) : null);
         values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri);
 
@@ -224,36 +225,26 @@
         }
     }
 
-    public boolean isPromise() {
-        return restoredIntent != null;
+    public ComponentName getTargetComponent() {
+        return promisedIntent != null ? promisedIntent.getComponent() : intent.getComponent();
     }
 
-    public boolean isPromiseFor(String pkgName) {
-        return restoredIntent != null
-                && pkgName != null
-                && pkgName.equals(restoredIntent.getComponent().getPackageName());
+    public boolean hasStatusFlag(int flag) {
+        return (status & flag) != 0;
     }
 
-    public boolean isAbandoned() {
-        return isPromise()
-                && (mState == PACKAGE_STATE_ERROR
-                        || mState == PACKAGE_STATE_UNKNOWN);
+
+    public final boolean isPromise() {
+        return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON);
     }
 
-    public int getProgress() {
-        return mProgress;
+    public int getInstallProgress() {
+        return mInstallProgress;
     }
 
-    public void setProgress(int progress) {
-        mProgress = progress;
-    }
-
-    public void setState(int state) {
-        mState = state;
-    }
-
-    public int getState() {
-        return mState;
+    public void setInstallProgress(int progress) {
+        mInstallProgress = progress;
+        status |= FLAG_INSTALL_SESSION_ACTIVE;
     }
 }
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index f20f261..1a7c9fc 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -103,11 +103,10 @@
     }
 
     /**
-     * Indicates if the device is running LMP or not.
-     * TODO(sansid): Change the check to a VERSION_CODES code check once we have a version for L.
+     * Indicates if the device is running LMP or higher.
      */
-    public static boolean isLmp() {
-        return "L".equals(Build.VERSION.CODENAME);
+    public static boolean isLmpOrAbove() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
     }
 
     /**
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 732b9ba..774996e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -31,7 +31,10 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -65,6 +68,7 @@
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 
@@ -72,6 +76,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -1102,7 +1107,7 @@
         case MotionEvent.ACTION_UP:
             if (mTouchState == TOUCH_STATE_REST) {
                 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
-                if (!currentPage.lastDownOnOccupiedCell()) {
+                if (currentPage != null && !currentPage.lastDownOnOccupiedCell()) {
                     onWallpaperTap(ev);
                 }
             }
@@ -1266,9 +1271,6 @@
                 mLauncher.updateVoiceButtonProxyVisible(false);
             }
         }
-        if (getPageIndicator() != null) {
-            getPageIndicator().setContentDescription(getPageIndicatorDescription());
-        }
     }
 
     protected CustomContentCallbacks getCustomContentCallbacks() {
@@ -4749,26 +4751,6 @@
         stripEmptyScreens();
     }
 
-    private void updateShortcut(HashMap<ComponentName, AppInfo> appsMap, ItemInfo info,
-                                View child) {
-        ComponentName cn = info.getIntent().getComponent();
-        if (info.getRestoredIntent() != null) {
-            cn = info.getRestoredIntent().getComponent();
-        }
-        if (cn != null) {
-            AppInfo appInfo = appsMap.get(cn);
-            if ((appInfo != null) && LauncherModel.isShortcutInfoUpdateable(info)) {
-                ShortcutInfo shortcutInfo = (ShortcutInfo) info;
-                BubbleTextView shortcut = (BubbleTextView) child;
-                shortcutInfo.restore();
-                shortcutInfo.updateIcon(mIconCache);
-                shortcutInfo.title = appInfo.title.toString();
-                shortcutInfo.contentDescription = appInfo.contentDescription;
-                shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true);
-            }
-        }
-    }
-
     interface ItemOperator {
         /**
          * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
@@ -4819,6 +4801,25 @@
     }
 
     void updateShortcutsAndWidgets(ArrayList<AppInfo> apps) {
+        // Break the appinfo list per user
+        final HashMap<UserHandleCompat, ArrayList<AppInfo>> appsPerUser =
+                new HashMap<UserHandleCompat, ArrayList<AppInfo>>();
+        for (AppInfo info : apps) {
+            ArrayList<AppInfo> filtered = appsPerUser.get(info.user);
+            if (filtered == null) {
+                filtered = new ArrayList<AppInfo>();
+                appsPerUser.put(info.user, filtered);
+            }
+            filtered.add(info);
+        }
+
+        for (Map.Entry<UserHandleCompat, ArrayList<AppInfo>> entry : appsPerUser.entrySet()) {
+            updateShortcutsAndWidgetsPerUser(entry.getValue(), entry.getKey());
+        }
+    }
+
+    private void updateShortcutsAndWidgetsPerUser(ArrayList<AppInfo> apps,
+            final UserHandleCompat user) {
         // Create a map of the apps to test against
         final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
         final HashSet<String> pkgNames = new HashSet<String>();
@@ -4826,14 +4827,78 @@
             appsMap.put(ai.componentName, ai);
             pkgNames.add(ai.componentName.getPackageName());
         }
+        final HashSet<ComponentName> iconsToRemove = new HashSet<ComponentName>();
 
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v, View parent) {
-                if (info instanceof ShortcutInfo) {
-                    updateShortcut(appsMap, info, v);
-                    if (parent != null) {
-                        parent.invalidate();
+                if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+                    ShortcutInfo shortcutInfo = (ShortcutInfo) info;
+                    ComponentName cn = shortcutInfo.getTargetComponent();
+                    AppInfo appInfo = appsMap.get(cn);
+                    if (user.equals(shortcutInfo.user) && cn != null
+                            && LauncherModel.isShortcutInfoUpdateable(info)
+                            && pkgNames.contains(cn.getPackageName())) {
+                        boolean promiseStateChanged = false;
+                        boolean infoUpdated = false;
+                        if (shortcutInfo.isPromise()) {
+                            if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+                                // Auto install icon
+                                PackageManager pm = getContext().getPackageManager();
+                                ResolveInfo matched = pm.resolveActivity(
+                                        new Intent(Intent.ACTION_MAIN)
+                                        .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
+                                        PackageManager.MATCH_DEFAULT_ONLY);
+                                if (matched == null) {
+                                    // Try to find the best match activity.
+                                    Intent intent = pm.getLaunchIntentForPackage(
+                                            cn.getPackageName());
+                                    if (intent != null) {
+                                        cn = intent.getComponent();
+                                        appInfo = appsMap.get(cn);
+                                    }
+
+                                    if ((intent == null) || (appsMap == null)) {
+                                        // Could not find a default activity. Remove this item.
+                                        iconsToRemove.add(shortcutInfo.getTargetComponent());
+
+                                        // process next shortcut.
+                                        return false;
+                                    }
+                                    shortcutInfo.promisedIntent = intent;
+                                }
+                            }
+
+                            // Restore the shortcut.
+                            shortcutInfo.intent = shortcutInfo.promisedIntent;
+                            shortcutInfo.promisedIntent = null;
+                            shortcutInfo.status &= ~ShortcutInfo.FLAG_RESTORED_ICON
+                                    & ~ShortcutInfo.FLAG_AUTOINTALL_ICON
+                                    & ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+
+                            promiseStateChanged = true;
+                            infoUpdated = true;
+                            shortcutInfo.updateIcon(mIconCache);
+                            LauncherModel.updateItemInDatabase(getContext(), shortcutInfo);
+                        }
+
+
+                        if (appInfo != null) {
+                            shortcutInfo.updateIcon(mIconCache);
+                            shortcutInfo.title = appInfo.title.toString();
+                            shortcutInfo.contentDescription = appInfo.contentDescription;
+                            infoUpdated = true;
+                        }
+
+                        if (infoUpdated) {
+                            BubbleTextView shortcut = (BubbleTextView) v;
+                            shortcut.applyFromShortcutInfo(shortcutInfo,
+                                    mIconCache, true, promiseStateChanged);
+
+                            if (parent != null) {
+                                parent.invalidate();
+                            }
+                        }
                     }
                 }
                 // process all the shortcuts
@@ -4841,7 +4906,12 @@
             }
         });
 
-        restorePendingWidgets(pkgNames);
+        if (!iconsToRemove.isEmpty()) {
+            removeItemsByComponentName(iconsToRemove, user);
+        }
+        if (user.equals(UserHandleCompat.myUserHandle())) {
+            restorePendingWidgets(pkgNames);
+        }
     }
 
     public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
@@ -4851,6 +4921,38 @@
         removeItemsByPackageName(packages, user);
     }
 
+    public void updatePackageBadge(final String packageName, final UserHandleCompat user) {
+        mapOverItems(MAP_RECURSE, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
+                if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+                    ShortcutInfo shortcutInfo = (ShortcutInfo) info;
+                    ComponentName cn = shortcutInfo.getTargetComponent();
+                    if (user.equals(shortcutInfo.user) && cn != null
+                            && shortcutInfo.isPromise()
+                            && packageName.equals(cn.getPackageName())) {
+                        if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+                            // For auto install apps update the icon as well as label.
+                            mIconCache.getTitleAndIcon(shortcutInfo,
+                                    shortcutInfo.promisedIntent, user, true);
+                        } else {
+                            // Only update the icon for restored apps.
+                            shortcutInfo.updateIcon(mIconCache);
+                        }
+                        BubbleTextView shortcut = (BubbleTextView) v;
+                        shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
+
+                        if (parent != null) {
+                            parent.invalidate();
+                        }
+                    }
+                }
+                // process all the shortcuts
+                return false;
+            }
+        });
+    }
+
     public void updatePackageState(ArrayList<PackageInstallInfo> installInfos) {
         HashSet<String> completedPackages = new HashSet<String>();
 
@@ -4858,12 +4960,18 @@
             mapOverItems(MAP_RECURSE, new ItemOperator() {
                 @Override
                 public boolean evaluate(ItemInfo info, View v, View parent) {
-                    if (info instanceof ShortcutInfo
-                            && ((ShortcutInfo) info).isPromiseFor(installInfo.packageName)
-                            && v instanceof BubbleTextView) {
-                        ((ShortcutInfo) info).setProgress(installInfo.progress);
-                        ((ShortcutInfo) info).setState(installInfo.state);
-                        ((BubbleTextView)v).applyState();
+                    if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+                        ShortcutInfo si = (ShortcutInfo) info;
+                        ComponentName cn = si.getTargetComponent();
+                        if (si.isPromise() && (cn != null)
+                                && installInfo.packageName.equals(cn.getPackageName())) {
+                            si.setInstallProgress(installInfo.progress);
+                            if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+                                // Mark this info as broken.
+                                si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                            }
+                            ((BubbleTextView)v).applyState(false);
+                        }
                     } else if (v instanceof PendingAppWidgetHostView
                             && info instanceof LauncherAppWidgetInfo
                             && ((LauncherAppWidgetInfo) info).providerName.getPackageName()
@@ -4877,11 +4985,12 @@
                 }
             });
 
-            if (installInfo.state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
+            if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
                 completedPackages.add(installInfo.packageName);
             }
         }
 
+        // Note that package states are sent only for myUser
         if (!completedPackages.isEmpty()) {
             restorePendingWidgets(completedPackages);
         }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 57fac7f..6512d42 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -38,12 +38,11 @@
 
     public static AppWidgetManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
-            // TODO change this to use api version once L gets an API number.
             if (sInstance == null) {
-                if (Utilities.isLmp()) {
-                    sInstance = new AppWidgetManagerCompatVL(context);
+                if (Utilities.isLmpOrAbove()) {
+                    sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
                 } else {
-                    sInstance = new AppWidgetManagerCompatV16(context);
+                    sInstance = new AppWidgetManagerCompatV16(context.getApplicationContext());
                 }
             }
             return sInstance;
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 535c74b..03d43a6 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -43,7 +43,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@TargetApi(Build.VERSION_CODES.L)
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
 
     private final UserManager mUserManager;
@@ -122,11 +122,8 @@
             badgeLocation.offset(bitmap.getWidth() - badgeSize - badgeMargin, top);
         }
 
-        UserManager userManager = (UserManager) mContext.getSystemService(
-                Context.USER_SERVICE);
-
-        Drawable drawable = userManager.getBadgedDrawableForUser(new BitmapDrawable(res, bitmap),
-                info.getProfile(), badgeLocation, 0);
+        Drawable drawable = mPm.getUserBadgedDrawableForDensity(
+                new BitmapDrawable(res, bitmap), info.getProfile(), badgeLocation, 0);
 
         if (drawable instanceof BitmapDrawable) {
             return ((BitmapDrawable) drawable).getBitmap();
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index de8c669..6efcc00 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -23,6 +23,8 @@
 import android.os.Build;
 import android.os.Bundle;
 
+import com.android.launcher3.Utilities;
+
 import java.util.List;
 
 public abstract class LauncherAppsCompat {
@@ -48,12 +50,11 @@
 
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
-            // STOPSHIP(kennyguy) change this to use api version once L gets an API number.
             if (sInstance == null) {
-                if ("L".equals(Build.VERSION.CODENAME)) {
-                    sInstance = new LauncherAppsCompatVL(context);
+                if (Utilities.isLmpOrAbove()) {
+                    sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
                 } else {
-                    sInstance = new LauncherAppsCompatV16(context);
+                    sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
                 }
             }
             return sInstance;
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index c47f223..7e5e6bf 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -22,19 +22,17 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.UserHandle;
 import android.provider.Settings;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 /**
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 6422551..e0d28b5 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -82,7 +82,7 @@
         synchronized (mCallbacks) {
             mCallbacks.put(callback, wrappedCallback);
         }
-        mLauncherApps.addCallback(wrappedCallback);
+        mLauncherApps.registerCallback(wrappedCallback);
     }
 
     public void removeOnAppsChangedCallback(
@@ -92,7 +92,7 @@
             wrappedCallback = mCallbacks.remove(callback);
         }
         if (wrappedCallback != null) {
-            mLauncherApps.removeCallback(wrappedCallback);
+            mLauncherApps.unregisterCallback(wrappedCallback);
         }
     }
 
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 89a2157..0eb8754 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -20,15 +20,21 @@
 
 import com.android.launcher3.Utilities;
 
+import java.util.HashSet;
+
 public abstract class PackageInstallerCompat {
 
+    public static final int STATUS_INSTALLED = 0;
+    public static final int STATUS_INSTALLING = 1;
+    public static final int STATUS_FAILED = 2;
+
     private static final Object sInstanceLock = new Object();
     private static PackageInstallerCompat sInstance;
 
     public static PackageInstallerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isLmp()) {
+                if (Utilities.isLmpOrAbove()) {
                     sInstance = new PackageInstallerCompatVL(context);
                 } else {
                     sInstance = new PackageInstallerCompatV16(context) { };
@@ -38,6 +44,8 @@
         }
     }
 
+    public abstract HashSet<String> updateAndGetActiveSessionCache();
+
     public abstract void onPause();
 
     public abstract void onResume();
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
index 6a2a02e..1910d22 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
@@ -22,7 +22,6 @@
 import android.util.Log;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.ShortcutInfo;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -30,6 +29,7 @@
 import org.json.JSONTokener;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 
 public class PackageInstallerCompatV16 extends PackageInstallerCompat {
 
@@ -107,7 +107,7 @@
         PackageInstallInfo installInfo = new PackageInstallInfo(packageName);
         installInfo.progress = progress;
         installInfo.state = state;
-        if (state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
+        if (state == STATUS_INSTALLED) {
             // no longer necessary to track this package
             editor.remove(packageName);
             if (DEBUG) Log.d(TAG, "no longer tracking " + packageName);
@@ -123,7 +123,7 @@
         if (!mUseQueue) {
             if (mReplayPending) {
                 replayUpdates();
-            } else {
+            } else if (state != STATUS_INSTALLED) {
                 LauncherAppState app = LauncherAppState.getInstanceNoCreate();
                 ArrayList<PackageInstallInfo> update = new ArrayList<PackageInstallInfo>();
                 update.add(installInfo);
@@ -167,4 +167,9 @@
         }
         return value;
     }
+
+    @Override
+    public HashSet<String> updateAndGetActiveSessionCache() {
+        return new HashSet<String>();
+    }
 }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index c78ab99..16ad379 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -23,10 +23,11 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.ShortcutInfo;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 
 public class PackageInstallerCompatVL extends PackageInstallerCompat {
 
@@ -34,18 +35,23 @@
     private static final boolean DEBUG = false;
 
     private final SparseArray<SessionInfo> mPendingReplays = new SparseArray<SessionInfo>();
+    private final HashSet<String> mPendingBadgeUpdates = new HashSet<String>();
     private final PackageInstaller mInstaller;
+    private final IconCache mCache;
 
     private boolean mResumed;
     private boolean mBound;
 
     PackageInstallerCompatVL(Context context) {
         mInstaller = context.getPackageManager().getPackageInstaller();
+        LauncherAppState.setApplicationContext(context.getApplicationContext());
+        mCache = LauncherAppState.getInstance().getIconCache();
 
         mResumed = false;
         mBound = false;
 
-        mInstaller.addSessionCallback(mCallback);
+        mInstaller.registerSessionCallback(mCallback);
+
         // On start, send updates for all active sessions
         for (SessionInfo info : mInstaller.getAllSessions()) {
             mPendingReplays.append(info.getSessionId(), info);
@@ -53,8 +59,28 @@
     }
 
     @Override
+    public HashSet<String> updateAndGetActiveSessionCache() {
+        HashSet<String> activePackages = new HashSet<String>();
+        UserHandleCompat user = UserHandleCompat.myUserHandle();
+        for (SessionInfo info : mInstaller.getAllSessions()) {
+            addSessionInfoToCahce(info, user);
+            if (info.getAppPackageName() != null) {
+                activePackages.add(info.getAppPackageName());
+            }
+        }
+        return activePackages;
+    }
+
+    private void addSessionInfoToCahce(SessionInfo info, UserHandleCompat user) {
+        String packageName = info.getAppPackageName();
+        if (packageName != null) {
+            mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(),
+                    info.getAppLabel());
+        }
+    }
+
+    @Override
     public void onStop() {
-        mInstaller.removeSessionCallback(mCallback);
     }
 
     @Override
@@ -85,7 +111,7 @@
             // Not yet ready
             return;
         }
-        if ((mPendingReplays.size() == 0) && (newInfo == null)) {
+        if ((mPendingReplays.size() == 0) && (newInfo == null) && mPendingBadgeUpdates.isEmpty()) {
             // Nothing to update
             return;
         }
@@ -98,14 +124,14 @@
         }
 
         ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
-        if (newInfo != null) {
+        if ((newInfo != null) && (newInfo.state != STATUS_INSTALLED)) {
             updates.add(newInfo);
         }
-        for (int i = mPendingReplays.size() - 1; i > 0; i--) {
+        for (int i = mPendingReplays.size() - 1; i >= 0; i--) {
             SessionInfo session = mPendingReplays.valueAt(i);
             if (session.getAppPackageName() != null) {
                 updates.add(new PackageInstallInfo(session.getAppPackageName(),
-                        ShortcutInfo.PACKAGE_STATE_INSTALLING,
+                        STATUS_INSTALLING,
                         (int) (session.getProgress() * 100)));
             }
         }
@@ -113,17 +139,20 @@
         if (!updates.isEmpty()) {
             app.setPackageState(updates);
         }
+
+        if (!mPendingBadgeUpdates.isEmpty()) {
+            for (String pkg : mPendingBadgeUpdates) {
+                app.updatePackageBadge(pkg);
+            }
+            mPendingBadgeUpdates.clear();
+        }
     }
 
     private final SessionCallback mCallback = new SessionCallback() {
 
         @Override
         public void onCreated(int sessionId) {
-            SessionInfo session = mInstaller.getSessionInfo(sessionId);
-            if (session != null) {
-                mPendingReplays.put(sessionId, session);
-                replayUpdates(null);
-            }
+            pushSessionBadgeToLauncher(sessionId);
         }
 
         @Override
@@ -131,12 +160,12 @@
             mPendingReplays.remove(sessionId);
             SessionInfo session = mInstaller.getSessionInfo(sessionId);
             if ((session != null) && (session.getAppPackageName() != null)) {
+                mPendingBadgeUpdates.remove(session.getAppPackageName());
                 // Replay all updates with a one time update for this installed package. No
                 // need to store this record for future updates, as the app list will get
                 // refreshed on resume.
                 replayUpdates(new PackageInstallInfo(session.getAppPackageName(),
-                        success ? ShortcutInfo.PACKAGE_STATE_DEFAULT
-                                : ShortcutInfo.PACKAGE_STATE_ERROR, 0));
+                        success ? STATUS_INSTALLED : STATUS_FAILED, 0));
             }
         }
 
@@ -150,10 +179,23 @@
         }
 
         @Override
-        public void onOpened(int sessionId) { }
+        public void onActiveChanged(int sessionId, boolean active) { }
 
         @Override
-        public void onClosed(int sessionId) { }
+        public void onBadgingChanged(int sessionId) {
+            pushSessionBadgeToLauncher(sessionId);
+        }
 
+        private void pushSessionBadgeToLauncher(int sessionId) {
+            SessionInfo session = mInstaller.getSessionInfo(sessionId);
+            if (session != null) {
+                addSessionInfoToCahce(session, UserHandleCompat.myUserHandle());
+                if (session.getAppPackageName() != null) {
+                    mPendingBadgeUpdates.add(session.getAppPackageName());
+                }
+                mPendingReplays.put(sessionId, session);
+                replayUpdates(null);
+            }
+        }
     };
 }
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
index 4baf052..2ae6731 100644
--- a/src/com/android/launcher3/compat/UserHandleCompat.java
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -20,6 +20,8 @@
 import android.os.Build;
 import android.os.UserHandle;
 
+import com.android.launcher3.Utilities;
+
 public class UserHandleCompat {
     private UserHandle mUser;
 
@@ -86,9 +88,8 @@
      * profiles so this is a no-op.
      */
     public void addToIntent(Intent intent, String name) {
-        // TODO change this to use api version once L gets an API number.
-        if ("L".equals(Build.VERSION.CODENAME) && mUser != null) {
+        if (Utilities.isLmpOrAbove() && mUser != null) {
             intent.putExtra(name, mUser);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 8effb81..1374b4e 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -20,6 +20,8 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 
+import com.android.launcher3.Utilities;
+
 import java.util.List;
 
 public abstract class UserManagerCompat {
@@ -27,8 +29,7 @@
     }
 
     public static UserManagerCompat getInstance(Context context) {
-        // TODO change this to use api version once L gets an API number.
-        if ("L".equals(Build.VERSION.CODENAME)) {
+        if (Utilities.isLmpOrAbove()) {
             return new UserManagerCompatVL(context);
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
             return new UserManagerCompatV17(context);
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index ad6f78e..19eeabd 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -18,6 +18,7 @@
 package com.android.launcher3.compat;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -27,9 +28,11 @@
 import java.util.List;
 
 public class UserManagerCompatVL extends UserManagerCompatV17 {
+    private final PackageManager mPm;
 
     UserManagerCompatVL(Context context) {
         super(context);
+        mPm = context.getPackageManager();
     }
 
     @Override
@@ -48,7 +51,7 @@
 
     @Override
     public Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user) {
-        return mUserManager.getBadgedIconForUser(unbadged, user.getUser());
+        return mPm.getUserBadgedIcon(unbadged, user.getUser());
     }
 
     @Override
@@ -56,7 +59,7 @@
         if (user == null) {
             return label;
         }
-        return mUserManager.getBadgedLabelForUser(label, user.getUser());
+        return mPm.getUserBadgedLabel(label, user.getUser());
     }
 }