Adding a rank column for itemInfo

> Rank is used to determine position of an item
in a folder.

Bug: 18590192
Change-Id: I2826a7c570b4cc58e719d685f17a24ec6133804f
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 1890af4..66b6568 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -71,8 +71,6 @@
     static final int STATE_ANIMATING = 1;
     static final int STATE_OPEN = 2;
 
-    private static final int CLOSE_FOLDER_DELAY_MS = 150;
-
     private int mExpandDuration;
     private int mMaterialExpandDuration;
     private int mMaterialExpandStagger;
@@ -342,60 +340,33 @@
         return mInfo;
     }
 
-    private class GridComparator implements Comparator<ShortcutInfo> {
-        int mNumCols;
-        public GridComparator(int numCols) {
-            mNumCols = numCols;
-        }
-
-        @Override
-        public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
-            int lhIndex = lhs.cellY * mNumCols + lhs.cellX;
-            int rhIndex = rhs.cellY * mNumCols + rhs.cellX;
-            return (lhIndex - rhIndex);
-        }
-    }
-
-    private void placeInReadingOrder(ArrayList<ShortcutInfo> items) {
-        int maxX = 0;
-        int count = items.size();
-        for (int i = 0; i < count; i++) {
-            ShortcutInfo item = items.get(i);
-            if (item.cellX > maxX) {
-                maxX = item.cellX;
-            }
-        }
-
-        GridComparator gridComparator = new GridComparator(maxX + 1);
-        Collections.sort(items, gridComparator);
-        final int countX = mContent.getCountX();
-        for (int i = 0; i < count; i++) {
-            int x = i % countX;
-            int y = i / countX;
-            ShortcutInfo item = items.get(i);
-            item.cellX = x;
-            item.cellY = y;
-        }
-    }
-
     void bind(FolderInfo info) {
         mInfo = info;
         ArrayList<ShortcutInfo> children = info.contents;
         ArrayList<ShortcutInfo> overflow = new ArrayList<ShortcutInfo>();
-        setupContentForNumItems(children.size());
-        placeInReadingOrder(children);
-        int count = 0;
+
+        final int totalChildren = children.size();
+        setupContentForNumItems(totalChildren);
+
+        // Arrange children in the grid based on the rank.
+        Collections.sort(children, Utilities.RANK_COMPARATOR);
+        final int countX = mContent.getCountX();
+
+        int visibleChildren = 0;
         for (int i = 0; i < children.size(); i++) {
-            ShortcutInfo child = (ShortcutInfo) children.get(i);
+            ShortcutInfo child = children.get(i);
+            child.cellX = i % countX;
+            child.cellY = i / countX;
+
             if (createAndAddShortcut(child) == null) {
                 overflow.add(child);
             } else {
-                count++;
+                visibleChildren++;
             }
         }
 
         // We rearrange the items in case there are any empty gaps
-        setupContentForNumItems(count);
+        setupContentForNumItems(visibleChildren);
 
         // If our folder has too many items we prune them from the list. This is an issue
         // when upgrading from the old Folders implementation which could contain an unlimited
@@ -414,7 +385,6 @@
         } else {
             mFolderName.setText("");
         }
-        updateItemLocationsInDatabase();
 
         // In case any children didn't come across during loading, clean up the folder accordingly
         mFolderIcon.post(new Runnable() {
@@ -914,22 +884,13 @@
         // Do nothing
     }
 
-    private void updateItemLocationsInDatabase() {
-        ArrayList<View> list = getItemsInReadingOrder();
-        for (int i = 0; i < list.size(); i++) {
-            View v = list.get(i);
-            ItemInfo info = (ItemInfo) v.getTag();
-            LauncherModel.moveItemInDatabase(mLauncher, info, mInfo.id, 0,
-                    info.cellX, info.cellY);
-        }
-    }
-
     private void updateItemLocationsInDatabaseBatch() {
         ArrayList<View> list = getItemsInReadingOrder();
         ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
         for (int i = 0; i < list.size(); i++) {
             View v = list.get(i);
             ItemInfo info = (ItemInfo) v.getTag();
+            info.rank = i;
             items.add(info);
         }
 
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 09b77f7..aff8323 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -98,6 +98,11 @@
     public int minSpanY = 1;
 
     /**
+     * Indicates the position in an ordered list.
+     */
+    public int rank = 0;
+
+    /**
      * Indicates that this item needs to be updated in the db
      */
     public boolean requiresDbUpdate = false;
@@ -135,6 +140,7 @@
         cellY = info.cellY;
         spanX = info.spanX;
         spanY = info.spanY;
+        rank = info.rank;
         screenId = info.screenId;
         itemType = info.itemType;
         container = info.container;
@@ -161,6 +167,7 @@
         values.put(LauncherSettings.Favorites.CELLY, cellY);
         values.put(LauncherSettings.Favorites.SPANX, spanX);
         values.put(LauncherSettings.Favorites.SPANY, spanY);
+        values.put(LauncherSettings.Favorites.RANK, rank);
         long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
         values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber);
 
@@ -170,11 +177,6 @@
         }
     }
 
-    void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) {
-        values.put(LauncherSettings.Favorites.CELLX, cellX);
-        values.put(LauncherSettings.Favorites.CELLY, cellY);
-    }
-
     static byte[] flattenBitmap(Bitmap bitmap) {
         // Try go guesstimate how much space the icon will take when serialized
         // to avoid unnecessary allocations/copies during the write.
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 3868a57..3d7d3f2 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -90,6 +90,11 @@
         if (hasData && mHelper.restoreSuccessful) {
             LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated();
             LauncherClings.synchonouslyMarkFirstRunClingDismissed(this);
+
+            // TODO: Update the backup set to include rank.
+            if (mHelper.restoredBackupVersion <= 2) {
+                LauncherAppState.getLauncherProvider().updateFolderItemsRank();
+            }
         } else {
             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 895a569..4faa8ac 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -19,7 +19,6 @@
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupHelper;
 import android.app.backup.BackupManager;
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -60,7 +59,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.zip.CRC32;
 
 /**
@@ -148,6 +146,7 @@
 
     private DeviceProfieData mCurrentProfile;
     boolean restoreSuccessful;
+    int restoredBackupVersion = 1;
 
     public LauncherBackupHelper(Context context) {
         mContext = context;
@@ -299,6 +298,7 @@
                 MessageNano.mergeFrom(journal, readCheckedBytes(mBuffer, dataSize));
                 applyJournal(journal);
                 restoreSuccessful = isBackupCompatible(journal);
+                restoredBackupVersion = journal.backupVersion;
                 return;
             }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 3c1cf48..95ebaec 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -768,6 +768,7 @@
         values.put(LauncherSettings.Favorites.CONTAINER, item.container);
         values.put(LauncherSettings.Favorites.CELLX, item.cellX);
         values.put(LauncherSettings.Favorites.CELLY, item.cellY);
+        values.put(LauncherSettings.Favorites.RANK, item.rank);
         values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
 
         updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
@@ -801,6 +802,7 @@
             values.put(LauncherSettings.Favorites.CONTAINER, item.container);
             values.put(LauncherSettings.Favorites.CELLX, item.cellX);
             values.put(LauncherSettings.Favorites.CELLY, item.cellY);
+            values.put(LauncherSettings.Favorites.RANK, item.rank);
             values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
 
             contentValues.add(values);
@@ -832,6 +834,7 @@
         values.put(LauncherSettings.Favorites.CONTAINER, item.container);
         values.put(LauncherSettings.Favorites.CELLX, item.cellX);
         values.put(LauncherSettings.Favorites.CELLY, item.cellY);
+        values.put(LauncherSettings.Favorites.RANK, item.rank);
         values.put(LauncherSettings.Favorites.SPANX, item.spanX);
         values.put(LauncherSettings.Favorites.SPANY, item.spanY);
         values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
@@ -845,7 +848,6 @@
     static void updateItemInDatabase(Context context, final ItemInfo item) {
         final ContentValues values = new ContentValues();
         item.onAddToDatabase(context, values);
-        item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
         updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
     }
 
@@ -906,6 +908,7 @@
         final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
         final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
         final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+        final int rankIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.RANK);
         final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
         final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
         final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
@@ -915,6 +918,7 @@
                 ItemInfo item = new ItemInfo();
                 item.cellX = c.getInt(cellXIndex);
                 item.cellY = c.getInt(cellYIndex);
+                item.rank = c.getInt(rankIndex);
                 item.spanX = Math.max(1, c.getInt(spanXIndex));
                 item.spanY = Math.max(1, c.getInt(spanYIndex));
                 item.container = c.getInt(containerIndex);
@@ -1002,7 +1006,6 @@
 
         item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
         values.put(LauncherSettings.Favorites._ID, item.id);
-        item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
 
         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
         Runnable r = new Runnable() {
@@ -1917,6 +1920,8 @@
                             (LauncherSettings.Favorites.SPANX);
                     final int spanYIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.SPANY);
+                    final int rankIndex = c.getColumnIndexOrThrow(
+                            LauncherSettings.Favorites.RANK);
                     final int restoredIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.RESTORED);
                     final int profileIdIndex = c.getColumnIndexOrThrow(
@@ -2102,6 +2107,7 @@
                                     info.screenId = c.getInt(screenIndex);
                                     info.cellX = c.getInt(cellXIndex);
                                     info.cellY = c.getInt(cellYIndex);
+                                    info.rank = c.getInt(rankIndex);
                                     info.spanX = 1;
                                     info.spanY = 1;
                                     info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 9233bcb..a9ad596 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -57,7 +57,7 @@
     private static final boolean LOGD = false;
 
     private static final int MIN_DATABASE_VERSION = 12;
-    private static final int DATABASE_VERSION = 20;
+    private static final int DATABASE_VERSION = 21;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -326,6 +326,10 @@
                 Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
     }
 
+    public void updateFolderItemsRank() {
+        mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false);
+    }
+
     public void deleteDatabase() {
         // Are you sure? (y/n)
         final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
@@ -409,7 +413,8 @@
                     "appWidgetProvider TEXT," +
                     "modified INTEGER NOT NULL DEFAULT 0," +
                     "restored INTEGER NOT NULL DEFAULT 0," +
-                    "profileId INTEGER DEFAULT " + userSerialNumber +
+                    "profileId INTEGER DEFAULT " + userSerialNumber + "," +
+                    "rank INTEGER NOT NULL DEFAULT 0" +
                     ");");
             addWorkspacesTable(db);
 
@@ -585,6 +590,12 @@
                 // else old version remains, which means we wipe old data
             }
 
+            if (version < 21) {
+                if (updateFolderItemsRank(db, true)) {
+                    version  = 21;
+                }
+            }
+
             if (version != DATABASE_VERSION) {
                 Log.w(TAG, "Destroying all old data.");
                 createEmptyDB(db);
@@ -609,6 +620,37 @@
             onCreate(db);
         }
 
+        private boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
+            db.beginTransaction();
+            try {
+                if (addRankColumn) {
+                    // Insert new column for holding rank
+                    db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;");
+                }
+
+                // Get a map for folder ID to folder width
+                Cursor c = db.rawQuery("SELECT container, MAX(cellX) FROM favorites"
+                        + " WHERE container IN (SELECT _id FROM favorites WHERE itemType = ?)"
+                        + " GROUP BY container;",
+                        new String[] {Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)});
+
+                while (c.moveToNext()) {
+                    db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE container=?;",
+                            new Object[] {c.getLong(1) + 1, c.getLong(0)});
+                }
+
+                c.close();
+                db.setTransactionSuccessful();
+            } catch (SQLException ex) {
+                // Old version remains, which means we wipe old data
+                Log.e(TAG, ex.getMessage(), ex);
+                return false;
+            } finally {
+                db.endTransaction();
+            }
+            return true;
+        }
+
         private boolean addProfileColumn(SQLiteDatabase db) {
             db.beginTransaction();
             try {
@@ -1116,6 +1158,8 @@
                         } finally {
                             db.endTransaction();
                         }
+
+                        updateFolderItemsRank(db, false);
                     }
                 } finally {
                     c.close();
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 65e3d28..13fd7ee 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -295,6 +295,7 @@
          * @see android.provider.LiveFolders#DISPLAY_MODE_GRID
          * @see android.provider.LiveFolders#DISPLAY_MODE_LIST
          */
+        @Deprecated
         static final String DISPLAY_MODE = "displayMode";
 
         /**
@@ -302,5 +303,11 @@
          * <P>Type: INTEGER</P>
          */
         static final String RESTORED = "restored";
+
+        /**
+         * Indicates the position of the item inside an auto-arranged view like folder or hotseat.
+         * <p>Type: INTEGER</p>
+         */
+        static final String RANK = "rank";
     }
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 215d63d..1a9b9a1 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -49,6 +49,7 @@
 import android.widget.Toast;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 
 /**
  * Various utilities shared amongst the Launcher's classes.
@@ -542,4 +543,12 @@
         }
         return defaultWidgetForSearchPackage;
     }
+
+    public static final Comparator<ItemInfo> RANK_COMPARATOR = new Comparator<ItemInfo>() {
+
+        @Override
+        public int compare(ItemInfo lhs, ItemInfo rhs) {
+            return lhs.rank - rhs.rank;
+        }
+    };
 }