Separating methods for updating the model to a sepatate class.

Removing static access to model update methods, to allow for better
access control and testing

Change-Id: I9afe004dbf1b2fe50df422fd28bceea9230a4704
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index da12e67..121b9fb 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2144,7 +2144,7 @@
                 info.spanY = lp.cellVSpan;
 
                 if (requiresDbUpdate) {
-                    LauncherModel.modifyItemInDatabase(getContext(), info, container, screenId,
+                    mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
                             info.cellX, info.cellY, info.spanX, info.spanY);
                 }
             }
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 5fff2e7..2c69d0a 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -16,9 +16,9 @@
 
 package com.android.launcher3;
 
-import android.content.Context;
 import android.os.Process;
 
+import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.util.ContentWriter;
 
 import java.util.ArrayList;
@@ -128,17 +128,17 @@
     /**
      * @param option flag to set or clear
      * @param isEnabled whether to set or clear the flag
-     * @param context if not null, save changes to the db.
+     * @param writer if not null, save changes to the db.
      */
-    public void setOption(int option, boolean isEnabled, Context context) {
+    public void setOption(int option, boolean isEnabled, ModelWriter writer) {
         int oldOptions = options;
         if (isEnabled) {
             options |= option;
         } else {
             options &= ~option;
         }
-        if (context != null && oldOptions != options) {
-            LauncherModel.updateItemInDatabase(context, this);
+        if (writer != null && oldOptions != options) {
+            writer.updateItemInDatabase(this);
         }
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 11db9a0..1c077c2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,6 +85,7 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DefaultAppSearchController;
 import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -256,6 +257,7 @@
     private ViewOnDrawExecutor mPendingExecutor;
 
     private LauncherModel mModel;
+    private ModelWriter mModelWriter;
     private IconCache mIconCache;
     private ExtractedColors mExtractedColors;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
@@ -373,6 +375,7 @@
         mSharedPrefs = Utilities.getPrefs(this);
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
+        mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
         mIconCache = app.getIconCache();
         mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
 
@@ -1500,7 +1503,7 @@
             return;
         }
 
-        LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1]);
+        getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
         mWorkspace.addInScreen(view, info);
     }
 
@@ -1528,7 +1531,7 @@
         launcherInfo.minSpanY = itemInfo.minSpanY;
         launcherInfo.user = appWidgetInfo.getUser();
 
-        LauncherModel.addItemToDatabase(this, launcherInfo,
+        getModelWriter().addItemToDatabase(launcherInfo,
                 itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
 
         if (hostView == null) {
@@ -1688,6 +1691,10 @@
         return mModel;
     }
 
+    public ModelWriter getModelWriter() {
+        return mModelWriter;
+    }
+
     public SharedPreferences getSharedPrefs() {
         return mSharedPrefs;
     }
@@ -2129,8 +2136,7 @@
         folderInfo.title = getText(R.string.folder_name);
 
         // Update the model
-        LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
-                cellX, cellY);
+        getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
 
         // Create the view
         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo);
@@ -2158,7 +2164,7 @@
                 mWorkspace.removeWorkspaceItem(v);
             }
             if (deleteFromDb) {
-                LauncherModel.deleteItemFromDatabase(this, itemInfo);
+                getModelWriter().deleteItemFromDatabase(itemInfo);
             }
         } else if (itemInfo instanceof FolderInfo) {
             final FolderInfo folderInfo = (FolderInfo) itemInfo;
@@ -2167,7 +2173,7 @@
             }
             mWorkspace.removeWorkspaceItem(v);
             if (deleteFromDb) {
-                LauncherModel.deleteFolderAndContentsFromDatabase(this, folderInfo);
+                getModelWriter().deleteFolderAndContentsFromDatabase(folderInfo);
             }
         } else if (itemInfo instanceof LauncherAppWidgetInfo) {
             final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
@@ -2196,7 +2202,7 @@
                 }
             }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
         }
-        LauncherModel.deleteItemFromDatabase(this, widgetInfo);
+        getModelWriter().deleteItemFromDatabase(widgetInfo);
     }
 
     @Override
@@ -3389,7 +3395,7 @@
                         throw (new RuntimeException(desc));
                     } else {
                         Log.d(TAG, desc);
-                        LauncherModel.deleteItemFromDatabase(this, item);
+                        getModelWriter().deleteItemFromDatabase(item);
                         continue;
                     }
                 }
@@ -3486,7 +3492,7 @@
                             + " belongs to component " + item.providerName
                             + ", as the provider is null");
                 }
-                LauncherModel.deleteItemFromDatabase(this, item);
+                getModelWriter().deleteItemFromDatabase(item);
                 return;
             }
 
@@ -3533,14 +3539,14 @@
                                 : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
                     }
 
-                    LauncherModel.updateItemInDatabase(this, item);
+                    getModelWriter().updateItemInDatabase(item);
                 }
             } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
                     && (appWidgetInfo.configure == null)) {
                 // The widget was marked as UI not ready, but there is no configure activity to
                 // update the UI.
                 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
-                LauncherModel.updateItemInDatabase(this, item);
+                getModelWriter().updateItemInDatabase(item);
             }
         }
 
@@ -3590,7 +3596,7 @@
         info.restoreStatus = finalRestoreFlag;
 
         mWorkspace.reinflateWidgetsIfNecessary();
-        LauncherModel.updateItemInDatabase(this, info);
+        getModelWriter().updateItemInDatabase(info);
         return info;
     }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 79f9792..7540dac 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -57,6 +57,7 @@
 import com.android.launcher3.model.ExtendedModelTask;
 import com.android.launcher3.model.GridSizeMigrationTask;
 import com.android.launcher3.model.LoaderCursor;
+import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.PackageUpdatedTask;
@@ -264,19 +265,8 @@
         enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
     }
 
-    /**
-     * Adds an item to the DB if it was not created previously, or move it to a new
-     * <container, screen, cellX, cellY>
-     */
-    public static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
-            long screenId, int cellX, int cellY) {
-        if (item.container == ItemInfo.NO_ID) {
-            // From all apps
-            addItemToDatabase(context, item, container, screenId, cellX, cellY);
-        } else {
-            // From somewhere else
-            moveItemInDatabase(context, item, container, screenId, cellX, cellY);
-        }
+    public ModelWriter getWriter(boolean hasVerticalHotseat) {
+        return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat);
     }
 
     static void checkItemInfoLocked(
@@ -330,281 +320,6 @@
         runOnWorkerThread(r);
     }
 
-    static void updateItemInDatabaseHelper(Context context, final ContentWriter writer,
-            final ItemInfo item, final String callingFunction) {
-        final long itemId = item.id;
-        final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
-        final ContentResolver cr = context.getContentResolver();
-
-        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
-        final Context appContext = context.getApplicationContext();
-        Runnable r = new Runnable() {
-            public void run() {
-                cr.update(uri, writer.getValues(appContext), null, null);
-                updateItemArrays(item, itemId, stackTrace);
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
-    static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
-            final ArrayList<ItemInfo> items, final String callingFunction) {
-        final ContentResolver cr = context.getContentResolver();
-
-        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
-        Runnable r = new Runnable() {
-            public void run() {
-                ArrayList<ContentProviderOperation> ops =
-                        new ArrayList<ContentProviderOperation>();
-                int count = items.size();
-                for (int i = 0; i < count; i++) {
-                    ItemInfo item = items.get(i);
-                    final long itemId = item.id;
-                    final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
-                    ContentValues values = valuesList.get(i);
-
-                    ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
-                    updateItemArrays(item, itemId, stackTrace);
-
-                }
-                try {
-                    cr.applyBatch(LauncherProvider.AUTHORITY, ops);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
-    static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
-        // Lock on mBgLock *after* the db operation
-        synchronized (sBgDataModel) {
-            checkItemInfoLocked(itemId, item, stackTrace);
-
-            if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                    item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                // Item is in a folder, make sure this folder exists
-                if (!sBgDataModel.folders.containsKey(item.container)) {
-                    // An items container is being set to a that of an item which is not in
-                    // the list of Folders.
-                    String msg = "item: " + item + " container being set to: " +
-                            item.container + ", not in the list of folders";
-                    Log.e(TAG, msg);
-                }
-            }
-
-            // Items are added/removed from the corresponding FolderInfo elsewhere, such
-            // as in Workspace.onDrop. Here, we just add/remove them from the list of items
-            // that are on the desktop, as appropriate
-            ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
-            if (modelItem != null &&
-                    (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
-                     modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
-                switch (modelItem.itemType) {
-                    case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                    case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                    case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                    case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                        if (!sBgDataModel.workspaceItems.contains(modelItem)) {
-                            sBgDataModel.workspaceItems.add(modelItem);
-                        }
-                        break;
-                    default:
-                        break;
-                }
-            } else {
-                sBgDataModel.workspaceItems.remove(modelItem);
-            }
-        }
-    }
-
-    /**
-     * Move an item in the DB to a new <container, screen, cellX, cellY>
-     */
-    public static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
-            final long screenId, final int cellX, final int cellY) {
-        item.container = container;
-        item.cellX = cellX;
-        item.cellY = cellY;
-
-        // We store hotseat items in canonical form which is this orientation invariant position
-        // in the hotseat
-        if (context instanceof Launcher && screenId < 0 &&
-                container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screenId = Launcher.getLauncher(context).getHotseat()
-                    .getOrderInHotseat(cellX, cellY);
-        } else {
-            item.screenId = screenId;
-        }
-
-        final ContentWriter writer = new ContentWriter(context)
-                .put(LauncherSettings.Favorites.CONTAINER, item.container)
-                .put(LauncherSettings.Favorites.CELLX, item.cellX)
-                .put(LauncherSettings.Favorites.CELLY, item.cellY)
-                .put(LauncherSettings.Favorites.RANK, item.rank)
-                .put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
-        updateItemInDatabaseHelper(context, writer, item, "moveItemInDatabase");
-    }
-
-    /**
-     * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
-     * cellX, cellY have already been updated on the ItemInfos.
-     */
-    public static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
-            final long container, final int screen) {
-
-        ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
-        int count = items.size();
-
-        for (int i = 0; i < count; i++) {
-            ItemInfo item = items.get(i);
-            item.container = container;
-
-            // We store hotseat items in canonical form which is this orientation invariant position
-            // in the hotseat
-            if (context instanceof Launcher && screen < 0 &&
-                    container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                item.screenId = Launcher.getLauncher(context).getHotseat().getOrderInHotseat(item.cellX,
-                        item.cellY);
-            } else {
-                item.screenId = screen;
-            }
-
-            final ContentValues values = new ContentValues();
-            values.put(LauncherSettings.Favorites.CONTAINER, item.container);
-            values.put(LauncherSettings.Favorites.CELLX, item.cellX);
-            values.put(LauncherSettings.Favorites.CELLY, item.cellY);
-            values.put(LauncherSettings.Favorites.RANK, item.rank);
-            values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
-            contentValues.add(values);
-        }
-        updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
-    }
-
-    /**
-     * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
-     */
-    static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
-            final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
-        item.container = container;
-        item.cellX = cellX;
-        item.cellY = cellY;
-        item.spanX = spanX;
-        item.spanY = spanY;
-
-        // We store hotseat items in canonical form which is this orientation invariant position
-        // in the hotseat
-        if (context instanceof Launcher && screenId < 0 &&
-                container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screenId = Launcher.getLauncher(context).getHotseat()
-                    .getOrderInHotseat(cellX, cellY);
-        } else {
-            item.screenId = screenId;
-        }
-
-        final ContentWriter writer = new ContentWriter(context)
-                .put(LauncherSettings.Favorites.CONTAINER, item.container)
-                .put(LauncherSettings.Favorites.CELLX, item.cellX)
-                .put(LauncherSettings.Favorites.CELLY, item.cellY)
-                .put(LauncherSettings.Favorites.RANK, item.rank)
-                .put(LauncherSettings.Favorites.SPANX, item.spanX)
-                .put(LauncherSettings.Favorites.SPANY, item.spanY)
-                .put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
-        updateItemInDatabaseHelper(context, writer, item, "modifyItemInDatabase");
-    }
-
-    /**
-     * Update an item to the database in a specified container.
-     */
-    public static void updateItemInDatabase(Context context, final ItemInfo item) {
-        ContentWriter writer = new ContentWriter(context);
-        item.onAddToDatabase(writer);
-        updateItemInDatabaseHelper(context, writer, item, "updateItemInDatabase");
-    }
-
-    /**
-     * Add an item to the database in a specified container. Sets the container, screen, cellX and
-     * cellY fields of the item. Also assigns an ID to the item.
-     */
-    public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
-            final long screenId, final int cellX, final int cellY) {
-        item.container = container;
-        item.cellX = cellX;
-        item.cellY = cellY;
-        // We store hotseat items in canonical form which is this orientation invariant position
-        // in the hotseat
-        if (context instanceof Launcher && screenId < 0 &&
-                container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screenId = Launcher.getLauncher(context).getHotseat()
-                    .getOrderInHotseat(cellX, cellY);
-        } else {
-            item.screenId = screenId;
-        }
-
-        final ContentWriter writer = new ContentWriter(context);
-        final ContentResolver cr = context.getContentResolver();
-        item.onAddToDatabase(writer);
-
-        item.id = LauncherSettings.Settings.call(cr, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
-                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
-
-        writer.put(LauncherSettings.Favorites._ID, item.id);
-
-        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
-        final Context appContext = context.getApplicationContext();
-        Runnable r = new Runnable() {
-            public void run() {
-                cr.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(appContext));
-
-                synchronized (sBgDataModel) {
-                    checkItemInfoLocked(item.id, item, stackTrace);
-                    sBgDataModel.addItem(appContext, item, true);
-                }
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
-    /**
-     * Removes the specified item from the database
-     */
-    public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
-        ArrayList<ItemInfo> items = new ArrayList<>();
-        items.add(item);
-        deleteItemsFromDatabase(context, items);
-    }
-
-    /**
-     * Removes all the items from the database matching {@param matcher}.
-     */
-    public static void deleteItemsFromDatabase(Context context, ItemInfoMatcher matcher) {
-        deleteItemsFromDatabase(context, matcher.filterItemInfos(sBgDataModel.itemsIdMap));
-    }
-
-    /**
-     * Removes the specified items from the database
-     */
-    public static void deleteItemsFromDatabase(Context context,
-            final Iterable<? extends ItemInfo> items) {
-        final ContentResolver cr = context.getContentResolver();
-        final Context appContext = context.getApplicationContext();
-        Runnable r = new Runnable() {
-            public void run() {
-                for (ItemInfo item : items) {
-                    final Uri uri = LauncherSettings.Favorites.getContentUri(item.id);
-                    cr.delete(uri, null, null);
-
-                    sBgDataModel.removeItem(appContext, item);
-                }
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
     /**
      * Update the order of the workspace screens in the database. The array list contains
      * a list of screen ids in the order that they should appear.
@@ -654,27 +369,6 @@
     }
 
     /**
-     * Remove the specified folder and all its contents from the database.
-     */
-    public static void deleteFolderAndContentsFromDatabase(Context context, final FolderInfo info) {
-        final ContentResolver cr = context.getContentResolver();
-        final Context appContext = context.getApplicationContext();
-
-        Runnable r = new Runnable() {
-            public void run() {
-                cr.delete(LauncherSettings.Favorites.CONTENT_URI,
-                        LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
-                sBgDataModel.removeItem(appContext, info.contents);
-                info.contents.clear();
-
-                cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
-                sBgDataModel.removeItem(appContext, info);
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
-    /**
      * Set this as the current Launcher activity object for the loader.
      */
     public void initialize(Callbacks callbacks) {
@@ -2203,6 +1897,11 @@
                 }
             });
         }
+
+        public ModelWriter getModelWriter() {
+            // Updates from model task, do not deal with icon position in hotseat.
+            return mModel.getWriter(false /* hasVerticalHotseat */);
+        }
     }
 
     public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d2008a6..fd0bc0c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2652,8 +2652,8 @@
                         }
                     }
 
-                    LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
-                            lp.cellY, item.spanX, item.spanY);
+                    mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
+                            lp.cellX, lp.cellY, item.spanX, item.spanY);
                 } else {
                     if (!returnToOriginalCellToPreventShuffling) {
                         onNoCellFound(dropTargetLayout);
@@ -3385,7 +3385,7 @@
             }
             // Add the item to DB before adding to screen ensures that the container and other
             // values of the info is properly updated.
-            LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
+            mLauncher.getModelWriter().addOrMoveItemInDatabase(info, container, screenId,
                     mTargetCell[0], mTargetCell[1]);
 
             addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -4003,7 +4003,7 @@
         HashSet<String> packages = new HashSet<>(1);
         packages.add(packageName);
         ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
-        LauncherModel.deleteItemsFromDatabase(mLauncher, matcher);
+        mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
         removeItemsByMatcher(matcher);
     }
 
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index b77493b..a476650 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -168,7 +168,7 @@
                 public void run() {
                     if (item instanceof AppInfo) {
                         ShortcutInfo info = ((AppInfo) item).makeShortcut();
-                        LauncherModel.addItemToDatabase(mLauncher, info,
+                        mLauncher.getModelWriter().addItemToDatabase(info,
                                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
                                 screenId, coordinates[0], coordinates[1]);
 
@@ -194,7 +194,7 @@
 
             final int[] coordinates = new int[2];
             final long screenId = findSpaceOnWorkspace(item, coordinates);
-            LauncherModel.moveItemInDatabase(mLauncher, info,
+            mLauncher.getModelWriter().moveItemInDatabase(info,
                     LauncherSettings.Favorites.CONTAINER_DESKTOP,
                     screenId, coordinates[0], coordinates[1]);
 
@@ -304,7 +304,7 @@
         ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
                 sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
         host.requestLayout();
-        LauncherModel.updateItemInDatabase(mLauncher, info);
+        mLauncher.getModelWriter().updateItemInDatabase(info);
         announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
     }
 
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index f7ca703..b784fe7 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -59,7 +59,7 @@
             Runnable onComplete = new Runnable() {
                 @Override
                 public void run() {
-                    LauncherModel.addItemToDatabase(mLauncher, info,
+                    mLauncher.getModelWriter().addItemToDatabase(info,
                             LauncherSettings.Favorites.CONTAINER_DESKTOP,
                             screenId, coordinates[0], coordinates[1]);
                     ArrayList<ItemInfo> itemList = new ArrayList<>();
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 876c3c4..bd12e16 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -354,7 +354,7 @@
         // gets saved.
         String newTitle = mFolderName.getText().toString();
         mInfo.setTitle(newTitle);
-        LauncherModel.updateItemInDatabase(mLauncher, mInfo);
+        mLauncher.getModelWriter().updateItemInDatabase(mInfo);
 
         Utilities.sendCustomAccessibilityEvent(
                 this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -444,7 +444,7 @@
         // TODO: Remove this, as with multi-page folders, there will never be any overflow
         for (ShortcutInfo item: overflow) {
             mInfo.remove(item, false);
-            LauncherModel.deleteItemFromDatabase(mLauncher, item);
+            mLauncher.getModelWriter().deleteItemFromDatabase(item);
         }
 
         DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
@@ -646,7 +646,8 @@
                     mPageIndicator.playEntryAnimation();
 
                     if (updateAnimationFlag) {
-                        mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
+                        mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true,
+                                mLauncher.getModelWriter());
                     }
                 }
             });
@@ -972,7 +973,8 @@
         // been refreshed yet.
         if (getItemCount() <= mContent.itemsPerPage()) {
             // Show the animation, next time something is added to the folder.
-            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, mLauncher);
+            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false,
+                    mLauncher.getModelWriter());
         }
 
         if (!isFlingToDelete) {
@@ -1021,7 +1023,7 @@
             items.add(info);
         }
 
-        LauncherModel.moveItemsInDatabase(mLauncher, items, mInfo.id, 0);
+        mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
     }
 
     public void notifyDrop() {
@@ -1188,8 +1190,8 @@
                                 mInfo.screenId);
                         ShortcutInfo finalItem = mInfo.contents.remove(0);
                         newIcon = mLauncher.createShortcut(cellLayout, finalItem);
-                        LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
-                                mInfo.screenId, mInfo.cellX, mInfo.cellY);
+                        mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
+                                mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
                     }
 
                     // Remove the folder
@@ -1300,8 +1302,8 @@
             currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
             // Actually move the item in the database if it was an external drag. Call this
             // before creating the view, so that ShortcutInfo is updated appropriately.
-            LauncherModel.addOrMoveItemInDatabase(
-                    mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
+            mLauncher.getModelWriter().addOrMoveItemInDatabase(
+                    si, mInfo.id, 0, si.cellX, si.cellY);
 
             // We only need to update the locations if it doesn't get handled in #onDropCompleted.
             if (d.dragSource != this) {
@@ -1341,7 +1343,7 @@
 
         if (mContent.getPageCount() > 1) {
             // The animation has already been shown while opening the folder.
-            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
+            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter());
         }
 
         if (d.stateAnnouncer != null) {
@@ -1365,8 +1367,8 @@
     public void onAdd(ShortcutInfo item) {
         mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
         mItemsInvalidated = true;
-        LauncherModel.addOrMoveItemInDatabase(
-                mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+        mLauncher.getModelWriter().addOrMoveItemInDatabase(
+                item, mInfo.id, 0, item.cellX, item.cellY);
     }
 
     public void onRemove(ShortcutInfo item) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 4a2a735..9dfdfdc 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -336,7 +336,7 @@
                     info.cellY = newY;
                     info.rank = rank;
                     if (saveChanges) {
-                        LauncherModel.addOrMoveItemInDatabase(getContext(), info,
+                        mFolder.mLauncher.getModelWriter().addOrMoveItemInDatabase(info,
                                 mFolder.mInfo.id, 0, info.cellX, info.cellY);
                     }
                 }
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index c97b3b5..9696054 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -95,7 +95,9 @@
                 }
 
                 // Add the shortcut to the db
-                addItemToDatabase(context, itemInfo, screenId, cordinates);
+                getModelWriter().addItemToDatabase(itemInfo,
+                        LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
+                        cordinates[0], cordinates[1]);
 
                 // Save the ShortcutInfo for binding in the workspace
                 addedItemsFinal.add(itemInfo);
@@ -129,11 +131,6 @@
         }
     }
 
-    protected void addItemToDatabase(Context context, ItemInfo item, long screenId, int[] pos) {
-        LauncherModel.addItemToDatabase(context, item,
-                LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, pos[0], pos[1]);
-    }
-
     protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) {
         LauncherModel.updateWorkspaceScreenOrder(context, workspaceScreens);
     }
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
new file mode 100644
index 0000000..4931dca
--- /dev/null
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LooperExecuter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+/**
+ * Class for handling model updates.
+ */
+public class ModelWriter {
+
+    private static final String TAG = "ModelWriter";
+
+    private final Context mContext;
+    private final BgDataModel mBgDataModel;
+    private final Executor mWorkerExecutor;
+    private final boolean mHasVerticalHotseat;
+
+    public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
+        mContext = context;
+        mBgDataModel = dataModel;
+        mWorkerExecutor = new LooperExecuter(LauncherModel.getWorkerLooper());
+        mHasVerticalHotseat = hasVerticalHotseat;
+    }
+
+    private void updateItemInfoProps(
+            ItemInfo item, long container, long screenId, int cellX, int cellY) {
+        item.container = container;
+        item.cellX = cellX;
+        item.cellY = cellY;
+        // We store hotseat items in canonical form which is this orientation invariant position
+        // in the hotseat
+        if (container == Favorites.CONTAINER_HOTSEAT) {
+            item.screenId = mHasVerticalHotseat
+                    ? LauncherAppState.getIDP(mContext).numHotseatIcons - cellY - 1 : cellX;
+        } else {
+            item.screenId = screenId;
+        }
+    }
+
+    /**
+     * Adds an item to the DB if it was not created previously, or move it to a new
+     * <container, screen, cellX, cellY>
+     */
+    public void addOrMoveItemInDatabase(ItemInfo item,
+            long container, long screenId, int cellX, int cellY) {
+        if (item.container == ItemInfo.NO_ID) {
+            // From all apps
+            addItemToDatabase(item, container, screenId, cellX, cellY);
+        } else {
+            // From somewhere else
+            moveItemInDatabase(item, container, screenId, cellX, cellY);
+        }
+    }
+
+    private void checkItemInfoLocked(long itemId, ItemInfo item, StackTraceElement[] stackTrace) {
+        ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
+        if (modelItem != null && item != modelItem) {
+            // check all the data is consistent
+            if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
+                ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
+                ShortcutInfo shortcut = (ShortcutInfo) item;
+                if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
+                        modelShortcut.intent.filterEquals(shortcut.intent) &&
+                        modelShortcut.id == shortcut.id &&
+                        modelShortcut.itemType == shortcut.itemType &&
+                        modelShortcut.container == shortcut.container &&
+                        modelShortcut.screenId == shortcut.screenId &&
+                        modelShortcut.cellX == shortcut.cellX &&
+                        modelShortcut.cellY == shortcut.cellY &&
+                        modelShortcut.spanX == shortcut.spanX &&
+                        modelShortcut.spanY == shortcut.spanY) {
+                    // For all intents and purposes, this is the same object
+                    return;
+                }
+            }
+
+            // the modelItem needs to match up perfectly with item if our model is
+            // to be consistent with the database-- for now, just require
+            // modelItem == item or the equality check above
+            String msg = "item: " + ((item != null) ? item.toString() : "null") +
+                    "modelItem: " +
+                    ((modelItem != null) ? modelItem.toString() : "null") +
+                    "Error: ItemInfo passed to checkItemInfo doesn't match original";
+            RuntimeException e = new RuntimeException(msg);
+            if (stackTrace != null) {
+                e.setStackTrace(stackTrace);
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Move an item in the DB to a new <container, screen, cellX, cellY>
+     */
+    public void moveItemInDatabase(final ItemInfo item,
+            long container, long screenId, int cellX, int cellY) {
+        updateItemInfoProps(item, container, screenId, cellX, cellY);
+
+        final ContentWriter writer = new ContentWriter(mContext)
+                .put(Favorites.CONTAINER, item.container)
+                .put(Favorites.CELLX, item.cellX)
+                .put(Favorites.CELLY, item.cellY)
+                .put(Favorites.RANK, item.rank)
+                .put(Favorites.SCREEN, item.screenId);
+
+        mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+    }
+
+    /**
+     * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
+     * cellX, cellY have already been updated on the ItemInfos.
+     */
+    public void moveItemsInDatabase(final ArrayList<ItemInfo> items, long container, int screen) {
+        ArrayList<ContentValues> contentValues = new ArrayList<>();
+        int count = items.size();
+
+        for (int i = 0; i < count; i++) {
+            ItemInfo item = items.get(i);
+            updateItemInfoProps(item, container, screen, item.cellX, item.cellY);
+
+            final ContentValues values = new ContentValues();
+            values.put(Favorites.CONTAINER, item.container);
+            values.put(Favorites.CELLX, item.cellX);
+            values.put(Favorites.CELLY, item.cellY);
+            values.put(Favorites.RANK, item.rank);
+            values.put(Favorites.SCREEN, item.screenId);
+
+            contentValues.add(values);
+        }
+        mWorkerExecutor.execute(new UpdateItemsRunnable(items, contentValues));
+    }
+
+    /**
+     * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
+     */
+    public void modifyItemInDatabase(final ItemInfo item,
+            long container, long screenId, int cellX, int cellY, int spanX, int spanY) {
+        updateItemInfoProps(item, container, screenId, cellX, cellY);
+        item.spanX = spanX;
+        item.spanY = spanY;
+
+        final ContentWriter writer = new ContentWriter(mContext)
+                .put(Favorites.CONTAINER, item.container)
+                .put(Favorites.CELLX, item.cellX)
+                .put(Favorites.CELLY, item.cellY)
+                .put(Favorites.RANK, item.rank)
+                .put(Favorites.SPANX, item.spanX)
+                .put(Favorites.SPANY, item.spanY)
+                .put(Favorites.SCREEN, item.screenId);
+
+        mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+    }
+
+    /**
+     * Update an item to the database in a specified container.
+     */
+    public void updateItemInDatabase(ItemInfo item) {
+        ContentWriter writer = new ContentWriter(mContext);
+        item.onAddToDatabase(writer);
+        mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+    }
+
+    /**
+     * Add an item to the database in a specified container. Sets the container, screen, cellX and
+     * cellY fields of the item. Also assigns an ID to the item.
+     */
+    public void addItemToDatabase(final ItemInfo item,
+            long container, long screenId, int cellX, int cellY) {
+        updateItemInfoProps(item, container, screenId, cellX, cellY);
+
+        final ContentWriter writer = new ContentWriter(mContext);
+        final ContentResolver cr = mContext.getContentResolver();
+        item.onAddToDatabase(writer);
+
+        item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getLong(Settings.EXTRA_VALUE);
+        writer.put(Favorites._ID, item.id);
+
+        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+        mWorkerExecutor.execute(new Runnable() {
+            public void run() {
+                cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+
+                synchronized (mBgDataModel) {
+                    checkItemInfoLocked(item.id, item, stackTrace);
+                    mBgDataModel.addItem(mContext, item, true);
+                }
+            }
+        });
+    }
+
+    /**
+     * Removes the specified item from the database
+     */
+    public void deleteItemFromDatabase(ItemInfo item) {
+        deleteItemsFromDatabase(Arrays.asList(item));
+    }
+
+    /**
+     * Removes all the items from the database matching {@param matcher}.
+     */
+    public void deleteItemsFromDatabase(ItemInfoMatcher matcher) {
+        deleteItemsFromDatabase(matcher.filterItemInfos(mBgDataModel.itemsIdMap));
+    }
+
+    /**
+     * Removes the specified items from the database
+     */
+    public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
+        mWorkerExecutor.execute(new Runnable() {
+            public void run() {
+                for (ItemInfo item : items) {
+                    final Uri uri = Favorites.getContentUri(item.id);
+                    mContext.getContentResolver().delete(uri, null, null);
+
+                    mBgDataModel.removeItem(mContext, item);
+                }
+            }
+        });
+    }
+
+    /**
+     * Remove the specified folder and all its contents from the database.
+     */
+    public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
+        mWorkerExecutor.execute(new Runnable() {
+            public void run() {
+                ContentResolver cr = mContext.getContentResolver();
+                cr.delete(LauncherSettings.Favorites.CONTENT_URI,
+                        LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
+                mBgDataModel.removeItem(mContext, info.contents);
+                info.contents.clear();
+
+                cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
+                mBgDataModel.removeItem(mContext, info);
+            }
+        });
+    }
+
+    private class UpdateItemRunnable extends UpdateItemBaseRunnable {
+        private final ItemInfo mItem;
+        private final ContentWriter mWriter;
+        private final long mItemId;
+
+        UpdateItemRunnable(ItemInfo item, ContentWriter writer) {
+            mItem = item;
+            mWriter = writer;
+            mItemId = item.id;
+        }
+
+        @Override
+        public void run() {
+            Uri uri = Favorites.getContentUri(mItemId);
+            mContext.getContentResolver().update(uri, mWriter.getValues(mContext), null, null);
+            updateItemArrays(mItem, mItemId);
+        }
+    }
+
+    private class UpdateItemsRunnable extends UpdateItemBaseRunnable {
+        private final ArrayList<ContentValues> mValues;
+        private final ArrayList<ItemInfo> mItems;
+
+        UpdateItemsRunnable(ArrayList<ItemInfo> items, ArrayList<ContentValues> values) {
+            mValues = values;
+            mItems = items;
+        }
+
+        @Override
+        public void run() {
+            ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+            int count = mItems.size();
+            for (int i = 0; i < count; i++) {
+                ItemInfo item = mItems.get(i);
+                final long itemId = item.id;
+                final Uri uri = Favorites.getContentUri(itemId);
+                ContentValues values = mValues.get(i);
+
+                ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
+                updateItemArrays(item, itemId);
+            }
+            try {
+                mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private abstract class UpdateItemBaseRunnable implements Runnable {
+        private final StackTraceElement[] mStackTrace;
+
+        UpdateItemBaseRunnable() {
+            mStackTrace = new Throwable().getStackTrace();
+        }
+
+        protected void updateItemArrays(ItemInfo item, long itemId) {
+            // Lock on mBgLock *after* the db operation
+            synchronized (mBgDataModel) {
+                checkItemInfoLocked(itemId, item, mStackTrace);
+
+                if (item.container != Favorites.CONTAINER_DESKTOP &&
+                        item.container != Favorites.CONTAINER_HOTSEAT) {
+                    // Item is in a folder, make sure this folder exists
+                    if (!mBgDataModel.folders.containsKey(item.container)) {
+                        // An items container is being set to a that of an item which is not in
+                        // the list of Folders.
+                        String msg = "item: " + item + " container being set to: " +
+                                item.container + ", not in the list of folders";
+                        Log.e(TAG, msg);
+                    }
+                }
+
+                // Items are added/removed from the corresponding FolderInfo elsewhere, such
+                // as in Workspace.onDrop. Here, we just add/remove them from the list of items
+                // that are on the desktop, as appropriate
+                ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
+                if (modelItem != null &&
+                        (modelItem.container == Favorites.CONTAINER_DESKTOP ||
+                                modelItem.container == Favorites.CONTAINER_HOTSEAT)) {
+                    switch (modelItem.itemType) {
+                        case Favorites.ITEM_TYPE_APPLICATION:
+                        case Favorites.ITEM_TYPE_SHORTCUT:
+                        case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                        case Favorites.ITEM_TYPE_FOLDER:
+                            if (!mBgDataModel.workspaceItems.contains(modelItem)) {
+                                mBgDataModel.workspaceItems.add(modelItem);
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                } else {
+                    mBgDataModel.workspaceItems.remove(modelItem);
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 211b979..ee7186a 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -270,7 +270,7 @@
                             updatedShortcuts.add(si);
                         }
                         if (infoUpdated) {
-                            LauncherModel.updateItemInDatabase(context, si);
+                            getModelWriter().updateItemInDatabase(si);
                         }
                     } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
                         LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
@@ -287,7 +287,7 @@
                             widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
 
                             widgets.add(widgetInfo);
-                            LauncherModel.updateItemInDatabase(context, widgetInfo);
+                            getModelWriter().updateItemInDatabase(widgetInfo);
                         }
                     }
                 }
@@ -295,7 +295,7 @@
 
             bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser);
             if (!removedShortcuts.isEmpty()) {
-                LauncherModel.deleteItemsFromDatabase(context, removedShortcuts);
+                getModelWriter().deleteItemsFromDatabase(removedShortcuts);
             }
 
             if (!widgets.isEmpty()) {
@@ -332,10 +332,10 @@
         }
 
         if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
-            LauncherModel.deleteItemsFromDatabase(
-                    context, ItemInfoMatcher.ofPackages(removedPackages, mUser));
-            LauncherModel.deleteItemsFromDatabase(
-                    context, ItemInfoMatcher.ofComponents(removedComponents, mUser));
+            getModelWriter().deleteItemsFromDatabase(
+                    ItemInfoMatcher.ofPackages(removedPackages, mUser));
+            getModelWriter().deleteItemsFromDatabase(
+                    ItemInfoMatcher.ofComponents(removedComponents, mUser));
 
             // Remove any queued items from the install queue
             InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index ba7112f..d8a429c 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -104,7 +104,7 @@
 
         bindUpdatedShortcuts(updatedShortcutInfos, removedShortcutInfos, mUser);
         if (!removedShortcutInfos.isEmpty()) {
-            LauncherModel.deleteItemsFromDatabase(context, removedShortcutInfos);
+            getModelWriter().deleteItemsFromDatabase(removedShortcutInfos);
         }
 
         if (mUpdateIdMap) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 25f2f9d..363f1ee 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -95,7 +95,7 @@
         }
         bindUpdatedShortcuts(updatedShortcutInfos, deletedShortcutInfos, mUser);
         if (!deletedShortcutInfos.isEmpty()) {
-            LauncherModel.deleteItemsFromDatabase(context, deletedShortcutInfos);
+            getModelWriter().deleteItemsFromDatabase(deletedShortcutInfos);
         }
 
         // Remove shortcut id map for that user
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index af61554..577d19f 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -203,7 +203,7 @@
             long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) {
         for (ItemInfo info : workFolderApps) {
             info.rank = startingRank++;
-            LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0);
+            mModel.getWriter(false).addItemToDatabase(info, workFolderId, 0, 0, 0);
         }
     }