Supporting custom widgets provided by launcher

-> This change provides integration for widgets provided by
   the launcher package which can run arbitrary code.

Change-Id: I6052da5c4afed7ee72e3b44d045b9c46f2d84c42
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index f57f4d0..2402507 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -8,6 +8,7 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.view.Gravity;
 import android.widget.FrameLayout;
@@ -78,13 +79,13 @@
         mLauncher = (Launcher) context;
         mCellLayout = cellLayout;
         mWidgetView = widgetView;
-        mResizeMode = widgetView.getAppWidgetInfo().resizeMode;
+        LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
+                widgetView.getAppWidgetInfo();
+        mResizeMode = info.resizeMode;
         mDragLayer = dragLayer;
 
-        final AppWidgetProviderInfo info = widgetView.getAppWidgetInfo();
-        int[] result = Launcher.getMinSpanForWidget(mLauncher, info);
-        mMinHSpan = result[0];
-        mMinVSpan = result[1];
+        mMinHSpan = info.minSpanX;
+        mMinVSpan = info.minSpanY;
 
         setBackgroundResource(R.drawable.widget_resize_frame_holo);
         setPadding(0, 0, 0, 0);
@@ -114,8 +115,16 @@
                 Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
         addView(mBottomHandle, lp);
 
-        Rect p = AppWidgetHostView.getDefaultPaddingForWidget(context,
-                widgetView.getAppWidgetInfo().provider, null);
+        Rect p = new Rect(0, 0, 0, 0);
+        if (!info.isCustomWidget) {
+            p = AppWidgetHostView.getDefaultPaddingForWidget(context,
+                    widgetView.getAppWidgetInfo().provider, null);
+        } else {
+            Resources r = context.getResources();
+            int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding);
+            p.set(padding, padding, padding, padding);
+        }
+
         mWidgetPaddingLeft = p.left;
         mWidgetPaddingTop = p.top;
         mWidgetPaddingRight = p.right;
@@ -335,7 +344,6 @@
 
     static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
             int spanX, int spanY) {
-
         getWidgetSizeRanges(launcher, spanX, spanY, mTmpRect);
         widgetView.updateAppWidgetSize(null, mTmpRect.left, mTmpRect.top,
                 mTmpRect.right, mTmpRect.bottom);
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index c8187f0..9e7e523 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -383,12 +383,13 @@
         // Get the list of widgets and shortcuts
         mWidgets.clear();
         for (Object o : widgetsAndShortcuts) {
-            if (o instanceof AppWidgetProviderInfo) {
-                AppWidgetProviderInfo widget = (AppWidgetProviderInfo) o;
-                if (!app.shouldShowAppOrWidgetProvider(widget.provider)) {
+            if (o instanceof LauncherAppWidgetProviderInfo) {
+                LauncherAppWidgetProviderInfo widget = (LauncherAppWidgetProviderInfo) o;
+                if (!app.shouldShowAppOrWidgetProvider(widget.provider) && !widget.isCustomWidget) {
                     continue;
                 }
-                if (widget.minWidth > 0 && widget.minHeight > 0) {
+
+                if (widget.minSpanX > 0 && widget.minSpanY > 0) {
                     // Ensure that all widgets we show can be added on a workspace of this size
                     int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget);
                     int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget);
@@ -410,6 +411,7 @@
                 mWidgets.add(o);
             }
         }
+
         updatePageCountsAndInvalidateData();
     }
 
@@ -503,8 +505,9 @@
     }
 
     private void preloadWidget(final PendingAddWidgetInfo info) {
-        final AppWidgetProviderInfo pInfo = info.info;
-        final Bundle options = getDefaultOptionsForWidget(mLauncher, info);
+        final LauncherAppWidgetProviderInfo pInfo = info.info;
+        final Bundle options = pInfo.isCustomWidget ? null :
+                getDefaultOptionsForWidget(mLauncher, info);
 
         if (pInfo.configure != null) {
             info.bindOptions = options;
@@ -515,11 +518,17 @@
         mBindWidgetRunnable = new Runnable() {
             @Override
             public void run() {
+                if (pInfo.isCustomWidget) {
+                    mWidgetCleanupState = WIDGET_BOUND;
+                    return;
+                }
+
                 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
                 if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
                         mWidgetLoadingId, pInfo, options)) {
                     mWidgetCleanupState = WIDGET_BOUND;
                 }
+
             }
         };
         post(mBindWidgetRunnable);
@@ -530,8 +539,8 @@
                 if (mWidgetCleanupState != WIDGET_BOUND) {
                     return;
                 }
-                AppWidgetHostView hostView = mLauncher.
-                        getAppWidgetHost().createView(getContext(), mWidgetLoadingId, pInfo);
+                AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
+                        getContext(), mWidgetLoadingId, pInfo);
                 info.boundWidget = hostView;
                 mWidgetCleanupState = WIDGET_INFLATED;
                 hostView.setVisibility(INVISIBLE);
@@ -575,7 +584,7 @@
                 removeCallbacks(mInflateWidgetRunnable);
             } else if (mWidgetCleanupState == WIDGET_BOUND) {
                  // Delete the widget id which was allocated
-                if (mWidgetLoadingId != -1) {
+                if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
                     mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
                 }
 
@@ -583,7 +592,7 @@
                 removeCallbacks(mInflateWidgetRunnable);
             } else if (mWidgetCleanupState == WIDGET_INFLATED) {
                 // Delete the widget id which was allocated
-                if (mWidgetLoadingId != -1) {
+                if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
                     mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
                 }
 
@@ -645,7 +654,6 @@
             maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]);
 
             int[] previewSizeBeforeScale = new int[1];
-
             preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
                     spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale);
 
@@ -1125,20 +1133,13 @@
             PendingAddItemInfo createItemInfo = null;
             PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate(
                     R.layout.apps_customize_widget, layout, false);
-            if (rawInfo instanceof AppWidgetProviderInfo) {
+
+            if (rawInfo instanceof LauncherAppWidgetProviderInfo) {
                 // Fill in the widget information
-                AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo;
-                createItemInfo = new PendingAddWidgetInfo(info, null, null);
+                LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) rawInfo;
+                createItemInfo = new PendingAddWidgetInfo(info, null);
 
-                // Determine the widget spans and min resize spans.
-                int[] spanXY = Launcher.getSpanForWidget(mLauncher, info);
-                createItemInfo.spanX = spanXY[0];
-                createItemInfo.spanY = spanXY[1];
-                int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, info);
-                createItemInfo.minSpanX = minSpanXY[0];
-                createItemInfo.minSpanY = minSpanXY[1];
-
-                widget.applyFromAppWidgetProviderInfo(info, -1, spanXY, getWidgetPreviewLoader());
+                widget.applyFromAppWidgetProviderInfo(info, -1, getWidgetPreviewLoader());
                 widget.setTag(createItemInfo);
                 widget.setShortPressListener(this);
             } else if (rawInfo instanceof ResolveInfo) {
@@ -1151,6 +1152,7 @@
                 widget.applyFromResolveInfo(mPackageManager, info, getWidgetPreviewLoader());
                 widget.setTag(createItemInfo);
             }
+
             widget.setOnClickListener(this);
             widget.setOnLongClickListener(this);
             widget.setOnTouchListener(this);
@@ -1421,6 +1423,11 @@
             updatePageCountsAndInvalidateData();
         }
     }
+
+    public ArrayList<AppInfo> getApps() {
+        return mApps;
+    }
+
     private void addAppsWithoutInvalidate(ArrayList<AppInfo> list) {
         // We add it in place, in alphabetical order
         int count = list.size();
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 0ff1ef4..7424d61 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -592,7 +592,7 @@
     }
 
     public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
-            boolean markCells) {
+            boolean markCells, boolean inLayout) {
         final LayoutParams lp = params;
 
         // Hotseat icons - remove text
@@ -613,8 +613,11 @@
             if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
 
             child.setId(childId);
-
-            mShortcutsAndWidgets.addView(child, index, lp);
+            if (inLayout) {
+                mShortcutsAndWidgets.addView(child, index, lp, true);
+            } else {
+                mShortcutsAndWidgets.addView(child, index, lp, false);
+            }
 
             if (markCells) markCellsAsOccupiedForView(child);
 
@@ -623,6 +626,11 @@
         return false;
     }
 
+    public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
+            boolean markCells) {
+        return addViewToCellLayout(child, index, childId, params, markCells, false);
+    }
+
     @Override
     public void removeAllViews() {
         clearOccupiedCells();
diff --git a/src/com/android/launcher3/CustomAppWidget.java b/src/com/android/launcher3/CustomAppWidget.java
new file mode 100644
index 0000000..1b4ed79
--- /dev/null
+++ b/src/com/android/launcher3/CustomAppWidget.java
@@ -0,0 +1,14 @@
+package com.android.launcher3;
+
+public interface CustomAppWidget {
+    public String getLabel();
+    public int getPreviewImage();
+    public int getIcon();
+    public int getWidgetLayout();
+
+    public int getSpanX();
+    public int getSpanY();
+    public int getMinSpanX();
+    public int getMinSpanY();
+    public int getResizeMode();
+}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index ea058ea..5a5c002 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -105,6 +105,7 @@
                 switch (addInfo.itemType) {
                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                     case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                    case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                         return true;
                 }
             }
@@ -146,6 +147,7 @@
         if (info instanceof ItemInfo) {
             ItemInfo item = (ItemInfo) info;
             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
+                    item.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ||
                     item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                 return true;
             }
@@ -341,7 +343,9 @@
 
             final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
             final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
-            if ((appWidgetHost != null) && launcherAppWidgetInfo.isWidgetIdValid()) {
+
+            if (appWidgetHost != null && !launcherAppWidgetInfo.isCustomWidget()
+                    && launcherAppWidgetInfo.isWidgetIdValid()) {
                 // Deleting an app widget ID is a void call but writes to disk before returning
                 // to the caller...
                 new AsyncTask<Void, Void, Void>() {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index d6aadce..34e1f3c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -67,8 +67,8 @@
     String name;
     float minWidthDps;
     float minHeightDps;
-    float numRows;
-    float numColumns;
+    public float numRows;
+    public float numColumns;
     float numHotseatIcons;
     float iconSize;
     private float iconTextSize;
@@ -84,13 +84,13 @@
     boolean transposeLayoutWithOrientation;
 
     int desiredWorkspaceLeftRightMarginPx;
-    int edgeMarginPx;
+    public int edgeMarginPx;
     Rect defaultWidgetPadding;
 
     int widthPx;
     int heightPx;
-    int availableWidthPx;
-    int availableHeightPx;
+    public int availableWidthPx;
+    public int availableHeightPx;
     int defaultPageSpacingPx;
 
     int overviewModeMinIconZoneHeightPx;
@@ -100,11 +100,12 @@
     float overviewModeIconZoneRatio;
     float overviewModeScaleFactor;
 
+    public int cellWidthPx;
+    public int cellHeightPx;
+
     int iconSizePx;
     int iconTextSizePx;
     int iconDrawablePaddingPx;
-    int cellWidthPx;
-    int cellHeightPx;
     int allAppsIconSizePx;
     int allAppsIconTextSizePx;
     int allAppsCellWidthPx;
@@ -578,6 +579,16 @@
         }
     }
 
+    public int getWorkspaceGridHeight() {
+        Rect p = getWorkspacePadding();
+        return availableHeightPx - p.top - p.bottom;
+    }
+
+    public int getWorkspaceGridWidth() {
+        Rect p = getWorkspacePadding();
+        return availableWidthPx - p.left - p.right;
+    }
+
     /** Returns the workspace padding in the specified orientation */
     Rect getWorkspacePadding() {
         return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
diff --git a/src/com/android/launcher3/DummyWidget.java b/src/com/android/launcher3/DummyWidget.java
new file mode 100644
index 0000000..59cd805
--- /dev/null
+++ b/src/com/android/launcher3/DummyWidget.java
@@ -0,0 +1,50 @@
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetProviderInfo;
+
+public class DummyWidget implements CustomAppWidget {
+    @Override
+    public String getLabel() {
+        return "Dumb Launcher Widget";
+    }
+
+    @Override
+    public int getPreviewImage() {
+        return 0;
+    }
+
+    @Override
+    public int getIcon() {
+        return 0;
+    }
+
+    @Override
+    public int getWidgetLayout() {
+        return R.layout.dummy_widget;
+    }
+
+    @Override
+    public int getSpanX() {
+        return 2;
+    }
+
+    @Override
+    public int getSpanY() {
+        return 2;
+    }
+
+    @Override
+    public int getMinSpanX() {
+        return 1;
+    }
+
+    @Override
+    public int getMinSpanY() {
+        return 1;
+    }
+
+    @Override
+    public int getResizeMode() {
+        return AppWidgetProviderInfo.RESIZE_BOTH;
+    }
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ac46fd3..0ceb862 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -260,7 +260,7 @@
     private LauncherAppWidgetHost mAppWidgetHost;
 
     private ItemInfo mPendingAddInfo = new ItemInfo();
-    private AppWidgetProviderInfo mPendingAddWidgetInfo;
+    private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
     private int mPendingAddWidgetId = -1;
 
     private int[] mTmpAddItemCellCoordinates = new int[2];
@@ -351,6 +351,16 @@
 
     private BubbleTextView mWaitingForResume;
 
+    protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
+            new HashMap<String, CustomAppWidget>();
+
+    private static final boolean ENABLE_CUSTOM_WIDGET_TEST = false;
+    static {
+        if (ENABLE_CUSTOM_WIDGET_TEST) {
+            sCustomAppWidgets.put(DummyWidget.class.getName(), new DummyWidget());
+        }
+    }
+
     private Runnable mBuildLayersRunnable = new Runnable() {
         public void run() {
             if (mWorkspace != null) {
@@ -760,7 +770,7 @@
                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
             } else if (resultCode == RESULT_OK) {
-                addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
+                addAppWidgetImpl(appWidgetId, (PendingAddWidgetInfo) mPendingAddInfo, null,
                         mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
             }
             return;
@@ -1487,7 +1497,7 @@
      *
      * @return A View inflated from layoutResId.
      */
-    View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
+    public View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
         BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
         favorite.applyFromShortcutInfo(info, mIconCache, true);
         favorite.setOnClickListener(this);
@@ -1586,86 +1596,45 @@
      * @param appWidgetId The app widget id
      * @param cellInfo The position on screen where to create the widget.
      */
-    private void completeAddAppWidget(final int appWidgetId, long container, long screenId,
-            AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
+    private void completeAddAppWidget(int appWidgetId, long container, long screenId,
+            AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
+
+        ItemInfo info = mPendingAddInfo;
         if (appWidgetInfo == null) {
-            appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+            appWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this,
+                    mAppWidgetManager.getAppWidgetInfo(appWidgetId));
         }
 
-        // Calculate the grid spans needed to fit this widget
-        CellLayout layout = getCellLayout(container, screenId);
-
-        int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo);
-        int[] spanXY = getSpanForWidget(this, appWidgetInfo);
-
-        // Try finding open space on Launcher screen
-        // We have saved the position to which the widget was dragged-- this really only matters
-        // if we are placing widgets on a "spring-loaded" screen
-        int[] cellXY = mTmpAddItemCellCoordinates;
-        int[] touchXY = mPendingAddInfo.dropPos;
-        int[] finalSpan = new int[2];
-        boolean foundCellSpan = false;
-        if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
-            cellXY[0] = mPendingAddInfo.cellX;
-            cellXY[1] = mPendingAddInfo.cellY;
-            spanXY[0] = mPendingAddInfo.spanX;
-            spanXY[1] = mPendingAddInfo.spanY;
-            foundCellSpan = true;
-        } else if (touchXY != null) {
-            // when dragging and dropping, just find the closest free spot
-            int[] result = layout.findNearestVacantArea(
-                    touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
-                    spanXY[1], cellXY, finalSpan);
-            spanXY[0] = finalSpan[0];
-            spanXY[1] = finalSpan[1];
-            foundCellSpan = (result != null);
-        } else {
-            foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
+        if (appWidgetInfo.isCustomWidget) {
+            appWidgetId = LauncherAppWidgetInfo.CUSTOM_WIDGET_ID;
         }
 
-        if (!foundCellSpan) {
-            if (appWidgetId != -1) {
-                // Deleting an app widget ID is a void call but writes to disk before returning
-                // to the caller...
-                new AsyncTask<Void, Void, Void>() {
-                    public Void doInBackground(Void ... args) {
-                        mAppWidgetHost.deleteAppWidgetId(appWidgetId);
-                        return null;
-                    }
-                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
-            }
-            showOutOfSpaceMessage(isHotseatLayout(layout));
-            return;
-        }
-
-        // Build Launcher-specific widget info and save to database
-        LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId,
-                appWidgetInfo.provider);
-        launcherInfo.spanX = spanXY[0];
-        launcherInfo.spanY = spanXY[1];
-        launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
-        launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
+        LauncherAppWidgetInfo launcherInfo;
+        launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
+        launcherInfo.spanX = info.spanX;
+        launcherInfo.spanY = info.spanY;
+        launcherInfo.minSpanX = info.minSpanX;
+        launcherInfo.minSpanY = info.minSpanY;
         launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
 
         LauncherModel.addItemToDatabase(this, launcherInfo,
-                container, screenId, cellXY[0], cellXY[1], false);
+                container, screenId, info.cellX, info.cellY, false);
 
         if (!mRestoring) {
             if (hostView == null) {
                 // Perform actual inflation because we're live
-                launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
-                launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+                launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId,
+                        appWidgetInfo);
             } else {
                 // The AppWidgetHostView has already been inflated and instantiated
                 launcherInfo.hostView = hostView;
             }
-
             launcherInfo.hostView.setTag(launcherInfo);
             launcherInfo.hostView.setVisibility(View.VISIBLE);
             launcherInfo.notifyWidgetSizeChanged(this);
 
-            mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, cellXY[0], cellXY[1],
-                    launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
+            mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, info.cellX,
+                    info.cellY, launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
 
             addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
         }
@@ -1896,6 +1865,10 @@
         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
     }
 
+    public ArrayList<AppInfo> getAllAppsList() {
+        return mAppsCustomizeContent.getApps();
+    }
+
     public DragLayer getDragLayer() {
         return mDragLayer;
     }
@@ -2284,14 +2257,14 @@
         mPendingAddInfo.dropPos = null;
     }
 
-    void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
-            final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) {
+    void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, final
+            AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) {
         addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
     }
 
-    void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
-            final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int
-            delay) {
+    void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info,
+            final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo,
+            int delay) {
         if (appWidgetInfo.configure != null) {
             mPendingAddWidgetInfo = appWidgetInfo;
             mPendingAddWidgetId = appWidgetId;
@@ -2585,7 +2558,8 @@
             int widgetId = info.appWidgetId;
             AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
             if (appWidgetInfo != null) {
-                mPendingAddWidgetInfo = appWidgetInfo;
+                mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
+                        this, appWidgetInfo);
                 mPendingAddInfo.copyFrom(info);
                 mPendingAddWidgetId = widgetId;
 
@@ -4373,12 +4347,12 @@
         }
         final Workspace workspace = mWorkspace;
 
-        AppWidgetProviderInfo appWidgetInfo;
+        LauncherAppWidgetProviderInfo appWidgetInfo =
+                LauncherModel.getProviderInfo(this, item.providerName);
+
         if (!mIsSafeModeEnabled
                 && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
                 && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
-
-            appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName);
             if (appWidgetInfo == null) {
                 if (DEBUG_WIDGETS) {
                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
@@ -4391,7 +4365,7 @@
             // Note: This assumes that the id remap broadcast is received before this step.
             // If that is not the case, the id remap will be ignored and user may see the
             // click to setup view.
-            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null);
+            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null);
             pendingInfo.spanX = item.spanX;
             pendingInfo.spanY = item.spanY;
             pendingInfo.minSpanX = item.minSpanX;
@@ -4428,9 +4402,9 @@
 
         if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
             final int appWidgetId = item.appWidgetId;
-            appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
             if (DEBUG_WIDGETS) {
-                Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
+                Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
+                        + appWidgetInfo.provider);
             }
 
             item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
@@ -4449,7 +4423,9 @@
 
         workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
                 item.cellY, item.spanX, item.spanY, false);
-        addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
+        if (!item.isCustomWidget()) {
+            addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
+        }
 
         workspace.requestLayout();
 
@@ -5125,6 +5101,14 @@
         }
     }
 
+    public static CustomAppWidget getCustomAppWidget(String name) {
+        return sCustomAppWidgets.get(name);
+    }
+
+    public static HashMap<String, CustomAppWidget> getCustomAppWidgets() {
+        return sCustomAppWidgets;
+    }
+
     public void dumpLogsToLocalData() {
         if (DEBUG_DUMP_LOG) {
             new AsyncTask<Void, Void, Void>() {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index a309f26..840508a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -21,9 +21,11 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.os.TransactionTooLargeException;
+import android.view.LayoutInflater;
 
 import java.util.ArrayList;
 
+
 /**
  * Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
  * which correctly captures all long-press events. This ensures that users can
@@ -85,4 +87,29 @@
             callback.run();
         }
     }
+
+    public AppWidgetHostView createView(Context context, int appWidgetId,
+            LauncherAppWidgetProviderInfo appWidget) {
+        if (appWidget.isCustomWidget) {
+            LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
+            LayoutInflater inflater = (LayoutInflater)
+                    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            inflater.inflate(appWidget.initialLayout, lahv);
+            lahv.setAppWidget(0, appWidget);
+            lahv.updateLastInflationOrientation();
+            return lahv;
+        } else {
+            return super.createView(context, appWidgetId, appWidget);
+        }
+    }
+
+    /**
+     * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
+     */
+    @Override
+    protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
+        LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
+                mLauncher, appWidget);
+        super.onProviderChanged(appWidgetId, info);
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index e39727b..ebe55ab 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -54,10 +55,14 @@
         return mInflater.inflate(R.layout.appwidget_error, this, false);
     }
 
+    public void updateLastInflationOrientation() {
+        mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
+    }
+
     @Override
     public void updateAppWidget(RemoteViews remoteViews) {
         // Store the orientation in which the widget was inflated
-        mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
+        updateLastInflationOrientation();
         super.updateAppWidget(remoteViews);
     }
 
@@ -137,6 +142,16 @@
     }
 
     @Override
+    public AppWidgetProviderInfo getAppWidgetInfo() {
+        AppWidgetProviderInfo info = super.getAppWidgetInfo();
+        if (!(info instanceof LauncherAppWidgetProviderInfo)) {
+            throw new IllegalStateException("Launcher widget must have"
+                    + "LauncherAppWidgetProviderInfo");
+        }
+        return info;
+    }
+
+    @Override
     public void onTouchComplete() {
         if (!mLongPressHelper.hasPerformedLongPress()) {
             // If a long press has been performed, we don't want to clear the record of that since
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 5c6535a..aad18b5 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -56,6 +56,11 @@
     static final int NO_ID = -1;
 
     /**
+     * Indicates that this is a locally defined widget and hence has no system allocated id.
+     */
+    static final int CUSTOM_WIDGET_ID = -100;
+
+    /**
      * Identifier for this widget when talking with
      * {@link android.appwidget.AppWidgetManager} for updates.
      */
@@ -86,7 +91,12 @@
     AppWidgetHostView hostView = null;
 
     LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
-        itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+        if (appWidgetId == CUSTOM_WIDGET_ID) {
+            itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+        } else {
+            itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+        }
+
         this.appWidgetId = appWidgetId;
         this.providerName = providerName;
 
@@ -99,6 +109,10 @@
         restoreStatus = RESTORE_COMPLETED;
     }
 
+    public boolean isCustomWidget() {
+        return appWidgetId == CUSTOM_WIDGET_ID;
+    }
+
     @Override
     void onAddToDatabase(Context context, ContentValues values) {
         super.onAddToDatabase(context, values);
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
new file mode 100644
index 0000000..e7f49b2
--- /dev/null
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -0,0 +1,89 @@
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+
+import java.lang.reflect.Field;
+
+/**
+ * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
+ * a common object for describing both framework provided AppWidgets as well as custom widgets
+ * (who's implementation is owned by the launcher). This object represents a widget type / class,
+ * as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
+ */
+public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
+
+    public boolean isCustomWidget = false;
+    int spanX = -1;
+    int spanY = -1;
+    int minSpanX = -1;
+    int minSpanY = -1;
+
+    public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
+            AppWidgetProviderInfo info) {
+
+        // In lieu of a public super copy constructor, we first write the AppWidgetProviderInfo
+        // into a parcel, and then construct a new LauncherAppWidgetProvider info from the
+        // associated super parcel constructor. This allows us to copy non-public members without
+        // using reflection.
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        LauncherAppWidgetProviderInfo lawpi = new LauncherAppWidgetProviderInfo(p);
+
+        int[] minResizeSpan = Launcher.getMinSpanForWidget(context, lawpi);
+        int[] span = Launcher.getSpanForWidget(context, lawpi);
+
+        lawpi.spanX = span[0];
+        lawpi.spanY = span[1];
+        lawpi.minSpanX = minResizeSpan[0];
+        lawpi.minSpanY = minResizeSpan[1];
+
+        return lawpi;
+    }
+
+    public LauncherAppWidgetProviderInfo(Parcel in) {
+        super(in);
+    }
+
+    public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) {
+        isCustomWidget = true;
+
+        provider = new ComponentName(context, widget.getClass().getName());
+        icon = widget.getIcon();
+        label = widget.getLabel();
+        previewImage = widget.getPreviewImage();
+        initialLayout = widget.getWidgetLayout();
+        resizeMode = widget.getResizeMode();
+
+        spanX = widget.getSpanX();
+        spanY = widget.getSpanY();
+        minSpanX = widget.getMinSpanX();
+        minSpanY = widget.getMinSpanY();
+    }
+
+    public String getLabel(PackageManager packageManager) {
+        if (isCustomWidget) {
+            return label.toString().trim();
+        }
+        return super.loadLabel(packageManager);
+    }
+
+    public Drawable getIcon(Context context, IconCache cache) {
+        if (isCustomWidget) {
+            return cache.getFullResIcon(provider.getPackageName(), icon);
+        }
+        return super.loadIcon(context, cache.getFullResIconDpi());
+    }
+
+    public String toString() {
+        if (isCustomWidget) {
+            return "LauncherAppWidgetProviderInfo(" + provider + ")";
+        }
+        return super.toString();
+    }
+ }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index c260fbc..7f3a798 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -906,7 +906,8 @@
     /** Serialize a widget for persistence, including a checksum wrapper. */
     private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache,
             ComponentName provider) {
-        final AppWidgetProviderInfo info = findAppWidgetProviderInfo(provider);
+        final LauncherAppWidgetProviderInfo info =
+                LauncherModel.getProviderInfo(mContext, provider);
         Widget widget = new Widget();
         widget.provider = provider.flattenToShortString();
         widget.label = info.label;
@@ -1074,19 +1075,6 @@
         return wrapper.payload;
     }
 
-    private AppWidgetProviderInfo findAppWidgetProviderInfo(ComponentName component) {
-        if (mWidgetMap == null) {
-            List<AppWidgetProviderInfo> widgets =
-                    AppWidgetManager.getInstance(mContext).getInstalledProviders();
-            mWidgetMap = new HashMap<ComponentName, AppWidgetProviderInfo>(widgets.size());
-            for (AppWidgetProviderInfo info : widgets) {
-                mWidgetMap.put(info.provider, info);
-            }
-        }
-        return mWidgetMap.get(component);
-    }
-
-
     private boolean initializeIconCache() {
         if (mIconCache != null) {
             return true;
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 954887d..3c1cf48 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.app.SearchManager;
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -174,6 +173,10 @@
     // sBgWorkspaceScreens is the ordered set of workspace screens.
     static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
 
+    // sBgWidgetProviders is the set of widget providers including custom internal widgets
+    public static HashMap<ComponentName, LauncherAppWidgetProviderInfo> sBgWidgetProviders;
+    public static boolean sWidgetProvidersDirty;
+
     // sPendingPackages is a set of packages which could be on sdcard and are not available yet
     static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
             new HashMap<UserHandleCompat, HashSet<String>>();
@@ -1265,7 +1268,6 @@
                     PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
                     user));
         }
-
     }
 
     /**
@@ -1837,7 +1839,6 @@
             final Context context = mContext;
             final ContentResolver contentResolver = context.getContentResolver();
             final PackageManager manager = context.getPackageManager();
-            final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
             final boolean isSafeMode = manager.isSafeMode();
             final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
             final boolean isSdCardReady = context.registerReceiver(null,
@@ -2174,7 +2175,11 @@
                                 break;
 
                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                            case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                                 // Read all Launcher-specific widget details
+                                boolean customWidget = itemType ==
+                                    LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+
                                 int appWidgetId = c.getInt(appWidgetIdIndex);
                                 String savedProvider = c.getString(appWidgetProviderIndex);
                                 id = c.getLong(idIndex);
@@ -2188,14 +2193,16 @@
                                 final boolean wasProviderReady = (restoreStatus &
                                         LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
 
-                                final AppWidgetProviderInfo provider = isIdValid
-                                        ? widgets.getAppWidgetInfo(appWidgetId)
-                                        : findAppWidgetProviderInfoWithComponent(context, component);
+                                final LauncherAppWidgetProviderInfo provider =
+                                        LauncherModel.getProviderInfo(context,
+                                                ComponentName.unflattenFromString(savedProvider));
 
                                 final boolean isProviderReady = isValidProvider(provider);
-                                if (!isSafeMode && wasProviderReady && !isProviderReady) {
+                                if (!isSafeMode && !customWidget &&
+                                        wasProviderReady && !isProviderReady) {
                                     String log = "Deleting widget that isn't installed anymore: "
                                             + "id=" + id + " appWidgetId=" + appWidgetId;
+
                                     Log.e(TAG, log);
                                     Launcher.addDumpLog(TAG, log, false);
                                     itemsToRemove.add(id);
@@ -2203,10 +2210,13 @@
                                     if (isProviderReady) {
                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                                 provider.provider);
-                                        int[] minSpan =
-                                                Launcher.getMinSpanForWidget(context, provider);
-                                        appWidgetInfo.minSpanX = minSpan[0];
-                                        appWidgetInfo.minSpanY = minSpan[1];
+
+                                        if (!customWidget) {
+                                            int[] minSpan =
+                                                    Launcher.getMinSpanForWidget(context, provider);
+                                            appWidgetInfo.minSpanX = minSpan[0];
+                                            appWidgetInfo.minSpanY = minSpan[1];
+                                        }
 
                                         int status = restoreStatus;
                                         if (!wasProviderReady) {
@@ -2251,6 +2261,12 @@
                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
 
+                                    if (!customWidget) {
+                                        int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
+                                        appWidgetInfo.minSpanX = minSpan[0];
+                                        appWidgetInfo.minSpanY = minSpan[1];
+                                    }
+
                                     container = c.getInt(containerIndex);
                                     if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                                         container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -2266,17 +2282,21 @@
                                         break;
                                     }
 
-                                    String providerName = appWidgetInfo.providerName.flattenToString();
-                                    if (!providerName.equals(savedProvider) ||
-                                            (appWidgetInfo.restoreStatus != restoreStatus)) {
-                                        ContentValues values = new ContentValues();
-                                        values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
-                                                providerName);
-                                        values.put(LauncherSettings.Favorites.RESTORED,
-                                                appWidgetInfo.restoreStatus);
-                                        String where = BaseColumns._ID + "= ?";
-                                        String[] args = {Long.toString(id)};
-                                        contentResolver.update(contentUri, values, where, args);
+                                    if (!customWidget) {
+                                        String providerName =
+                                                appWidgetInfo.providerName.flattenToString();
+                                        if (!providerName.equals(savedProvider) ||
+                                                (appWidgetInfo.restoreStatus != restoreStatus)) {
+                                            ContentValues values = new ContentValues();
+                                            values.put(
+                                                    LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+                                                    providerName);
+                                            values.put(LauncherSettings.Favorites.RESTORED,
+                                                    appWidgetInfo.restoreStatus);
+                                            String where = BaseColumns._ID + "= ?";
+                                            String[] args = {Long.toString(id)};
+                                            contentResolver.update(contentUri, values, where, args);
+                                        }
                                     }
                                     sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
                                     sBgAppWidgets.add(appWidgetInfo);
@@ -3292,12 +3312,44 @@
         }
     }
 
+    public static List<LauncherAppWidgetProviderInfo> getWidgetProviders(Context context) {
+        synchronized (sBgLock) {
+            if (sBgWidgetProviders != null && !sWidgetProvidersDirty) {
+                return new ArrayList<LauncherAppWidgetProviderInfo>(sBgWidgetProviders.values());
+            }
+            sBgWidgetProviders = new HashMap<ComponentName, LauncherAppWidgetProviderInfo>();
+            List<AppWidgetProviderInfo> widgets =
+                    AppWidgetManagerCompat.getInstance(context).getAllProviders();
+            LauncherAppWidgetProviderInfo info;
+            for (AppWidgetProviderInfo pInfo : widgets) {
+                info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
+                sBgWidgetProviders.put(info.provider, info);
+            }
+
+            Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
+            for (CustomAppWidget widget : customWidgets) {
+                info = new LauncherAppWidgetProviderInfo(context, widget);
+                sBgWidgetProviders.put(info.provider, info);
+            }
+            sWidgetProvidersDirty = false;
+            return new ArrayList<LauncherAppWidgetProviderInfo>(sBgWidgetProviders.values());
+        }
+    }
+
+    public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name) {
+        synchronized (sBgLock) {
+            if (sBgWidgetProviders == null) {
+                getWidgetProviders(ctx);
+            }
+            return sBgWidgetProviders.get(name);
+        }
+    }
+
     // Returns a list of ResolveInfos/AppWindowInfos in sorted order
     public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
         PackageManager packageManager = context.getPackageManager();
         final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
-        widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
-
+        widgetsAndShortcuts.addAll(getWidgetProviders(context));
         Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
         widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
         Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
@@ -3583,21 +3635,6 @@
         }
     }
 
-    /**
-     * Attempts to find an AppWidgetProviderInfo that matches the given component.
-     */
-    static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
-            ComponentName component) {
-        List<AppWidgetProviderInfo> widgets =
-            AppWidgetManager.getInstance(context).getInstalledProviders();
-        for (AppWidgetProviderInfo info : widgets) {
-            if (info.provider.equals(component)) {
-                return info;
-            }
-        }
-        return null;
-    }
-
     ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
         Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
         String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
@@ -3783,16 +3820,16 @@
             if (mLabelCache.containsKey(a)) {
                 labelA = mLabelCache.get(a);
             } else {
-                labelA = (a instanceof AppWidgetProviderInfo)
-                        ? mManager.loadLabel((AppWidgetProviderInfo) a)
+                labelA = (a instanceof LauncherAppWidgetProviderInfo)
+                        ? mManager.loadLabel((LauncherAppWidgetProviderInfo) a)
                         : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
                 mLabelCache.put(a, labelA);
             }
             if (mLabelCache.containsKey(b)) {
                 labelB = mLabelCache.get(b);
             } else {
-                labelB = (b instanceof AppWidgetProviderInfo)
-                        ? mManager.loadLabel((AppWidgetProviderInfo) b)
+                labelB = (b instanceof LauncherAppWidgetProviderInfo)
+                        ? mManager.loadLabel((LauncherAppWidgetProviderInfo) a)
                         : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
                 mLabelCache.put(b, labelB);
             }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 3553702..65e3d28 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -239,6 +239,11 @@
         static final int ITEM_TYPE_APPWIDGET = 4;
 
         /**
+         * The favorite is a custom widget provided by the launcher
+         */
+        static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
+
+        /**
          * The favorite is a clock
          */
         static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java
index e6e11a3..41270e3 100644
--- a/src/com/android/launcher3/PagedViewWidget.java
+++ b/src/com/android/launcher3/PagedViewWidget.java
@@ -117,8 +117,8 @@
         }
     }
 
-    public void applyFromAppWidgetProviderInfo(AppWidgetProviderInfo info,
-            int maxWidth, int[] cellSpan, WidgetPreviewLoader loader) {
+    public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
+            int maxWidth, WidgetPreviewLoader loader) {
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
@@ -132,8 +132,8 @@
         name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
         final TextView dims = (TextView) findViewById(R.id.widget_dims);
         if (dims != null) {
-            int hSpan = Math.min(cellSpan[0], (int) grid.numColumns);
-            int vSpan = Math.min(cellSpan[1], (int) grid.numRows);
+            int hSpan = Math.min(info.spanX, (int) grid.numColumns);
+            int vSpan = Math.min(info.spanY, (int) grid.numRows);
             dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
         }
         mWidgetPreviewLoader = loader;
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index 967cc92..ac54a26 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.os.Bundle;
@@ -54,17 +53,16 @@
     int minResizeHeight;
     int previewImage;
     int icon;
-    AppWidgetProviderInfo info;
+    LauncherAppWidgetProviderInfo info;
     AppWidgetHostView boundWidget;
     Bundle bindOptions = null;
 
-    // Any configuration data that we want to pass to a configuration activity when
-    // starting up a widget
-    String mimeType;
-    Parcelable configurationData;
-
-    public PendingAddWidgetInfo(AppWidgetProviderInfo i, String dataMimeType, Parcelable data) {
-        itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+    public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) {
+        if (i.isCustomWidget) {
+            itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+        } else {
+            itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+        }
         this.info = i;
         componentName = i.provider;
         minWidth = i.minWidth;
@@ -73,10 +71,15 @@
         minResizeHeight = i.minResizeHeight;
         previewImage = i.previewImage;
         icon = i.icon;
-        if (dataMimeType != null && data != null) {
-            mimeType = dataMimeType;
-            configurationData = data;
-        }
+
+        spanX = i.spanX;
+        spanY = i.spanY;
+        minSpanX = i.minSpanX;
+        minSpanY = i.minSpanY;
+    }
+
+    public boolean isCustomWidget() {
+        return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
     }
 
     // Copy constructor
@@ -89,8 +92,6 @@
         icon = copy.icon;
         info = copy.info;
         boundWidget = copy.boundWidget;
-        mimeType = copy.mimeType;
-        configurationData = copy.configurationData;
         componentName = copy.componentName;
         itemType = copy.itemType;
         spanX = copy.spanX;
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index bb5601e..ff06045 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 
 public class ShortcutAndWidgetContainer extends ViewGroup {
     static final String TAG = "CellLayoutChildren";
@@ -75,6 +76,14 @@
         return null;
     }
 
+    public void addView(View child, int index, LayoutParams params, boolean inLayout) {
+        if (!inLayout) {
+            addView(child, index, params);
+        } else {
+            addViewInLayout(child, index, params, false);
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         @SuppressWarnings("all") // suppress dead code warning
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 9cedae0..8f2d3ed 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -333,7 +333,6 @@
             sb.setLength(0);
         } else {
             sb.append(SHORTCUT_PREFIX);
-
             ResolveInfo info = (ResolveInfo) o;
             sb.append(new ComponentName(info.activityInfo.packageName,
                     info.activityInfo.name).flattenToString());
@@ -479,20 +478,19 @@
                 preview.getHeight() != mPreviewBitmapHeight)) {
             throw new RuntimeException("Improperly sized bitmap passed as argument");
         }
-        if (info instanceof AppWidgetProviderInfo) {
-            return generateWidgetPreview((AppWidgetProviderInfo) info, preview);
+        if (info instanceof LauncherAppWidgetProviderInfo) {
+            return generateWidgetPreview((LauncherAppWidgetProviderInfo) info, preview);
         } else {
             return generateShortcutPreview(
                     (ResolveInfo) info, mPreviewBitmapWidth, mPreviewBitmapHeight, preview);
         }
     }
 
-    public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, Bitmap preview) {
-        int[] cellSpans = Launcher.getSpanForWidget(mContext, info);
-        int maxWidth = maxWidthForWidgetPreview(cellSpans[0]);
-        int maxHeight = maxHeightForWidgetPreview(cellSpans[1]);
-        return generateWidgetPreview(info, cellSpans[0], cellSpans[1],
-                maxWidth, maxHeight, preview, null);
+    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, Bitmap preview) {
+        int maxWidth = maxWidthForWidgetPreview(info.spanX);
+        int maxHeight = maxHeightForWidgetPreview(info.spanY);
+        return generateWidgetPreview(info, info.spanX, info.spanY, maxWidth,
+                maxHeight, preview, null);
     }
 
     public int maxWidthForWidgetPreview(int spanX) {
@@ -505,7 +503,7 @@
                 mWidgetSpacingLayout.estimateCellHeight(spanY));
     }
 
-    public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, int cellHSpan, int cellVSpan,
+    public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, int cellHSpan, int cellVSpan,
             int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) {
         // Load the preview image if possible
         if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 8bd7991..b6e85f3 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3178,9 +3178,9 @@
                         // in its final location
 
                         final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
-                        AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
-                        if (pinfo != null &&
-                                pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
+                        LauncherAppWidgetProviderInfo pInfo = (LauncherAppWidgetProviderInfo)
+                                hostView.getAppWidgetInfo();
+                        if (pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
                             final Runnable addResizeFrame = new Runnable() {
                                 public void run() {
                                     DragLayer dragLayer = mLauncher.getDragLayer();
@@ -3228,7 +3228,9 @@
             mAnimatingViewIntoPlace = true;
             if (d.dragView.hasDrawn()) {
                 final ItemInfo info = (ItemInfo) cell.getTag();
-                if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
+                boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+                        || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+                if (isWidget) {
                     int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
                             ANIMATE_INTO_POSITION_AND_DISAPPEAR;
                     animateWidgetDrop(info, parent, d.dragView,
@@ -3951,6 +3953,7 @@
                     // When dragging and dropping from customization tray, we deal with creating
                     // widgets/shortcuts/folders in a slightly different way
                     switch (pendingInfo.itemType) {
+                    case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                     case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                         int span[] = new int[2];
                         span[0] = item.spanX;
@@ -3968,8 +3971,10 @@
                     }
                 }
             };
-            View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
-                    ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
+            boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+                    || pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+
+            View finalView = isWidget ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
 
             if (finalView instanceof AppWidgetHostView && updateWidgetSize) {
                 AppWidgetHostView awhv = (AppWidgetHostView) finalView;
@@ -3978,7 +3983,7 @@
             }
 
             int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
-            if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
+            if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null &&
                     ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
                 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
             }
@@ -4130,11 +4135,14 @@
             Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView");
             mLauncher.getDragLayer().removeView(finalView);
         }
+
+        boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
+                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
         if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
             Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
             dragView.setCrossFadeBitmap(crossFadeBitmap);
             dragView.crossFade((int) (duration * 0.8f));
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) {
+        } else if (isWidget && external) {
             scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0],  scaleXY[1]);
         }
 
@@ -4984,7 +4992,7 @@
         if (!changedInfo.isEmpty()) {
             DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
                     mLauncher.getAppWidgetHost());
-            if (LauncherModel.findAppWidgetProviderInfoWithComponent(getContext(),
+            if (LauncherModel.getProviderInfo(getContext(),
                     changedInfo.get(0).providerName) != null) {
                 // Re-inflate the widgets which have changed status
                 widgetRefresh.run();
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 6512d42..e41a66f 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -26,6 +26,7 @@
 import android.os.Bundle;
 
 import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 
 import java.util.List;
@@ -63,20 +64,20 @@
 
     public abstract List<AppWidgetProviderInfo> getAllProviders();
 
-    public abstract String loadLabel(AppWidgetProviderInfo info);
+    public abstract String loadLabel(LauncherAppWidgetProviderInfo info);
 
     public abstract boolean bindAppWidgetIdIfAllowed(
             int appWidgetId, AppWidgetProviderInfo info, Bundle options);
 
-    public abstract UserHandleCompat getUser(AppWidgetProviderInfo info);
+    public abstract UserHandleCompat getUser(LauncherAppWidgetProviderInfo info);
 
     public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId,
             Activity activity, AppWidgetHost host, int requestCode);
 
     public abstract Drawable loadPreview(AppWidgetProviderInfo info);
 
-    public abstract Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache);
+    public abstract Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache);
 
-    public abstract Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap);
+    public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap);
 
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index f599f43..767f16f 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 
 import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 
 import java.util.List;
@@ -44,7 +45,7 @@
     }
 
     @Override
-    public String loadLabel(AppWidgetProviderInfo info) {
+    public String loadLabel(LauncherAppWidgetProviderInfo info) {
         return info.label.trim();
     }
 
@@ -59,7 +60,7 @@
     }
 
     @Override
-    public UserHandleCompat getUser(AppWidgetProviderInfo info) {
+    public UserHandleCompat getUser(LauncherAppWidgetProviderInfo info) {
         return UserHandleCompat.myUserHandle();
     }
 
@@ -79,12 +80,12 @@
     }
 
     @Override
-    public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) {
+    public Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache) {
         return cache.getFullResIcon(info.provider.getPackageName(), info.icon);
     }
 
     @Override
-    public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) {
+    public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap) {
         return bitmap;
     }
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index c3853ab..158607a 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -38,6 +38,7 @@
 import android.widget.Toast;
 
 import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
 
 import java.util.ArrayList;
@@ -65,8 +66,8 @@
     }
 
     @Override
-    public String loadLabel(AppWidgetProviderInfo info) {
-        return info.loadLabel(mPm);
+    public String loadLabel(LauncherAppWidgetProviderInfo info) {
+        return info.getLabel(mPm);
     }
 
     @Override
@@ -77,7 +78,10 @@
     }
 
     @Override
-    public UserHandleCompat getUser(AppWidgetProviderInfo info) {
+    public UserHandleCompat getUser(LauncherAppWidgetProviderInfo info) {
+        if (info.isCustomWidget) {
+            return UserHandleCompat.myUserHandle();
+        }
         return UserHandleCompat.fromUser(info.getProfile());
     }
 
@@ -99,13 +103,13 @@
     }
 
     @Override
-    public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) {
-        return info.loadIcon(mContext, cache.getFullResIconDpi());
+    public Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache) {
+        return info.getIcon(mContext, cache);
     }
 
     @Override
-    public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) {
-        if (info.getProfile().equals(android.os.Process.myUserHandle())) {
+    public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap) {
+        if (info.isCustomWidget || info.getProfile().equals(android.os.Process.myUserHandle())) {
             return bitmap;
         }