1e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalpackage com.android.launcher3.model;
2e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
3e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.content.ComponentName;
4e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.content.ContentProviderOperation;
5e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.content.ContentValues;
6e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.content.Context;
7e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.content.Intent;
8e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.content.SharedPreferences;
9e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.content.pm.PackageInfo;
10a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyalimport android.content.pm.PackageManager;
11e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.database.Cursor;
12e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.graphics.Point;
13f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyalimport android.net.Uri;
14e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.text.TextUtils;
15e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport android.util.Log;
16e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
17e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.InvariantDeviceProfile;
18e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.ItemInfo;
19e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.LauncherAppState;
20e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.LauncherAppWidgetProviderInfo;
21e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.LauncherModel;
22e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.LauncherProvider;
23e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.LauncherSettings;
24e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.LauncherSettings.Favorites;
25e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.Utilities;
26da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyalimport com.android.launcher3.Workspace;
272e1efb480a9b77a97cb623d4f5faf6802a417422Sunny Goyalimport com.android.launcher3.compat.AppWidgetManagerCompat;
28e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.compat.PackageInstallerCompat;
29a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyalimport com.android.launcher3.config.FeatureFlags;
30ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyalimport com.android.launcher3.util.GridOccupancy;
31e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport com.android.launcher3.util.LongArrayMap;
32e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
33e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport java.util.ArrayList;
34e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport java.util.Collections;
35e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport java.util.HashMap;
36e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyalimport java.util.HashSet;
37f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyalimport java.util.Locale;
38e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
39e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal/**
40e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
41f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal * result of restoring from a larger device or device density change.
42e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal */
43f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyalpublic class GridSizeMigrationTask {
44e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
45f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    public static boolean ENABLED = Utilities.isNycOrAbove();
466579e1eee8a6fce44f020d40c3bbdbf245d6c12cSunny Goyal
47f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    private static final String TAG = "GridSizeMigrationTask";
48d934e0b0b7b60c9457fd0eb615355c16bac1a285Sunny Goyal    private static final boolean DEBUG = true;
49e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
50f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    private static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
51bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal    private static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
52f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
53e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    // These are carefully selected weights for various item types (Math.random?), to allow for
54f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    // the least absurd migration experience.
55e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private static final float WT_SHORTCUT = 1;
56e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private static final float WT_APPLICATION = 0.8f;
57e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private static final float WT_WIDGET_MIN = 2;
58e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private static final float WT_WIDGET_FACTOR = 0.6f;
59e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private static final float WT_FOLDER_FACTOR = 0.5f;
60e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
61e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private final Context mContext;
62e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private final InvariantDeviceProfile mIdp;
63e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
64f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    private final HashMap<String, Point> mWidgetMinSize = new HashMap<>();
65f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    private final ContentValues mTempValues = new ContentValues();
66a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected final ArrayList<Long> mEntryToRemove = new ArrayList<>();
67f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
68a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
69f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    private final HashSet<String> mValidPackages;
70e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
71e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private final int mSrcX, mSrcY;
72f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    private final int mTrgX, mTrgY;
73e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private final boolean mShouldRemoveX, mShouldRemoveY;
74e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
75f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    private final int mSrcHotseatSize;
76f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    private final int mDestHotseatSize;
77f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
78f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp,
79eb77aaea8990ede3ba774c7b92d48d098bda0f24Sunny Goyal            HashSet<String> validPackages, Point sourceSize, Point targetSize) {
80e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        mContext = context;
81f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mValidPackages = validPackages;
82f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mIdp = idp;
83e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
84e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        mSrcX = sourceSize.x;
85e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        mSrcY = sourceSize.y;
86e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
87f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mTrgX = targetSize.x;
88f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mTrgY = targetSize.y;
89e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
90e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        mShouldRemoveX = mTrgX < mSrcX;
91e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        mShouldRemoveY = mTrgY < mSrcY;
92f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
93f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        // Non-used variables
94bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal        mSrcHotseatSize = mDestHotseatSize = -1;
95e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
96e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
97f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    protected GridSizeMigrationTask(Context context,
98f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            InvariantDeviceProfile idp, HashSet<String> validPackages,
99bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal            int srcHotseatSize, int destHotseatSize) {
100f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mContext = context;
101f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mIdp = idp;
102f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mValidPackages = validPackages;
103e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
104f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mSrcHotseatSize = srcHotseatSize;
105e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
106f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mDestHotseatSize = destHotseatSize;
107f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
108f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        // Non-used variables
109f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mSrcX = mSrcY = mTrgX = mTrgY = -1;
110f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        mShouldRemoveX = mShouldRemoveY = false;
111f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    }
112f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
113f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    /**
114f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * Applied all the pending DB operations
115f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * @return true if any DB operation was commited.
116f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     */
117f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    private boolean applyOperations() throws Exception {
118f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        // Update items
119f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        if (!mUpdateOperations.isEmpty()) {
120f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations);
121f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        }
122f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
123f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        if (!mEntryToRemove.isEmpty()) {
124f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            if (DEBUG) {
125f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove));
126f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            }
127f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
128f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    Utilities.createDbSelectionQuery(
129f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                            LauncherSettings.Favorites._ID, mEntryToRemove), null);
130f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        }
131f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
132f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        return !mUpdateOperations.isEmpty() || !mEntryToRemove.isEmpty();
133f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    }
134f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
135f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    /**
136f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal     * To migrate hotseat, we load all the entries in order (LTR or RTL) and arrange them
137f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal     * in the order in the new hotseat while keeping an empty space for all-apps. If the number of
138f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal     * entries is more than what can fit in the new hotseat, we drop the entries with least weight.
139f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal     * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION}
140f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal     * & {@see #WT_FOLDER_FACTOR}.
141f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * @return true if any DB change was made
142f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal     */
143f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    protected boolean migrateHotseat() throws Exception {
144f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        ArrayList<DbEntry> items = loadHotseatEntries();
145f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
146803896767b37e447bcdff7be6b9872612e93b7bfSunny Goyal        int requiredCount = FeatureFlags.NO_ALL_APPS_ICON ? mDestHotseatSize : mDestHotseatSize - 1;
147f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
148f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        while (items.size() > requiredCount) {
149f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            // Pick the center item by default.
150f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            DbEntry toRemove = items.get(items.size() / 2);
151f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
152f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            // Find the item with least weight.
153f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            for (DbEntry entry : items) {
154f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                if (entry.weight < toRemove.weight) {
155f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    toRemove = entry;
156f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                }
157f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            }
158f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
159f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            mEntryToRemove.add(toRemove.id);
160f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            items.remove(toRemove);
161f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        }
162f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
163f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        // Update screen IDS
164f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        int newScreenId = 0;
165f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        for (DbEntry entry : items) {
166f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            if (entry.screenId != newScreenId) {
167f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                entry.screenId = newScreenId;
168f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
169f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                // These values does not affect the item position, but we should set them
170f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                // to something other than -1.
171f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                entry.cellX = newScreenId;
172f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                entry.cellY = 0;
173f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
174f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                update(entry);
175f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            }
176f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
177f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            newScreenId++;
178bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal            if (!FeatureFlags.NO_ALL_APPS_ICON && mIdp.isAllAppsButtonRank(newScreenId)) {
179f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                newScreenId++;
180f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            }
181f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        }
182f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
183f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        return applyOperations();
184f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    }
185f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
186f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    /**
187f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * @return true if any DB change was made
188f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     */
189f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    protected boolean migrateWorkspace() throws Exception {
190e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
191e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        if (allScreens.isEmpty()) {
192e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            throw new Exception("Unable to get workspace screens");
193e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
194e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
195e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        for (long screenId : allScreens) {
196e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if (DEBUG) {
197e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                Log.d(TAG, "Migrating " + screenId);
198e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
199e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            migrateScreen(screenId);
200e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
201e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
202e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        if (!mCarryOver.isEmpty()) {
203e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
204e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            for (DbEntry e : mCarryOver) {
205e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                itemMap.put(e.id, e);
206e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
207e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
208e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            do {
209e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // Some items are still remaining. Try adding a few new screens.
210e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
211e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // At every iteration, make sure that at least one item is removed from
212e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
213e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // break the loop and abort migration by throwing an exception.
214e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                OptimalPlacementSolution placement = new OptimalPlacementSolution(
215da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                        new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
216e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                placement.find();
217e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (placement.finalPlacedItems.size() > 0) {
218d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                    long newScreenId = LauncherSettings.Settings.call(
219d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                            mContext.getContentResolver(),
220d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                            LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
221d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                            .getLong(LauncherSettings.Settings.EXTRA_VALUE);
222f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
223e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    allScreens.add(newScreenId);
224e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    for (DbEntry item : placement.finalPlacedItems) {
225e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        if (!mCarryOver.remove(itemMap.get(item.id))) {
226e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            throw new Exception("Unable to find matching items");
227e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
228e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        item.screenId = newScreenId;
229e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        update(item);
230e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
231e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                } else {
232e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    throw new Exception("None of the items can be placed on an empty screen");
233e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
234e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
235e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            } while (!mCarryOver.isEmpty());
236e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
237f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            // Update screens
238f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
239f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            mUpdateOperations.add(ContentProviderOperation.newDelete(uri).build());
240f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            int count = allScreens.size();
241f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            for (int i = 0; i < count; i++) {
242f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                ContentValues v = new ContentValues();
243f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                long screenId = allScreens.get(i);
244f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
245f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
246f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                mUpdateOperations.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
247f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            }
248e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
249f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        return applyOperations();
250e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
251e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
252e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    /**
253e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * Migrate a particular screen id.
254e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * Strategy:
255e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     *   1) For all possible combinations of row and column, pick the one which causes the least
256da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal     *      data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
257e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     *   2) Maintain a list of all lost items before this screen, and add any new item lost from
258e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     *      this screen to that list as well.
259e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     *   3) If all those items from the above list can be placed on this screen, place them
260e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     *      (otherwise they are placed on a new screen).
261e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     */
262a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected void migrateScreen(long screenId) {
263da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal        // If we are migrating the first screen, do not touch the first row.
264a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID)
265a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                ? 1 : 0;
266da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal
267f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
268e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
269e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        int removedCol = Integer.MAX_VALUE;
270e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        int removedRow = Integer.MAX_VALUE;
271e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
272e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // removeWt represents the cost function for loss of items during migration, and moveWt
273e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // represents the cost function for repositioning the items. moveWt is only considered if
274e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // removeWt is same for two different configurations.
275e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
276e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // cost.
277e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        float removeWt = Float.MAX_VALUE;
278e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        float moveWt = Float.MAX_VALUE;
279e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        float[] outLoss = new float[2];
280e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        ArrayList<DbEntry> finalItems = null;
281e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
282e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // Try removing all possible combinations
283e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        for (int x = 0; x < mSrcX; x++) {
284a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            // Try removing the rows first from bottom. This keeps the workspace
285a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            // nicely aligned with hotseat.
286a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            for (int y = mSrcY - 1; y >= startY; y--) {
287e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // Use a deep copy when trying out a particular combination as it can change
288e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // the underlying object.
289da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), outLoss);
290e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
291e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
292e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    removeWt = outLoss[0];
293e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    moveWt = outLoss[1];
294e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    removedCol = mShouldRemoveX ? x : removedCol;
295e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    removedRow = mShouldRemoveY ? y : removedRow;
296e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    finalItems = itemsOnScreen;
297e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
298e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
299e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // No need to loop over all rows, if a row removal is not needed.
300e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (!mShouldRemoveY) {
301e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    break;
302e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
303e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
304e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
305e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if (!mShouldRemoveX) {
306e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                break;
307e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
308e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
309e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
310e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        if (DEBUG) {
311e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            Log.d(TAG, String.format("Removing row %d, column %d on screen %d",
312e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    removedRow, removedCol, screenId));
313e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
314e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
315e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
316e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        for (DbEntry e : deepCopy(items)) {
317e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            itemMap.put(e.id, e);
318e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
319e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
320e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        for (DbEntry item : finalItems) {
321e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            DbEntry org = itemMap.get(item.id);
322e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            itemMap.remove(item.id);
323e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
324e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            // Check if update is required
325e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if (!item.columnsSame(org)) {
326e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                update(item);
327e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
328e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
329e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
330e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // The remaining items in {@link #itemMap} are those which didn't get placed.
331e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        for (DbEntry item : itemMap) {
332e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            mCarryOver.add(item);
333e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
334e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
335e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        if (!mCarryOver.isEmpty() && removeWt == 0) {
336e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            // No new items were removed in this step. Try placing all the items on this screen.
337ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal            GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
338da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal            occupied.markCells(0, 0, mTrgX, startY, true);
339e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            for (DbEntry item : finalItems) {
340ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                occupied.markCells(item, true);
341e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
342e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
343e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
344da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                    deepCopy(mCarryOver), startY, true);
345e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            placement.find();
346e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if (placement.lowestWeightLoss == 0) {
347e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // All items got placed
348e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
349e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                for (DbEntry item : placement.finalPlacedItems) {
350e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    item.screenId = screenId;
351e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    update(item);
352e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
353e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
354e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                mCarryOver.clear();
355e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
356e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
357e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
358e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
359e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    /**
360e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * Updates an item in the DB.
361e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     */
362a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected void update(DbEntry item) {
363e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        mTempValues.clear();
364e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        item.addToContentValues(mTempValues);
365e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        mUpdateOperations.add(ContentProviderOperation
366e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                .newUpdate(LauncherSettings.Favorites.getContentUri(item.id))
367e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                .withValues(mTempValues).build());
368e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
369e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
370e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    /**
371e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * Tries the remove the provided row and column.
372e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * @param items all the items on the screen under operation
373e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
374e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * with the overall item movement.
375e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     */
376da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal    private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
377da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal            ArrayList<DbEntry> items, float[] outLoss) {
378ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal        GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
379da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal        occupied.markCells(0, 0, mTrgX, startY, true);
380e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
381e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        col = mShouldRemoveX ? col : Integer.MAX_VALUE;
382e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        row = mShouldRemoveY ? row : Integer.MAX_VALUE;
383e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
384e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        ArrayList<DbEntry> finalItems = new ArrayList<>();
385e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        ArrayList<DbEntry> removedItems = new ArrayList<>();
386e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
387e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        for (DbEntry item : items) {
388e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if ((item.cellX <= col && (item.spanX + item.cellX) > col)
389e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
390e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                removedItems.add(item);
391e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (item.cellX >= col) item.cellX --;
392e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (item.cellY >= row) item.cellY --;
393e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            } else {
394e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (item.cellX > col) item.cellX --;
395e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (item.cellY > row) item.cellY --;
396e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                finalItems.add(item);
397ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                occupied.markCells(item, true);
398e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
399e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
400e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
401da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal        OptimalPlacementSolution placement =
402da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                new OptimalPlacementSolution(occupied, removedItems, startY);
403e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        placement.find();
404e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        finalItems.addAll(placement.finalPlacedItems);
405e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        outLoss[0] = placement.lowestWeightLoss;
406e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        outLoss[1] = placement.lowestMoveCost;
407e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        return finalItems;
408e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
409e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
410e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private class OptimalPlacementSolution {
411e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        private final ArrayList<DbEntry> itemsToPlace;
412ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal        private final GridOccupancy occupied;
413e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
414e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // If set to true, item movement are not considered in move cost, leading to a more
415e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        // linear placement.
416e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        private final boolean ignoreMove;
417e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
418da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal        // The first row in the grid from where the placement should start.
419da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal        private final int startY;
420da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal
421e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        float lowestWeightLoss = Float.MAX_VALUE;
422e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        float lowestMoveCost = Float.MAX_VALUE;
423e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        ArrayList<DbEntry> finalPlacedItems;
424e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
425da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal        public OptimalPlacementSolution(
426da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, int startY) {
427da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal            this(occupied, itemsToPlace, startY, false);
428e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
429e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
430ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal        public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace,
431da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                int startY, boolean ignoreMove) {
432e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            this.occupied = occupied;
433e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            this.itemsToPlace = itemsToPlace;
434e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            this.ignoreMove = ignoreMove;
435da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal            this.startY = startY;
436e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
437e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            // Sort the items such that larger widgets appear first followed by 1x1 items
438e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            Collections.sort(this.itemsToPlace);
439e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
440e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
441e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public void find() {
442e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            find(0, 0, 0, new ArrayList<DbEntry>());
443e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
444e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
445e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        /**
446e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         * Recursively finds a placement for the provided items.
447e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         * @param index the position in {@link #itemsToPlace} to start looking at.
448e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         * @param weightLoss total weight loss upto this point
449e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         * @param moveCost total move cost upto this point
450e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         * @param itemsPlaced all the items already placed upto this point
451e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         */
452e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public void find(int index, float weightLoss, float moveCost,
453e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                ArrayList<DbEntry> itemsPlaced) {
454e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if ((weightLoss >= lowestWeightLoss) ||
455e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) {
456e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // Abort, as we already have a better solution.
457e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                return;
458e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
459e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            } else if (index >= itemsToPlace.size()) {
460e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // End loop.
461e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                lowestWeightLoss = weightLoss;
462e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                lowestMoveCost = moveCost;
463e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
464e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // Keep a deep copy of current configuration as it can change during recursion.
465e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                finalPlacedItems = deepCopy(itemsPlaced);
466e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                return;
467e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
468e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
469e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            DbEntry me = itemsToPlace.get(index);
470e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            int myX = me.cellX;
471e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            int myY = me.cellY;
472e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
473e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            // List of items to pass over if this item was placed.
474e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            ArrayList<DbEntry> itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1);
475e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            itemsIncludingMe.addAll(itemsPlaced);
476e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            itemsIncludingMe.add(me);
477e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
478e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if (me.spanX > 1 || me.spanY > 1) {
479e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // If the current item is a widget (and it greater than 1x1), try to place it at
480e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // all possible positions. This is because a widget placed at one position can
481e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // affect the placement of a different widget.
482e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                int myW = me.spanX;
483e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                int myH = me.spanY;
484e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
485da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                for (int y = startY; y < mTrgY; y++) {
486e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    for (int x = 0; x < mTrgX; x++) {
487e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        float newMoveCost = moveCost;
488e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        if (x != myX) {
489e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.cellX = x;
490e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            newMoveCost ++;
491e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
492e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        if (y != myY) {
493e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.cellY = y;
494e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            newMoveCost ++;
495e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
496e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        if (ignoreMove) {
497e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            newMoveCost = moveCost;
498e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
499e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
500ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                        if (occupied.isRegionVacant(x, y, myW, myH)) {
501e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            // place at this position and continue search.
502ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, true);
503e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
504ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, false);
505e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
506e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
507e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        // Try resizing horizontally
508ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                        if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
509e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanX --;
510ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, true);
511e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            // 1 extra move cost
512e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
513ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, false);
514e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanX ++;
515e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
516e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
517e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        // Try resizing vertically
518ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                        if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
519e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanY --;
520ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, true);
521e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            // 1 extra move cost
522e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
523ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, false);
524e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanY ++;
525e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
526e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
527e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        // Try resizing horizontally & vertically
528e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        if (myH > me.minSpanY && myW > me.minSpanX &&
529ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                                occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
530e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanX --;
531e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanY --;
532ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, true);
533e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            // 2 extra move cost
534e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
535ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                            occupied.markCells(me, false);
536e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanX ++;
537e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            me.spanY ++;
538e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
539e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        me.cellX = myX;
540e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        me.cellY = myY;
541e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
542e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
543e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
544e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // Finally also try a solution when this item is not included. Trying it in the end
545e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // causes it to get skipped in most cases due to higher weight loss, and prevents
546e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // unnecessary deep copies of various configurations.
547e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
548e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            } else {
549e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // Since this is a 1x1 item and all the following items are also 1x1, just place
550e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // it at 'the most appropriate position' and hope for the best.
551e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // The most appropriate position: one with lease straight line distance
552e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                int newDistance = Integer.MAX_VALUE;
553e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
554e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
555da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                for (int y = startY; y < mTrgY; y++) {
556e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    for (int x = 0; x < mTrgX; x++) {
557ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                        if (!occupied.cells[x][y]) {
558e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            int dist = ignoreMove ? 0 :
559e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                                ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
560e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            if (dist < newDistance) {
561e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                                newX = x;
562e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                                newY = y;
563e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                                newDistance = dist;
564e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            }
565e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        }
566e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
567e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
568e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
569e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (newX < mTrgX && newY < mTrgY) {
570e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    float newMoveCost = moveCost;
571e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    if (newX != myX) {
572e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        me.cellX = newX;
573e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        newMoveCost ++;
574e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
575e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    if (newY != myY) {
576e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        me.cellY = newY;
577e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        newMoveCost ++;
578e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
579e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    if (ignoreMove) {
580e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        newMoveCost = moveCost;
581e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
582ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                    occupied.markCells(me, true);
583e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
584ff4ba2d99593ed84963b3f71c555b529dd905835Sunny Goyal                    occupied.markCells(me, false);
585e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    me.cellX = myX;
586e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    me.cellY = myY;
587e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
588e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    // Try to find a solution without this item, only if
589e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    //  1) there was at least one space, i.e., we were able to place this item
590e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    //  2) if the next item has the same weight (all items are already sorted), as
591e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    //     if it has lower weight, that solution will automatically get discarded.
592e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    //  3) ignoreMove false otherwise, move cost is ignored and the weight will
593e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    //      anyway be same.
594e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    if (index + 1 < itemsToPlace.size()
595e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                            && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) {
596e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
597e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
598e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                } else {
599e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    // No more space. Jump to the end.
600e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    for (int i = index + 1; i < itemsToPlace.size(); i++) {
601e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                        weightLoss += itemsToPlace.get(i).weight;
602e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    }
603e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced);
604e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
605e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
606e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
607e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
608e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
609f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    private ArrayList<DbEntry> loadHotseatEntries() {
610f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
611f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                new String[]{
612f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        Favorites._ID,                  // 0
613f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        Favorites.ITEM_TYPE,            // 1
614f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        Favorites.INTENT,               // 2
615f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        Favorites.SCREEN},              // 3
616f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT, null, null, null);
617f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
618f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
619f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
620f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
621f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        final int indexScreen = c.getColumnIndexOrThrow(Favorites.SCREEN);
622f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
623f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        ArrayList<DbEntry> entries = new ArrayList<>();
624f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        while (c.moveToNext()) {
625f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            DbEntry entry = new DbEntry();
626f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            entry.id = c.getLong(indexId);
627f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            entry.itemType = c.getInt(indexItemType);
628f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            entry.screenId = c.getLong(indexScreen);
629f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
630f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            if (entry.screenId >= mSrcHotseatSize) {
631f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                mEntryToRemove.add(entry.id);
632f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                continue;
633f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            }
634f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
635f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            try {
636f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                // calculate weight
637f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                switch (entry.itemType) {
638f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    case Favorites.ITEM_TYPE_SHORTCUT:
639bfbf7f9f4a0b300613f0ff27a4eb592d88c08325Tony Wickham                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
640f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    case Favorites.ITEM_TYPE_APPLICATION: {
641f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        verifyIntent(c.getString(indexIntent));
642bfbf7f9f4a0b300613f0ff27a4eb592d88c08325Tony Wickham                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
643bfbf7f9f4a0b300613f0ff27a4eb592d88c08325Tony Wickham                                WT_APPLICATION : WT_SHORTCUT;
644f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        break;
645f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    }
646f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    case Favorites.ITEM_TYPE_FOLDER: {
647f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        int total = getFolderItemsCount(entry.id);
648f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        if (total == 0) {
649f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                            throw new Exception("Folder is empty");
650f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        }
651f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        entry.weight = WT_FOLDER_FACTOR * total;
652f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        break;
653f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    }
654f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    default:
655f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                        throw new Exception("Invalid item type");
656f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                }
657f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            } catch (Exception e) {
658f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                if (DEBUG) {
659f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                    Log.d(TAG, "Removing item " + entry.id, e);
660f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                }
661f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                mEntryToRemove.add(entry.id);
662f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal                continue;
663f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            }
664f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal            entries.add(entry);
665f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        }
666f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        c.close();
667f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        return entries;
668f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    }
669f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
670f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
671e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    /**
672e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * Loads entries for a particular screen id.
673e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     */
674a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
675a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        Cursor c = queryWorkspace(
676f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                new String[]{
677f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Favorites._ID,                  // 0
678f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Favorites.ITEM_TYPE,            // 1
679f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Favorites.CELLX,                // 2
680f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Favorites.CELLY,                // 3
681f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Favorites.SPANX,                // 4
682f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Favorites.SPANY,                // 5
683f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Favorites.INTENT,               // 6
6842e1efb480a9b77a97cb623d4f5faf6802a417422Sunny Goyal                        Favorites.APPWIDGET_PROVIDER,   // 7
6852e1efb480a9b77a97cb623d4f5faf6802a417422Sunny Goyal                        Favorites.APPWIDGET_ID},        // 8
686e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
687a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                        + " AND " + Favorites.SCREEN + " = " + screen);
688f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
689f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
690f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
691f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX);
692f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY);
693f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX);
694f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
695f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
696f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
6972e1efb480a9b77a97cb623d4f5faf6802a417422Sunny Goyal        final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID);
698f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
699f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        ArrayList<DbEntry> entries = new ArrayList<>();
700f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        while (c.moveToNext()) {
701f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            DbEntry entry = new DbEntry();
702f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entry.id = c.getLong(indexId);
703f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entry.itemType = c.getInt(indexItemType);
704f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entry.cellX = c.getInt(indexCellX);
705f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entry.cellY = c.getInt(indexCellY);
706f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entry.spanX = c.getInt(indexSpanX);
707f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entry.spanY = c.getInt(indexSpanY);
708f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entry.screenId = screen;
709f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
710f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            try {
711f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                // calculate weight
712f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                switch (entry.itemType) {
713f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    case Favorites.ITEM_TYPE_SHORTCUT:
714bfbf7f9f4a0b300613f0ff27a4eb592d88c08325Tony Wickham                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
715f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    case Favorites.ITEM_TYPE_APPLICATION: {
716f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        verifyIntent(c.getString(indexIntent));
717bfbf7f9f4a0b300613f0ff27a4eb592d88c08325Tony Wickham                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
718bfbf7f9f4a0b300613f0ff27a4eb592d88c08325Tony Wickham                                WT_APPLICATION : WT_SHORTCUT;
719f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        break;
720f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    }
721f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    case Favorites.ITEM_TYPE_APPWIDGET: {
722f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        String provider = c.getString(indexAppWidgetProvider);
723f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        ComponentName cn = ComponentName.unflattenFromString(provider);
724f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        verifyPackage(cn.getPackageName());
725f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
726f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                                * entry.spanX * entry.spanY);
727f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
7282e1efb480a9b77a97cb623d4f5faf6802a417422Sunny Goyal                        int widgetId = c.getInt(indexAppWidgetId);
7292e1efb480a9b77a97cb623d4f5faf6802a417422Sunny Goyal                        LauncherAppWidgetProviderInfo pInfo = AppWidgetManagerCompat.getInstance(
7302e1efb480a9b77a97cb623d4f5faf6802a417422Sunny Goyal                                mContext).getLauncherAppWidgetInfo(widgetId);
731f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        Point spans = pInfo == null ?
732f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                                mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext);
733f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        if (spans != null) {
734f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                            entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
735f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                            entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
736f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        } else {
737f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                            // Assume that the widget be resized down to 2x2
738f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                            entry.minSpanX = entry.minSpanY = 2;
739f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        }
740f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
741f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
742f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                            throw new Exception("Widget can't be resized down to fit the grid");
743f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        }
744f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        break;
745f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    }
746f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    case Favorites.ITEM_TYPE_FOLDER: {
747f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        int total = getFolderItemsCount(entry.id);
748f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        if (total == 0) {
749f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                            throw new Exception("Folder is empty");
750f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        }
751f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        entry.weight = WT_FOLDER_FACTOR * total;
752f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        break;
753f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    }
754f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    default:
755f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        throw new Exception("Invalid item type");
756f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                }
757f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            } catch (Exception e) {
758f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                if (DEBUG) {
759f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    Log.d(TAG, "Removing item " + entry.id, e);
760f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                }
761f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                mEntryToRemove.add(entry.id);
762f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                continue;
763f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            }
764f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            entries.add(entry);
765f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        }
766f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        c.close();
767f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        return entries;
768e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
769e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
770e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    /**
771e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * @return the number of valid items in the folder.
772e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     */
773e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private int getFolderItemsCount(long folderId) {
774a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        Cursor c = queryWorkspace(
775f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                new String[]{Favorites._ID, Favorites.INTENT},
776a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                Favorites.CONTAINER + " = " + folderId);
777e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
778e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        int total = 0;
779e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        while (c.moveToNext()) {
780e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            try {
781e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                verifyIntent(c.getString(1));
782e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                total++;
783e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            } catch (Exception e) {
784e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                mEntryToRemove.add(c.getLong(0));
785e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
786e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
787fb062c63355f65e6df9e99cbf4cce49cee2900d1Tony Wickham        c.close();
788e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        return total;
789e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
790e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
791a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected Cursor queryWorkspace(String[] columns, String where) {
792a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        return mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
793a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                columns, where, null, null, null);
794a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    }
795a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal
796e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    /**
797e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * Verifies if the intent should be restored.
798e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     */
799e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private void verifyIntent(String intentStr) throws Exception {
800e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        Intent intent = Intent.parseUri(intentStr, 0);
801e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        if (intent.getComponent() != null) {
802e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            verifyPackage(intent.getComponent().getPackageName());
803e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        } else if (intent.getPackage() != null) {
804e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            // Only verify package if the component was null.
805e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            verifyPackage(intent.getPackage());
806e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
807e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
808e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
809e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    /**
810e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     * Verifies if the package should be restored
811e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal     */
812e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private void verifyPackage(String packageName) throws Exception {
813e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        if (!mValidPackages.contains(packageName)) {
814e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            throw new Exception("Package not available");
815e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
816e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
817e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
818a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
819e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
820e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public float weight;
821e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
822e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public DbEntry() { }
823e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
824e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public DbEntry copy() {
825e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            DbEntry entry = new DbEntry();
826e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            entry.copyFrom(this);
827e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            entry.weight = weight;
828e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            entry.minSpanX = minSpanX;
829e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            entry.minSpanY = minSpanY;
830e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            return entry;
831e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
832e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
833e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        /**
834e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         * Comparator such that larger widgets come first,  followed by all 1x1 items
835e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         * based on their weights.
836e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal         */
837e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        @Override
838e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public int compareTo(DbEntry another) {
839e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            if (itemType == Favorites.ITEM_TYPE_APPWIDGET) {
840e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
841e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    return another.spanY * another.spanX - spanX * spanY;
842e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                } else {
843e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    return -1;
844e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                }
845e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
846e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                return 1;
847e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            } else {
848e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                // Place higher weight before lower weight.
849e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                return Float.compare(another.weight, weight);
850e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            }
851e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
852e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
853e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public boolean columnsSame(DbEntry org) {
854e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX &&
855e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal                    org.spanY == spanY && org.screenId == screenId;
856e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
857e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
858e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        public void addToContentValues(ContentValues values) {
859e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            values.put(LauncherSettings.Favorites.SCREEN, screenId);
860e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            values.put(LauncherSettings.Favorites.CELLX, cellX);
861e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            values.put(LauncherSettings.Favorites.CELLY, cellY);
862e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            values.put(LauncherSettings.Favorites.SPANX, spanX);
863e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            values.put(LauncherSettings.Favorites.SPANY, spanY);
864e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
865e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
866e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
867f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal    private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
868e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size());
869e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        for (DbEntry e : src) {
870e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal            dup.add(e.copy());
871e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        }
872e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        return dup;
873e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
874e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
875e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    private static Point parsePoint(String point) {
876e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        String[] split = point.split(",");
877e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal        return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
878e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    }
879e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
880f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    private static String getPointString(int x, int y) {
881f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        return String.format(Locale.ENGLISH, "%d,%d", x, y);
882f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    }
883f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
884a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal    public static void markForMigration(
885a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            Context context, int gridX, int gridY, int hotseatSize) {
886a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        Utilities.getPrefs(context).edit()
887a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(gridX, gridY))
888a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, hotseatSize)
889a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                .apply();
890a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal    }
891a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal
892f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    /**
893f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * Migrates the workspace and hotseat in case their sizes changed.
894f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * @return false if the migration failed.
895f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     */
896f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    public static boolean migrateGridIfNeeded(Context context) {
897f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        SharedPreferences prefs = Utilities.getPrefs(context);
898f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal        InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
899f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
900f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        String gridSizeString = getPointString(idp.numColumns, idp.numRows);
901e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
902f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) &&
903bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal                idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)) {
904f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            // Skip if workspace and hotseat sizes have not changed.
905f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            return true;
906f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        }
907f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
908f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        long migrationStartTime = System.currentTimeMillis();
909f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        try {
910f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            boolean dbChanged = false;
911f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
912a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal            HashSet validPackages = getValidPackages(context);
913f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            // Hotseat
914bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal            int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
915bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal            if (srcHotseatCount != idp.numHotseatIcons) {
916f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                // Migrate hotseat.
917f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
918f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                dbChanged = new GridSizeMigrationTask(context,
919f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        LauncherAppState.getInstance().getInvariantDeviceProfile(),
920bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal                        validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
921f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            }
922f862a26347b583bd84be22a8ceff4bc13158ec7eSunny Goyal
923f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            // Grid size
924f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            Point targetSize = new Point(idp.numColumns, idp.numRows);
925f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            Point sourceSize = parsePoint(prefs.getString(
926f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
927f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
928a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            if (new MultiStepMigrationTask(validPackages, context).migrate(sourceSize, targetSize)) {
929a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                dbChanged = true;
930f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            }
931e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal
932f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            if (dbChanged) {
933f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                // Make sure we haven't removed everything.
934f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                final Cursor c = context.getContentResolver().query(
935f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                        LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
936f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                boolean hasData = c.moveToNext();
937f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                c.close();
938f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                if (!hasData) {
939f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    throw new Exception("Removed every thing during grid resize");
940f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                }
941f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            }
942f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
943f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            return true;
944f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        } catch (Exception e) {
945f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            Log.e(TAG, "Error during grid migration", e);
946f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
947f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            return false;
948f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        } finally {
949f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            Log.v(TAG, "Workspace migration completed in "
950f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    + (System.currentTimeMillis() - migrationStartTime));
951f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
952f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            // Save current configuration, so that the migration does not run again.
953f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            prefs.edit()
954f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
955bb011dad4e69bec027be1e00d573a3095b318b43Sunny Goyal                    .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
956f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    .apply();
957f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        }
958f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    }
959a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal
960a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    protected static HashSet<String> getValidPackages(Context context) {
961a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        // Initialize list of valid packages. This contain all the packages which are already on
962a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        // the device and packages which are being installed. Any item which doesn't belong to
963a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        // this set is removed.
964a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        // Since the loader removes such items anyway, removing these items here doesn't cause
965a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        // any extra data loss and gives us more free space on the grid for better migration.
966a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        HashSet validPackages = new HashSet<>();
967a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        for (PackageInfo info : context.getPackageManager()
968a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
969a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal            validPackages.add(info.packageName);
970a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        }
971a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        validPackages.addAll(PackageInstallerCompat.getInstance(context)
972a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                .updateAndGetActiveSessionCache().keySet());
973a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal        return validPackages;
974a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    }
975a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal
976a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal    /**
977d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal     * Removes any broken item from the hotseat.
978d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal     * @return a map with occupied hotseat position set to non-null value.
979d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal     */
980d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal    public static LongArrayMap<Object> removeBrokenHotseatItems(Context context) throws Exception {
981d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        GridSizeMigrationTask task = new GridSizeMigrationTask(context,
982d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal                LauncherAppState.getInstance().getInvariantDeviceProfile(),
983d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal                getValidPackages(context), Integer.MAX_VALUE, Integer.MAX_VALUE);
984d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal
985d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        // Load all the valid entries
986d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        ArrayList<DbEntry> items = task.loadHotseatEntries();
987d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        // Delete any entry marked for deletion by above load.
988d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        task.applyOperations();
989d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        LongArrayMap<Object> positions = new LongArrayMap<>();
990d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        for (DbEntry item : items) {
991d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal            positions.put(item.screenId, item);
992d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        }
993d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal        return positions;
994d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal    }
995d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal
996d70ef242332e766b2c23e3b8bb537dc2d584e9ecSunny Goyal    /**
997a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal     * Task to run grid migration in multiple steps when the size difference is more than 1.
998a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal     */
999a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal    protected static class MultiStepMigrationTask {
1000a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        private final HashSet<String> mValidPackages;
1001a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        private final Context mContext;
1002a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal
1003a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        public MultiStepMigrationTask(HashSet<String> validPackages, Context context) {
1004a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            mValidPackages = validPackages;
1005a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            mContext = context;
1006a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        }
1007a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal
1008a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
1009a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            boolean dbChanged = false;
1010a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            if (!targetSize.equals(sourceSize)) {
1011a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                if (sourceSize.x < targetSize.x) {
1012a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    // Source is smaller that target, just expand the grid without actual migration.
1013a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    sourceSize.x = targetSize.x;
1014a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                }
1015a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                if (sourceSize.y < targetSize.y) {
1016a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    // Source is smaller that target, just expand the grid without actual migration.
1017a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    sourceSize.y = targetSize.y;
1018a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                }
1019a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal
1020a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                // Migrate the workspace grid, such that the points differ by max 1 in x and y
1021a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                // each on every step.
1022a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                while (!targetSize.equals(sourceSize)) {
1023a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    // Get the next size, such that the points differ by max 1 in x and y each
1024a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    Point nextSize = new Point(sourceSize);
1025a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    if (targetSize.x < nextSize.x) {
1026a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                        nextSize.x--;
1027a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    }
1028a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    if (targetSize.y < nextSize.y) {
1029a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                        nextSize.y--;
1030a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    }
1031a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    if (runStepTask(sourceSize, nextSize)) {
1032a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                        dbChanged = true;
1033a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    }
1034a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    sourceSize.set(nextSize.x, nextSize.y);
1035a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                }
1036a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            }
1037a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            return dbChanged;
1038a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        }
1039a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal
1040a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
1041a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            return new GridSizeMigrationTask(mContext,
1042a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    LauncherAppState.getInstance().getInvariantDeviceProfile(),
1043a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                    mValidPackages, sourceSize, nextSize).migrateWorkspace();
1044a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal        }
1045a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal    }
1046e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal}
1047