Merge "Support user event logging for drag and drop b/30039490" into ub-launcher3-calgary-polish
diff --git a/src/com/android/launcher3/Alarm.java b/src/com/android/launcher3/Alarm.java
index e9f1fd9..d5b434c 100644
--- a/src/com/android/launcher3/Alarm.java
+++ b/src/com/android/launcher3/Alarm.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.os.Handler;
+import android.os.SystemClock;
 
 public class Alarm implements Runnable{
     // if we reach this time and the alarm hasn't been cancelled, call the listener
@@ -41,9 +42,16 @@
     // Sets the alarm to go off in a certain number of milliseconds. If the alarm is already set,
     // it's overwritten and only the new alarm setting is used
     public void setAlarm(long millisecondsInFuture) {
-        long currentTime = System.currentTimeMillis();
+        long currentTime = SystemClock.uptimeMillis();
         mAlarmPending = true;
+        long oldTriggerTime = mAlarmTriggerTime;
         mAlarmTriggerTime = currentTime + millisecondsInFuture;
+
+        // If the previous alarm was set for a longer duration, cancel it.
+        if (mWaitingForCallback && oldTriggerTime > mAlarmTriggerTime) {
+            mHandler.removeCallbacks(this);
+            mWaitingForCallback = false;
+        }
         if (!mWaitingForCallback) {
             mHandler.postDelayed(this, mAlarmTriggerTime - currentTime);
             mWaitingForCallback = true;
@@ -51,15 +59,14 @@
     }
 
     public void cancelAlarm() {
-        mAlarmTriggerTime = 0;
         mAlarmPending = false;
     }
 
     // this is called when our timer runs out
     public void run() {
         mWaitingForCallback = false;
-        if (mAlarmTriggerTime != 0) {
-            long currentTime = System.currentTimeMillis();
+        if (mAlarmPending) {
+            long currentTime = SystemClock.uptimeMillis();
             if (mAlarmTriggerTime > currentTime) {
                 // We still need to wait some time to trigger spring loaded mode--
                 // post a new callback
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index e0694f3..604b164 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -125,11 +125,8 @@
     }
 
     @Override
-    public String toString() {
-        return "ApplicationInfo(title=" + title + " id=" + this.id
-                + " type=" + this.itemType + " container=" + this.container
-                + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
-                + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
+    protected String dumpProperties() {
+        return super.dumpProperties() + " componentName=" + componentName;
     }
 
     /**
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 9a99852..c0a8caa 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -126,14 +126,6 @@
         public void onItemsChanged(boolean animate);
     }
 
-    @Override
-    public String toString() {
-        return "FolderInfo(id=" + this.id + " type=" + this.itemType
-                + " container=" + this.container + " screen=" + screenId
-                + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
-                + " spanY=" + spanY + ")";
-    }
-
     public boolean hasOption(int optionFlag) {
         return (options & optionFlag) != 0;
     }
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 2a94e55..c0c22a3 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -189,10 +189,24 @@
     }
 
     @Override
-    public String toString() {
-        return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
-            + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
-            + " spanY=" + spanY + " user=" + user + ")";
+    public final String toString() {
+        return getClass().getSimpleName() + "(" + dumpProperties() + ")";
+    }
+
+    protected String dumpProperties() {
+        return "id=" + id
+                + " type=" + itemType
+                + " container=" + container
+                + " screen=" + screenId
+                + " cellX=" + cellX
+                + " cellY=" + cellY
+                + " spanX=" + spanX
+                + " spanY=" + spanY
+                + " minSpanX=" + minSpanX
+                + " minSpanY=" + minSpanY
+                + " rank=" + rank
+                + " user=" + user
+                + " title=" + title;
     }
 
     /**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f125287..c919db8 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2932,7 +2932,8 @@
         try {
             if (Utilities.ATLEAST_MARSHMALLOW && item != null
                     && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
+                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                    && ((ShortcutInfo) item).promisedIntent == null) {
                 // Shortcuts need some special checks due to legacy reasons.
                 startShortcutIntentSafely(intent, optsBundle, item);
             } else if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
@@ -4170,6 +4171,10 @@
             return Collections.EMPTY_LIST;
         }
         ComponentName component = info.getTargetComponent();
+        if (component == null) {
+            return Collections.EMPTY_LIST;
+        }
+
         List<String> ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
         return ids == null ? Collections.EMPTY_LIST : ids;
     }
@@ -4238,7 +4243,7 @@
 
             for (ShortcutInfo si : removed) {
                 if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                    removedDeepShortcuts.add(ShortcutKey.fromItemInfo(si));
+                    removedDeepShortcuts.add(ShortcutKey.fromShortcutInfo(si));
                 } else {
                     removedComponents.add(si.getTargetComponent());
                 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index f22c2a4..66d8957 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -143,8 +143,8 @@
     }
 
     @Override
-    public String toString() {
-        return "AppWidget(id=" + Integer.toString(appWidgetId) + ")";
+    protected String dumpProperties() {
+        return super.dumpProperties() + " appWidgetId=" + appWidgetId;
     }
 
     public final boolean isWidgetIdAllocated() {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 1607a4a..68450e7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -103,7 +103,6 @@
         implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
     static final boolean DEBUG_LOADERS = false;
     private static final boolean DEBUG_RECEIVER = false;
-    private static final boolean REMOVE_UNRESTORED_ICONS = true;
 
     static final String TAG = "Launcher.Model";
 
@@ -931,7 +930,8 @@
                             }
                             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                                 incrementPinnedShortcutCount(
-                                        ShortcutKey.fromItemInfo(item), true /* shouldPin */);
+                                        ShortcutKey.fromShortcutInfo((ShortcutInfo) item),
+                                        true /* shouldPin */);
                             }
                             break;
                         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -1000,7 +1000,8 @@
                                 sBgWorkspaceItems.remove(item);
                                 break;
                             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                                decrementPinnedShortcutCount(ShortcutKey.fromItemInfo(item));
+                                decrementPinnedShortcutCount(ShortcutKey.fromShortcutInfo(
+                                        (ShortcutInfo) item));
                                 // Fall through.
                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
@@ -1881,12 +1882,12 @@
                                                     restored = false;
                                                     itemReplaced = true;
 
-                                                } else if (REMOVE_UNRESTORED_ICONS) {
+                                                } else {
                                                     FileLog.d(TAG, "Unrestored package removed: " + cn);
                                                     itemsToRemove.add(id);
                                                     continue;
                                                 }
-                                            } else if (REMOVE_UNRESTORED_ICONS) {
+                                            } else {
                                                 FileLog.d(TAG, "Unrestored package removed: " + cn);
                                                 itemsToRemove.add(id);
                                                 continue;
@@ -2168,7 +2169,7 @@
                                             // App restore has started. Update the flag
                                             appWidgetInfo.restoreStatus |=
                                                     LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
-                                        } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
+                                        } else if (!isSafeMode) {
                                             FileLog.d(TAG, "Unrestored widget removed: " + component);
                                             itemsToRemove.add(id);
                                             continue;
@@ -3389,7 +3390,8 @@
             for (ItemInfo itemInfo : sBgItemsIdMap) {
                 if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                     ShortcutInfo si = (ShortcutInfo) itemInfo;
-                    if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) {
+                    if (si.getPromisedIntent().getPackage().equals(mPackageName)
+                            && si.user.equals(mUser)) {
                         String shortcutId = si.getDeepShortcutId();
                         if (idsToShortcuts.containsKey(shortcutId)) {
                             idsToWorkspaceShortcutInfos.addToList(shortcutId, si);
@@ -3462,7 +3464,7 @@
                     ShortcutInfo si = (ShortcutInfo) itemInfo;
                     if (isUserUnlocked) {
                         ShortcutInfoCompat shortcut =
-                                pinnedShortcuts.get(ShortcutKey.fromItemInfo(si));
+                                pinnedShortcuts.get(ShortcutKey.fromShortcutInfo(si));
                         // We couldn't verify the shortcut during loader. If its no longer available
                         // (probably due to clear data), delete the workspace item as well
                         if (shortcut == null) {
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index 1aaf85b..31820d7 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -29,4 +29,9 @@
      * The component that will be created.
      */
     public ComponentName componentName;
+
+    @Override
+    protected String dumpProperties() {
+        return super.dumpProperties() + " componentName=" + componentName;
+    }
 }
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 8c466b2..9d7be16 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -167,6 +167,11 @@
         return intent;
     }
 
+    /** Returns {@link #promisedIntent}, or {@link #intent} if promisedIntent is null. */
+    public Intent getPromisedIntent() {
+        return promisedIntent != null ? promisedIntent : intent;
+    }
+
     ShortcutInfo(Intent intent, CharSequence title, CharSequence contentDescription,
             Bitmap icon, UserHandleCompat user) {
         this();
@@ -265,14 +270,6 @@
         }
     }
 
-    @Override
-    public String toString() {
-        return "ShortcutInfo(title=" + title + "intent=" + intent + "id=" + this.id
-                + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId
-                + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
-                + " user=" + user + ")";
-    }
-
     public ComponentName getTargetComponent() {
         return promisedIntent != null ? promisedIntent.getComponent() : intent.getComponent();
     }
@@ -349,7 +346,7 @@
     /** Returns the ShortcutInfo id associated with the deep shortcut. */
     public String getDeepShortcutId() {
         return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
-                intent.getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
+                getPromisedIntent().getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
     }
 
     @Override
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 2988fb9..f2cf120 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -108,6 +108,10 @@
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
     }
 
+    public static boolean isNycOrAbove() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+    }
+
     public static final boolean ATLEAST_MARSHMALLOW =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
 
@@ -156,33 +160,14 @@
         if (isNycOrAbove()) {
             // If the device was scaled, used the original dimensions to determine if rotation
             // is allowed of not.
-            try {
-                // TODO: Use the actual field when the API is finalized.
-                int originalDensity =
-                        DisplayMetrics.class.getField("DENSITY_DEVICE_STABLE").getInt(null);
-                Resources res = context.getResources();
-                int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
-                        * res.getDisplayMetrics().densityDpi / originalDensity;
-                return originalSmallestWidth >= 600;
-            } catch (Exception e) {
-                // Ignore
-            }
+            Resources res = context.getResources();
+            int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
+                    * res.getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEVICE_STABLE;
+            return originalSmallestWidth >= 600;
         }
         return false;
     }
 
-    public static boolean isNycOrAbove() {
-        // TODO(vadimt): Replace using reflection with looking at the API version once
-        // Build.VERSION.SDK_INT gets bumped to 24. b/22942492.
-        try {
-            View.class.getDeclaredField("DRAG_FLAG_OPAQUE");
-            // View.DRAG_FLAG_OPAQUE doesn't exist in M-release, so it's an indication of N+.
-            return true;
-        } catch (NoSuchFieldException e) {
-            return false;
-        }
-    }
-
     public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
         byte[] data = c.getBlob(iconIndex);
         try {
@@ -776,16 +761,21 @@
     }
 
     public static boolean isBootCompleted() {
+        return "1".equals(getSystemProperty("sys.boot_completed", "1"));
+    }
+
+    public static String getSystemProperty(String property, String defaultValue) {
         try {
             Class clazz = Class.forName("android.os.SystemProperties");
             Method getter = clazz.getDeclaredMethod("get", String.class);
-            String value = (String) getter.invoke(null, "sys.boot_completed");
-            return "1".equals(value);
+            String value = (String) getter.invoke(null, property);
+            if (!TextUtils.isEmpty(value)) {
+                return value;
+            }
         } catch (Exception e) {
             Log.d(TAG, "Unable to read system properties");
-            // Assume that boot has completed
-            return true;
         }
+        return defaultValue;
     }
 
     /**
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 06d73c2..66745ab 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -624,8 +624,8 @@
             ViewGroup.LayoutParams lp = qsbContainer.getLayoutParams();
             if (cellHeight > 0 && lp.height != cellHeight) {
                 lp.height = cellHeight;
+                qsbContainer.setLayoutParams(lp);
             }
-            qsbContainer.setLayoutParams(lp);
         }
     }
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 29ed5d9..a5f8dd2 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -32,8 +32,12 @@
     public static UserManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isNycOrAbove()) {
+                if (Utilities.isNycMR1OrAbove()) {
+                    sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
+                } else if (Utilities.isNycOrAbove()) {
                     sInstance = new UserManagerCompatVN(context.getApplicationContext());
+                } else if (Utilities.ATLEAST_MARSHMALLOW) {
+                    sInstance = new UserManagerCompatVM(context.getApplicationContext());
                 } else if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new UserManagerCompatVL(context.getApplicationContext());
                 } else if (Utilities.ATLEAST_JB_MR1) {
@@ -58,4 +62,6 @@
     public abstract long getUserCreationTime(UserHandleCompat user);
     public abstract boolean isQuietModeEnabled(UserHandleCompat user);
     public abstract boolean isUserUnlocked(UserHandleCompat user);
+
+    public abstract boolean isDemoUser();
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index e678ffa..9bd4567 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -60,4 +60,9 @@
     public boolean isUserUnlocked(UserHandleCompat user) {
         return true;
     }
+
+    @Override
+    public boolean isDemoUser() {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index c53d702..2552b0c 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2014 The Android Open Source Project
  *
@@ -94,9 +93,6 @@
 
     @Override
     public long getUserCreationTime(UserHandleCompat user) {
-        if (Utilities.ATLEAST_MARSHMALLOW) {
-            return mUserManager.getUserCreationTime(user.getUser());
-        }
         SharedPreferences prefs = Utilities.getPrefs(mContext);
         String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
         if (!prefs.contains(key)) {
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVM.java b/src/com/android/launcher3/compat/UserManagerCompatVM.java
new file mode 100644
index 0000000..81d67ea
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVM.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.M)
+public class UserManagerCompatVM extends UserManagerCompatVL {
+
+    UserManagerCompatVM(Context context) {
+        super(context);
+    }
+
+    @Override
+    public long getUserCreationTime(UserHandleCompat user) {
+        return mUserManager.getUserCreationTime(user.getUser());
+    }
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
index 771d141..4edac05 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVN.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java
@@ -20,10 +20,10 @@
 import android.content.Context;
 import android.os.Build;
 
-@TargetApi(Build.VERSION_CODES.N)
-public class UserManagerCompatVN extends UserManagerCompatVL {
+import com.android.launcher3.Utilities;
 
-    private static final String TAG = "UserManagerCompatVN";
+@TargetApi(Build.VERSION_CODES.N)
+public class UserManagerCompatVN extends UserManagerCompatVM {
 
     UserManagerCompatVN(Context context) {
         super(context);
@@ -36,12 +36,7 @@
 
     @Override
     public boolean isUserUnlocked(UserHandleCompat user) {
-        // TODO: Remove the try-catch block when the API permission has been relaxed (b/30475753)
-        try {
-            return mUserManager.isUserUnlocked(user.getUser());
-        } catch (RuntimeException e) {
-            return !isQuietModeEnabled(user);
-        }
+        return mUserManager.isUserUnlocked(user.getUser());
     }
 }
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
new file mode 100644
index 0000000..3f64bc8
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.N_MR1)
+public class UserManagerCompatVNMr1 extends UserManagerCompatVN {
+
+    UserManagerCompatVNMr1(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean isDemoUser() {
+        return mUserManager.isDemoUser();
+    }
+}
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index ddc9cbf..c86ba86 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -46,17 +46,12 @@
      */
     public String titleSectionName;
 
-    int flags = 0;
-
     PackageItemInfo(String packageName) {
         this.packageName = packageName;
     }
 
     @Override
-    public String toString() {
-        return "PackageItemInfo(title=" + title + " id=" + this.id
-                + " type=" + this.itemType + " container=" + this.container
-                + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
-                + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
+    protected String dumpProperties() {
+        return super.dumpProperties() + " packageName=" + packageName;
     }
 }
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 9d8b6b3..47bee06 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -24,6 +24,7 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
 
@@ -43,6 +44,13 @@
     private static final String INFO_COLUMN_NAME = "name";
     private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
 
+    /**
+     * When enabled all icons are kept on the home screen, even if they don't have an active
+     * session. To enable use:
+     *      adb shell setprop log.tag.launcher_keep_all_icons VERBOSE
+     */
+    private static final String KEEP_ALL_ICONS = "launcher_keep_all_icons";
+
     public static boolean performRestore(DatabaseHelper helper) {
         SQLiteDatabase db = helper.getWritableDatabase();
         db.beginTransaction();
@@ -77,15 +85,17 @@
         }
 
         // Mark all items as restored.
+        boolean keepAllIcons = Utilities.isPropertyEnabled(KEEP_ALL_ICONS);
         ContentValues values = new ContentValues();
-        values.put(Favorites.RESTORED, 1);
+        values.put(Favorites.RESTORED, ShortcutInfo.FLAG_RESTORED_ICON
+                | (keepAllIcons ? ShortcutInfo.FLAG_RESTORE_STARTED : 0));
         db.update(Favorites.TABLE_NAME, values, null, null);
 
         // Mark widgets with appropriate restore flag
-        values.put(Favorites.RESTORED,
-                LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
-                        LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
-                        LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
+        values.put(Favorites.RESTORED,  LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+                LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+                LauncherAppWidgetInfo.FLAG_UI_NOT_READY |
+                (keepAllIcons ? LauncherAppWidgetInfo.FLAG_RESTORE_STARTED : 0));
         db.update(Favorites.TABLE_NAME, values, "itemType = ?",
                 new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
 
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index 4053030..a219c54 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -3,7 +3,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.ComponentKey;
 
@@ -32,7 +32,7 @@
         return new ShortcutKey(intent.getPackage(), user, shortcutId);
     }
 
-    public static ShortcutKey fromItemInfo(ItemInfo info) {
-        return fromIntent(info.getIntent(), info.user);
+    public static ShortcutKey fromShortcutInfo(ShortcutInfo info) {
+        return fromIntent(info.getPromisedIntent(), info.user);
     }
 }
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 6189bf2..46e9184 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -20,6 +20,7 @@
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
@@ -57,7 +58,7 @@
             @Override
             public boolean matches(ItemInfo info, ComponentName cn) {
                 return info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
-                        keys.contains(ShortcutKey.fromItemInfo(info));
+                        keys.contains(ShortcutKey.fromShortcutInfo((ShortcutInfo) info));
             }
         };
     }
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index a569850..486b18e 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -35,10 +35,4 @@
         componentName = new ComponentName(activityInfo.packageName, activityInfo.name);
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
-
-    @Override
-    public String toString() {
-        return String.format("PendingAddShortcutInfo package=%s, name=%s",
-                activityInfo.packageName, activityInfo.name);
-    }
 }
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index de06ab6..f800ac4 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -59,10 +59,4 @@
     public boolean isCustomWidget() {
         return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
     }
-
-    @Override
-    public String toString() {
-        return String.format("PendingAddWidgetInfo package=%s, name=%s",
-                componentName.getPackageName(), componentName.getShortClassName());
-    }
 }
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 02a1913..f9e53ba 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -25,6 +25,15 @@
             }
             SystemClock.sleep(sleepMillis);
         }
+
+        // Check once more before returning false.
+        try {
+            if (condition.isTrue()) {
+                return true;
+            }
+        } catch (Throwable t) {
+            // Ignore
+        }
         return false;
     }
 }