Merge "Prevent against NPE inside ComponentKey" into ub-launcher3-calgary-polish
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d4223e1..1607a4a 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -862,7 +862,9 @@
                     Intent targetIntent = info.promisedIntent == null
                             ? info.intent : info.promisedIntent;
                     if (targetIntent != null && info.user.equals(user)) {
-                        String s = targetIntent.toUri(0);
+                        Intent copyIntent = new Intent(targetIntent);
+                        copyIntent.setSourceBounds(intent.getSourceBounds());
+                        String s = copyIntent.toUri(0);
                         if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
                             return true;
                         }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 600768e..fd647c7 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -974,6 +974,26 @@
     }
 
     /**
+     * Removes any broken item from the hotseat.
+     * @return a map with occupied hotseat position set to non-null value.
+     */
+    public static LongArrayMap<Object> removeBrokenHotseatItems(Context context) throws Exception {
+        GridSizeMigrationTask task = new GridSizeMigrationTask(context,
+                LauncherAppState.getInstance().getInvariantDeviceProfile(),
+                getValidPackages(context), Integer.MAX_VALUE, Integer.MAX_VALUE);
+
+        // Load all the valid entries
+        ArrayList<DbEntry> items = task.loadHotseatEntries();
+        // Delete any entry marked for deletion by above load.
+        task.applyOperations();
+        LongArrayMap<Object> positions = new LongArrayMap<>();
+        for (DbEntry item : items) {
+            positions.put(item.screenId, item);
+        }
+        return positions;
+    }
+
+    /**
      * Task to run grid migration in multiple steps when the size difference is more than 1.
      */
     protected static class MultiStepMigrationTask {
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index b3f0c82..0d7ba1e 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -68,6 +68,17 @@
             return thisWorkProfile ? 1 : -1;
         }
 
-        return sCollator.compare(label, another.label);
+        int labelCompare = sCollator.compare(label, another.label);
+        if (labelCompare != 0) {
+            return labelCompare;
+        }
+
+        // If the label is same, put the smaller widget before the larger widget. If the area is
+        // also same, put the widget with smaller height before.
+        int thisArea = spanX * spanY;
+        int otherArea = another.spanX * another.spanY;
+        return thisArea == otherArea
+                ? Integer.compare(spanY, another.spanY)
+                : Integer.compare(thisArea, otherArea);
     }
 }
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index 233c3ed..5cb34e8 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -148,7 +148,6 @@
 
         // Set of package names present in hotseat
         final HashSet<String> hotseatTargetApps = new HashSet<>();
-        final LongArrayMap<Intent> hotseatItems = new LongArrayMap<>();
         int maxId = 0;
 
         // Number of imported items on workspace and hotseat
@@ -270,7 +269,6 @@
                     if (intent.getComponent() != null) {
                         intent.setPackage(intent.getComponent().getPackageName());
                     }
-                    hotseatItems.put(screen, intent);
                     hotseatTargetApps.add(getPackage(intent));
                 }
 
@@ -299,7 +297,13 @@
         if (totalItemsOnWorkspace < MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION) {
             throw new Exception("Insufficient data");
         }
+        if (!insertOperations.isEmpty()) {
+            mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                    insertOperations);
+            insertOperations.clear();
+        }
 
+        LongArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
         int myHotseatCount = LauncherAppState.getInstance().getInvariantDeviceProfile().numHotseatIcons;
         if (!FeatureFlags.NO_ALL_APPS_ICON) {
             myHotseatCount--;
@@ -307,14 +311,15 @@
         if (hotseatItems.size() < myHotseatCount) {
             // Insufficient hotseat items. Add a few more.
             HotseatParserCallback parserCallback = new HotseatParserCallback(
-                    hotseatTargetApps, hotseatItems, insertOperations, maxId + 1);
+                    hotseatTargetApps, hotseatItems, insertOperations, maxId + 1, myHotseatCount);
             new HotseatLayoutParser(mContext,
                     parserCallback).loadLayout(null, new ArrayList<Long>());
             mHotseatSize = (int) hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
-        }
-        if (!insertOperations.isEmpty()) {
-            mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
-                    insertOperations);
+
+            if (!insertOperations.isEmpty()) {
+                mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                        insertOperations);
+            }
         }
     }
 
@@ -404,16 +409,18 @@
      */
     private static class HotseatParserCallback implements LayoutParserCallback {
         private final HashSet<String> mExisitingApps;
-        private final LongArrayMap<Intent> mExistingItems;
+        private final LongArrayMap<Object> mExistingItems;
         private final ArrayList<ContentProviderOperation> mOutOps;
+        private final int mRequiredSize;
         private int mStartItemId;
 
         HotseatParserCallback(
-                HashSet<String> existingApps, LongArrayMap<Intent> existingItems,
-                ArrayList<ContentProviderOperation> outOps, int startItemId) {
+                HashSet<String> existingApps, LongArrayMap<Object> existingItems,
+                ArrayList<ContentProviderOperation> outOps, int startItemId, int requiredSize) {
             mExisitingApps = existingApps;
             mExistingItems = existingItems;
             mOutOps = outOps;
+            mRequiredSize = requiredSize;
             mStartItemId = startItemId;
         }
 
@@ -424,6 +431,10 @@
 
         @Override
         public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
+            if (mExistingItems.size() >= mRequiredSize) {
+                // No need to add more items.
+                return 0;
+            }
             Intent intent;
             try {
                 intent = Intent.parseUri(values.getAsString(Favorites.INTENT), 0);