WidgetTray revamp work
- RecyclerView is rendering
- Animation is connected
- Drag and drop is now handled
- UI tweaking: background, margins, more to come.
- Flicker and preview not loading issue:
  fixed height for the horizontal scroll view.
- Shortcuts are added
- Widget Preview loading should support shortPress for drop
- UI tweaks left: overlay of arrow when there are more items to scroll
- icons are added in the section header
- Sorting of widget sections and widget horizontal list
- Adding all the padding constants to dimen.xml file
- RecyclerView should only support one view type

For items to be addressed in follow up patches OR CLs,
TODO is added to the comment.

b/19897708

Change-Id: Ibfc4da1696a23d20bada93db46e126706eb13cdc
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index a1391b2..7c6b066 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -43,7 +43,7 @@
     /**
      * A bitmap version of the application icon.
      */
-    Bitmap iconBitmap;
+    public Bitmap iconBitmap;
 
     /**
      * Indicates whether we're using a low res icon
@@ -55,7 +55,7 @@
      */
     long firstInstallTime;
 
-    ComponentName componentName;
+    public ComponentName componentName;
 
     static final int DOWNLOADED_FLAG = 1;
     static final int UPDATED_SYSTEM_APP_FLAG = 2;
@@ -121,12 +121,15 @@
                 + " user=" + user + ")";
     }
 
+    /**
+     * Helper method used for debugging.
+     */
     public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) {
         Log.d(tag, label + " size=" + list.size());
         for (AppInfo info: list) {
-            Log.d(tag, "   title=\"" + info.title + "\" iconBitmap="
-                    + info.iconBitmap + " firstInstallTime="
-                    + info.firstInstallTime);
+            Log.d(tag, "   title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap 
+                    + " firstInstallTime=" + info.firstInstallTime
+                    + " componentName=" + info.componentName.getPackageName());
         }
     }
 
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
deleted file mode 100644
index 58bcf1d..0000000
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ /dev/null
@@ -1,1060 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.GridLayout;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FocusHelper.PagedViewKeyListener;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-
-/**
- * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
- */
-public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
-        View.OnClickListener, DragSource,
-        PagedViewWidget.ShortPressListener, LauncherTransitionable {
-    static final String TAG = "AppsCustomizePagedView";
-
-    private static Rect sTmpRect = new Rect();
-    private static final int[] sTempPosArray = new int[2];
-
-    /**
-     * The different content types that this paged view can show.
-     */
-    public enum ContentType {
-        Widgets
-    }
-    private ContentType mContentType = ContentType.Widgets;
-
-    // Refs
-    @Thunk Launcher mLauncher;
-    private DragController mDragController;
-    private final LayoutInflater mLayoutInflater;
-    private final PackageManager mPackageManager;
-
-    // Save and Restore
-    private int mSaveInstanceStateItemIndex = -1;
-
-    // Content
-    private ArrayList<Object> mWidgets;
-
-    // Caching
-    private IconCache mIconCache;
-
-    // Dimens
-    private int mContentWidth, mContentHeight;
-    @Thunk int mWidgetCountX, mWidgetCountY;
-    private int mNumWidgetPages;
-
-    private final PagedViewKeyListener mKeyListener = new PagedViewKeyListener();
-
-    private Runnable mInflateWidgetRunnable = null;
-    private Runnable mBindWidgetRunnable = null;
-    static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
-    static final int WIDGET_PRELOAD_PENDING = 0;
-    static final int WIDGET_BOUND = 1;
-    static final int WIDGET_INFLATED = 2;
-    int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
-    int mWidgetLoadingId = -1;
-    PendingAddWidgetInfo mCreateWidgetInfo = null;
-    private boolean mDraggingWidget = false;
-    boolean mPageBackgroundsVisible = true;
-
-    private Toast mWidgetInstructionToast;
-
-    // Deferral of loading widget previews during launcher transitions
-    private boolean mInTransition;
-
-    WidgetPreviewLoader mWidgetPreviewLoader;
-
-    private boolean mInBulkBind;
-    private boolean mNeedToUpdatePageCountsAndInvalidateData;
-
-    public AppsCustomizePagedView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mLayoutInflater = LayoutInflater.from(context);
-        mPackageManager = context.getPackageManager();
-        mWidgets = new ArrayList<>();
-        mIconCache = (LauncherAppState.getInstance()).getIconCache();
-
-        // Save the default widget preview background
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
-        mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);
-        mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
-        a.recycle();
-
-        // The padding on the non-matched dimension for the default widget preview icons
-        // (top + bottom)
-        mFadeInAdjacentScreens = false;
-
-        // Unless otherwise specified this view is important for accessibility.
-        if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
-            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-        }
-        setSinglePageInViewport();
-    }
-
-    @Override
-    protected void init() {
-        super.init();
-        mCenterPagesVertically = false;
-
-        Context context = getContext();
-        Resources r = context.getResources();
-        setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f);
-    }
-
-    public void onFinishInflate() {
-        super.onFinishInflate();
-
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        setPadding(grid.edgeMarginPx, 2 * grid.edgeMarginPx,
-                grid.edgeMarginPx, 2 * grid.edgeMarginPx);
-    }
-
-    void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) {
-        setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight);
-    }
-
-    WidgetPreviewLoader getWidgetPreviewLoader() {
-        if (mWidgetPreviewLoader == null) {
-            mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
-        }
-        return mWidgetPreviewLoader;
-    }
-
-    /** Returns the item index of the center item on this page so that we can restore to this
-     *  item index when we rotate. */
-    private int getMiddleComponentIndexOnCurrentPage() {
-        int i = -1;
-        if (getPageCount() > 0) {
-            int currentPage = getCurrentPage();
-            if (mContentType == ContentType.Widgets) {
-                PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage);
-                int numItemsPerPage = mWidgetCountX * mWidgetCountY;
-                int childCount = layout.getChildCount();
-                if (childCount > 0) {
-                    i = (currentPage * numItemsPerPage) + (childCount / 2);
-                }
-            } else {
-                throw new RuntimeException("Invalid ContentType");
-            }
-        }
-        return i;
-    }
-
-    /** Get the index of the item to restore to if we need to restore the current page. */
-    int getSaveInstanceStateIndex() {
-        if (mSaveInstanceStateItemIndex == -1) {
-            mSaveInstanceStateItemIndex = getMiddleComponentIndexOnCurrentPage();
-        }
-        return mSaveInstanceStateItemIndex;
-    }
-
-    /** Returns the page in the current orientation which is expected to contain the specified
-     *  item index. */
-    int getPageForComponent(int index) {
-        if (index < 0) return 0;
-
-        int numItemsPerPage = mWidgetCountX * mWidgetCountY;
-        return index / numItemsPerPage;
-    }
-
-    /** Restores the page for an item at the specified index */
-    void restorePageForIndex(int index) {
-        if (index < 0) return;
-        mSaveInstanceStateItemIndex = index;
-    }
-
-    private void updatePageCounts() {
-        mNumWidgetPages = (int) Math.ceil(mWidgets.size() /
-                (float) (mWidgetCountX * mWidgetCountY));
-    }
-
-    protected void onDataReady(int width, int height) {
-        updatePageCounts();
-
-        // Force a measure to update recalculate the gaps
-        mContentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
-        mContentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
-
-        final boolean hostIsTransitioning = getTabHost().isInTransition();
-        int page = getPageForComponent(mSaveInstanceStateItemIndex);
-        invalidatePageData(Math.max(0, page), hostIsTransitioning);
-    }
-
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        if (!isDataReady()) {
-            if (!mWidgets.isEmpty()) {
-                post(new Runnable() {
-                    // This code triggers requestLayout so must be posted outside of the
-                    // layout pass.
-                    public void run() {
-                        if (Utilities.isViewAttachedToWindow(AppsCustomizePagedView.this)) {
-                            setDataIsReady();
-                            onDataReady(getMeasuredWidth(), getMeasuredHeight());
-                        }
-                    }
-                });
-            }
-        }
-    }
-
-    public void onPackagesUpdated(ArrayList<Object> widgetsAndShortcuts) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-
-        // Get the list of widgets and shortcuts
-        mWidgets.clear();
-        for (Object o : widgetsAndShortcuts) {
-            if (o instanceof LauncherAppWidgetProviderInfo) {
-                LauncherAppWidgetProviderInfo widget = (LauncherAppWidgetProviderInfo) o;
-                if (!app.shouldShowAppOrWidgetProvider(widget.provider) && !widget.isCustomWidget) {
-                    continue;
-                }
-
-                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);
-                    int minSpanX = Math.min(spanXY[0], minSpanXY[0]);
-                    int minSpanY = Math.min(spanXY[1], minSpanXY[1]);
-                    if (minSpanX <= (int) grid.numColumns &&
-                        minSpanY <= (int) grid.numRows) {
-                        mWidgets.add(widget);
-                    } else {
-                        Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" +
-                              widget.minWidth + ", " + widget.minHeight + ")");
-                    }
-                } else {
-                    Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" +
-                          widget.minWidth + ", " + widget.minHeight + ")");
-                }
-            } else {
-                // just add shortcuts
-                mWidgets.add(o);
-            }
-        }
-
-        updatePageCountsAndInvalidateData();
-    }
-
-    public void setBulkBind(boolean bulkBind) {
-        if (bulkBind) {
-            mInBulkBind = true;
-        } else {
-            mInBulkBind = false;
-            if (mNeedToUpdatePageCountsAndInvalidateData) {
-                updatePageCountsAndInvalidateData();
-            }
-        }
-    }
-
-    private void updatePageCountsAndInvalidateData() {
-        if (mInBulkBind) {
-            mNeedToUpdatePageCountsAndInvalidateData = true;
-        } else {
-            updatePageCounts();
-            invalidateOnDataChange();
-            mNeedToUpdatePageCountsAndInvalidateData = false;
-        }
-    }
-
-    @Override
-    public void onClick(View v) {
-        // When we have exited all apps or are in transition, disregard clicks
-        if (!mLauncher.isWidgetsViewVisible()
-                || mLauncher.getWorkspace().isSwitchingState()
-                || !(v instanceof PagedViewWidget)) return;
-
-        // Let the user know that they have to long press to add a widget
-        if (mWidgetInstructionToast != null) {
-            mWidgetInstructionToast.cancel();
-        }
-        mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
-            Toast.LENGTH_SHORT);
-        mWidgetInstructionToast.show();
-    }
-
-    /*
-     * PagedViewWithDraggableItems implementation
-     */
-    @Override
-    protected void determineDraggingStart(android.view.MotionEvent ev) {
-    }
-
-    static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
-        Bundle options = null;
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, sTmpRect);
-            Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
-                    info.componentName, null);
-
-            float density = launcher.getResources().getDisplayMetrics().density;
-            int xPaddingDips = (int) ((padding.left + padding.right) / density);
-            int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
-
-            options = new Bundle();
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
-                    sTmpRect.left - xPaddingDips);
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
-                    sTmpRect.top - yPaddingDips);
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
-                    sTmpRect.right - xPaddingDips);
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
-                    sTmpRect.bottom - yPaddingDips);
-        }
-        return options;
-    }
-
-    private void preloadWidget(final PendingAddWidgetInfo info) {
-        final LauncherAppWidgetProviderInfo pInfo = info.info;
-        final Bundle options = pInfo.isCustomWidget ? null :
-                getDefaultOptionsForWidget(mLauncher, info);
-
-        if (pInfo.configure != null) {
-            info.bindOptions = options;
-            return;
-        }
-
-        mWidgetCleanupState = WIDGET_PRELOAD_PENDING;
-        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);
-
-        mInflateWidgetRunnable = new Runnable() {
-            @Override
-            public void run() {
-                if (mWidgetCleanupState != WIDGET_BOUND) {
-                    return;
-                }
-                AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
-                        getContext(), mWidgetLoadingId, pInfo);
-                info.boundWidget = hostView;
-                mWidgetCleanupState = WIDGET_INFLATED;
-                hostView.setVisibility(INVISIBLE);
-                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false);
-
-                // We want the first widget layout to be the correct size. This will be important
-                // for width size reporting to the AppWidgetManager.
-                DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
-                        unScaledSize[1]);
-                lp.x = lp.y = 0;
-                lp.customPosition = true;
-                hostView.setLayoutParams(lp);
-                mLauncher.getDragLayer().addView(hostView);
-            }
-        };
-        post(mInflateWidgetRunnable);
-    }
-
-    @Override
-    public void onShortPress(View v) {
-        // We are anticipating a long press, and we use this time to load bind and instantiate
-        // the widget. This will need to be cleaned up if it turns out no long press occurs.
-        if (mCreateWidgetInfo != null) {
-            // Just in case the cleanup process wasn't properly executed. This shouldn't happen.
-            cleanupWidgetPreloading(false);
-        }
-        mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
-        preloadWidget(mCreateWidgetInfo);
-    }
-
-    private void cleanupWidgetPreloading(boolean widgetWasAdded) {
-        if (!widgetWasAdded) {
-            // If the widget was not added, we may need to do further cleanup.
-            PendingAddWidgetInfo info = mCreateWidgetInfo;
-            mCreateWidgetInfo = null;
-
-            if (mWidgetCleanupState == WIDGET_PRELOAD_PENDING) {
-                // We never did any preloading, so just remove pending callbacks to do so
-                removeCallbacks(mBindWidgetRunnable);
-                removeCallbacks(mInflateWidgetRunnable);
-            } else if (mWidgetCleanupState == WIDGET_BOUND) {
-                 // Delete the widget id which was allocated
-                if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
-                    mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
-                }
-
-                // We never got around to inflating the widget, so remove the callback to do so.
-                removeCallbacks(mInflateWidgetRunnable);
-            } else if (mWidgetCleanupState == WIDGET_INFLATED) {
-                // Delete the widget id which was allocated
-                if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
-                    mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
-                }
-
-                // The widget was inflated and added to the DragLayer -- remove it.
-                AppWidgetHostView widget = info.boundWidget;
-                mLauncher.getDragLayer().removeView(widget);
-            }
-        }
-        mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
-        mWidgetLoadingId = -1;
-        mCreateWidgetInfo = null;
-        PagedViewWidget.resetShortPressTarget();
-    }
-
-    @Override
-    public void cleanUpShortPress(View v) {
-        if (!mDraggingWidget) {
-            cleanupWidgetPreloading(false);
-        }
-    }
-
-    private boolean beginDraggingWidget(PagedViewWidget v) {
-        mDraggingWidget = true;
-        // Get the widget preview as the drag representation
-        ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
-        PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
-
-        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
-        // we abort the drag.
-        if (image.getDrawable() == null) {
-            mDraggingWidget = false;
-            return false;
-        }
-
-        // Compose the drag image
-        Bitmap preview;
-        Bitmap outline;
-        float scale = 1f;
-        Point previewPadding = null;
-
-        if (createItemInfo instanceof PendingAddWidgetInfo) {
-            // This can happen in some weird cases involving multi-touch. We can't start dragging
-            // the widget if this is null, so we break out.
-            if (mCreateWidgetInfo == null) {
-                return false;
-            }
-
-            PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo;
-            createItemInfo = createWidgetInfo;
-            int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true);
-
-            FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
-            float minScale = 1.25f;
-            int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
-
-            int[] previewSizeBeforeScale = new int[1];
-            preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
-                    maxWidth, null, previewSizeBeforeScale);
-            // Compare the size of the drag preview to the preview in the AppsCustomize tray
-            int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
-                    v.getActualItemWidth());
-            scale = previewWidthInAppsCustomize / (float) preview.getWidth();
-
-            // The bitmap in the AppsCustomize tray is always the the same size, so there
-            // might be extra pixels around the preview itself - this accounts for that
-            if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) {
-                int padding =
-                        (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2;
-                previewPadding = new Point(padding, 0);
-            }
-        } else {
-            PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
-            Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo);
-            preview = Utilities.createIconBitmap(icon, mLauncher);
-            createItemInfo.spanX = createItemInfo.spanY = 1;
-        }
-
-        // Don't clip alpha values for the drag outline if we're using the default widget preview
-        boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
-                (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
-
-        // Save the preview for the outline generation, then dim the preview
-        outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
-                false);
-
-        // Start the drag
-        mLauncher.lockScreenOrientation();
-        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
-        mDragController.startDrag(image, preview, this, createItemInfo,
-                DragController.DRAG_ACTION_COPY, previewPadding, scale);
-        outline.recycle();
-        preview.recycle();
-        return true;
-    }
-
-    @Override
-    protected boolean beginDragging(final View v) {
-        if (!super.beginDragging(v)) return false;
-
-        if (v instanceof PagedViewWidget) {
-            if (!beginDraggingWidget((PagedViewWidget) v)) {
-                return false;
-            }
-        } else {
-            Log.e(TAG, "Unexpected dragging view: " + v);
-        }
-
-        // We delay entering spring-loaded mode slightly to make sure the UI
-        // thready is free of any work.
-        postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                // We don't enter spring-loaded mode if the drag has been cancelled
-                if (mLauncher.getDragController().isDragging()) {
-                    // Go into spring loaded mode (must happen before we startDrag())
-                    mLauncher.enterSpringLoadedDragMode();
-                }
-            }
-        }, 150);
-
-        return true;
-    }
-
-    /**
-     * Clean up after dragging.
-     *
-     * @param target where the item was dragged to (can be null if the item was flung)
-     */
-    private void endDragging(View target, boolean isFlingToDelete, boolean success) {
-        if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
-                !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
-            // Exit spring loaded mode if we have not successfully dropped or have not handled the
-            // drop in Workspace
-            mLauncher.exitSpringLoadedDragModeDelayed(true,
-                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
-            mLauncher.unlockScreenOrientation(false);
-        } else {
-            mLauncher.unlockScreenOrientation(false);
-        }
-    }
-
-    @Override
-    public View getContent() {
-        if (getChildCount() > 0) {
-            return getChildAt(0);
-        }
-        return null;
-    }
-
-    @Override
-    public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
-        mInTransition = true;
-        if (toWorkspace) {
-            cancelAllTasks(false);
-        }
-    }
-
-    @Override
-    public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
-    }
-
-    @Override
-    public void onLauncherTransitionStep(Launcher l, float t) {
-    }
-
-    @Override
-    public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
-        mInTransition = false;
-        mForceDrawAllChildrenNextFrame = !toWorkspace;
-        if (!toWorkspace) {
-            loadPreviewsForPage(getNextPage());
-        }
-    }
-
-    @Override
-    public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
-            boolean success) {
-        // Return early and wait for onFlingToDeleteCompleted if this was the result of a fling
-        if (isFlingToDelete) return;
-
-        endDragging(target, false, success);
-
-        // Display an error message if the drag failed due to there not being enough space on the
-        // target layout we were dropping on.
-        if (!success) {
-            boolean showOutOfSpaceMessage = false;
-            if (target instanceof Workspace) {
-                int currentScreen = mLauncher.getCurrentWorkspaceScreen();
-                Workspace workspace = (Workspace) target;
-                CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
-                ItemInfo itemInfo = (ItemInfo) d.dragInfo;
-                if (layout != null) {
-                    layout.calculateSpans(itemInfo);
-                    showOutOfSpaceMessage =
-                            !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
-                }
-            }
-            if (showOutOfSpaceMessage) {
-                mLauncher.showOutOfSpaceMessage(false);
-            }
-
-            d.deferDragViewCleanupPostAnimation = false;
-        }
-        cleanupWidgetPreloading(success);
-        mDraggingWidget = false;
-    }
-
-    @Override
-    public void onFlingToDeleteCompleted() {
-        // We just dismiss the drag when we fling, so cleanup here
-        endDragging(null, true, true);
-        cleanupWidgetPreloading(false);
-        mDraggingWidget = false;
-    }
-
-    @Override
-    public boolean supportsFlingToDelete() {
-        return true;
-    }
-
-    @Override
-    public boolean supportsAppInfoDropTarget() {
-        return true;
-    }
-
-    @Override
-    public boolean supportsDeleteDropTarget() {
-        return false;
-    }
-
-    @Override
-    public float getIntrinsicIconScaleFactor() {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        return (float) grid.allAppsIconSizePx / grid.iconSizePx;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        cancelAllTasks(true);
-    }
-
-    @Override
-    public void trimMemory() {
-        super.trimMemory();
-        cancelAllTasks(true);
-    }
-
-    private void cancelAllTasks(boolean clearCompletedTasks) {
-        for (int page = getPageCount() - 1; page >= 0; page--) {
-            final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
-            if (layout != null) {
-                for (int i = 0; i < layout.getChildCount(); i++) {
-                    ((PagedViewWidget) layout.getChildAt(i)).deletePreview(clearCompletedTasks);
-                }
-            }
-        }
-    }
-
-
-    public void setContentType(ContentType type) {
-        // Widgets appear to be cleared every time you leave, always force invalidate for them
-        if (mContentType != type || type == ContentType.Widgets) {
-            int page = (mContentType != type) ? 0 : getCurrentPage();
-            mContentType = type;
-            invalidatePageData(page, true);
-        }
-    }
-
-    public ContentType getContentType() {
-        return mContentType;
-    }
-
-    public void setPageBackgroundsVisible(boolean visible) {
-        mPageBackgroundsVisible = visible;
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; ++i) {
-            Drawable bg = getChildAt(i).getBackground();
-            if (bg != null) {
-                bg.setAlpha(visible ? 255 : 0);
-            }
-        }
-    }
-
-    /*
-     * Widgets PagedView implementation
-     */
-    private void setupPage(PagedViewGridLayout layout) {
-        // Note: We force a measure here to get around the fact that when we do layout calculations
-        // immediately after syncing, we don't have a proper width.
-        int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
-        int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
-
-        Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel_dark);
-        if (bg != null) {
-            bg.setAlpha(mPageBackgroundsVisible ? 255 : 0);
-            layout.setBackground(bg);
-        }
-        layout.measure(widthSpec, heightSpec);
-    }
-
-    public void syncWidgetPageItems(final int page, final boolean immediate) {
-        int numItemsPerPage = mWidgetCountX * mWidgetCountY;
-
-        final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
-
-        // Calculate the dimensions of each cell we are giving to each widget
-        final ArrayList<Object> items = new ArrayList<Object>();
-        int contentWidth = mContentWidth - layout.getPaddingLeft() - layout.getPaddingRight();
-        final int cellWidth = contentWidth / mWidgetCountX;
-        int contentHeight = mContentHeight - layout.getPaddingTop() - layout.getPaddingBottom();
-
-        final int cellHeight = contentHeight / mWidgetCountY;
-
-        // Prepare the set of widgets to load previews for in the background
-        int offset = page * numItemsPerPage;
-        for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) {
-            items.add(mWidgets.get(i));
-        }
-
-        // Prepopulate the pages with the other widget info, and fill in the previews later
-        layout.setColumnCount(layout.getCellCountX());
-        for (int i = 0; i < items.size(); ++i) {
-            Object rawInfo = items.get(i);
-            PendingAddItemInfo createItemInfo = null;
-            PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate(
-                    R.layout.apps_customize_widget, layout, false);
-
-            if (rawInfo instanceof LauncherAppWidgetProviderInfo) {
-                // Fill in the widget information
-                LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) rawInfo;
-                createItemInfo = new PendingAddWidgetInfo(info, null);
-
-                widget.applyFromAppWidgetProviderInfo(info, -1, getWidgetPreviewLoader());
-                widget.setTag(createItemInfo);
-                widget.setShortPressListener(this);
-            } else if (rawInfo instanceof ResolveInfo) {
-                // Fill in the shortcuts information
-                ResolveInfo info = (ResolveInfo) rawInfo;
-                createItemInfo = new PendingAddShortcutInfo(info.activityInfo);
-                createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-                createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
-                        info.activityInfo.name);
-                widget.applyFromResolveInfo(mPackageManager, info, getWidgetPreviewLoader());
-                widget.setTag(createItemInfo);
-            }
-
-            widget.setOnClickListener(this);
-            widget.setOnLongClickListener(this);
-            widget.setOnTouchListener(this);
-            widget.setOnKeyListener(mKeyListener);
-
-            // Layout each widget
-            int ix = i % mWidgetCountX;
-            int iy = i / mWidgetCountX;
-
-            if (ix > 0) {
-                View border = widget.findViewById(R.id.left_border);
-                border.setVisibility(View.VISIBLE);
-            }
-            if (ix < mWidgetCountX - 1) {
-                View border = widget.findViewById(R.id.right_border);
-                border.setVisibility(View.VISIBLE);
-            }
-
-            GridLayout.LayoutParams lp = new GridLayout.LayoutParams(
-                    GridLayout.spec(iy, GridLayout.START),
-                    GridLayout.spec(ix, GridLayout.TOP));
-            lp.width = cellWidth;
-            lp.height = cellHeight;
-            lp.setGravity(Gravity.TOP | Gravity.START);
-            layout.addView(widget, lp);
-        }
-
-        if (immediate && !mInTransition) {
-            loadPreviewsForPage(page);
-        }
-    }
-
-    private void loadPreviewsForPage(int page) {
-        final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
-
-        if (layout != null) {
-            for (int i = 0; i < layout.getChildCount(); i++) {
-                ((PagedViewWidget) layout.getChildAt(i)).ensurePreview();
-            }
-        }
-    }
-
-    @Override
-    public void syncPages() {
-        disablePagedViewAnimations();
-
-        removeAllViews();
-        cancelAllTasks(true);
-
-        Context context = getContext();
-        if (mContentType == ContentType.Widgets) {
-            for (int j = 0; j < mNumWidgetPages; ++j) {
-                PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,
-                        mWidgetCountY);
-                setupPage(layout);
-                addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT,
-                        LayoutParams.MATCH_PARENT));
-            }
-        } else {
-            throw new RuntimeException("Invalid ContentType");
-        }
-
-        enablePagedViewAnimations();
-    }
-
-    @Override
-    public void syncPageItems(int page, boolean immediate) {
-        if (mContentType == ContentType.Widgets) {
-            syncWidgetPageItems(page, immediate);
-        } else {
-            Log.e(TAG, "Unexpected ContentType");
-        }
-    }
-
-    // We want our pages to be z-ordered such that the further a page is to the left, the higher
-    // it is in the z-order. This is important to insure touch events are handled correctly.
-    View getPageAt(int index) {
-        return getChildAt(indexToPage(index));
-    }
-
-    @Override
-    protected int indexToPage(int index) {
-        return getChildCount() - index - 1;
-    }
-
-    // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
-    @Override
-    protected void screenScrolled(int screenCenter) {
-        super.screenScrolled(screenCenter);
-        enableHwLayersOnVisiblePages();
-    }
-
-    private void enableHwLayersOnVisiblePages() {
-        final int screenCount = getChildCount();
-
-        getVisiblePages(mTempVisiblePagesRange);
-        int leftScreen = mTempVisiblePagesRange[0];
-        int rightScreen = mTempVisiblePagesRange[1];
-        int forceDrawScreen = -1;
-        if (leftScreen == rightScreen) {
-            // make sure we're caching at least two pages always
-            if (rightScreen < screenCount - 1) {
-                rightScreen++;
-                forceDrawScreen = rightScreen;
-            } else if (leftScreen > 0) {
-                leftScreen--;
-                forceDrawScreen = leftScreen;
-            }
-        } else {
-            forceDrawScreen = leftScreen + 1;
-        }
-
-        for (int i = 0; i < screenCount; i++) {
-            final View layout = (View) getPageAt(i);
-            if (!(leftScreen <= i && i <= rightScreen &&
-                    (i == forceDrawScreen || shouldDrawChild(layout)))) {
-                layout.setLayerType(LAYER_TYPE_NONE, null);
-            }
-        }
-
-        for (int i = 0; i < screenCount; i++) {
-            final View layout = (View) getPageAt(i);
-
-            if (leftScreen <= i && i <= rightScreen &&
-                    (i == forceDrawScreen || shouldDrawChild(layout))) {
-                if (layout.getLayerType() != LAYER_TYPE_HARDWARE) {
-                    layout.setLayerType(LAYER_TYPE_HARDWARE, null);
-                }
-            }
-        }
-    }
-
-    protected void overScroll(float amount) {
-        dampedOverScroll(amount);
-    }
-
-    /**
-     * Used by the parent to get the content width to set the tab bar to
-     * @return
-     */
-    public int getPageContentWidth() {
-        return mContentWidth;
-    }
-
-    @Override
-    protected void onPageEndMoving() {
-        super.onPageEndMoving();
-        mForceDrawAllChildrenNextFrame = true;
-        // We reset the save index when we change pages so that it will be recalculated on next
-        // rotation
-        mSaveInstanceStateItemIndex = -1;
-    }
-
-    @Override
-    protected void onPageBeginMoving() {
-        super.onPageBeginMoving();
-        if (!mInTransition) {
-            getVisiblePages(sTempPosArray);
-            for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) {
-                loadPreviewsForPage(i);
-            }
-        }
-    }
-
-    /*
-     * AllAppsView implementation
-     */
-    public void setup(Launcher launcher, DragController dragController) {
-        mLauncher = launcher;
-        mDragController = dragController;
-    }
-
-    /**
-     * We should call thise method whenever the core data changes (mWidgets) so that we can
-     * appropriately determine when to invalidate the PagedView page data.  In cases where the data
-     * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the
-     * next onMeasure() pass, which will trigger an invalidatePageData() itself.
-     */
-    private void invalidateOnDataChange() {
-        if (!isDataReady()) {
-            // The next layout pass will trigger data-ready if both widgets and apps are set, so
-            // request a layout to trigger the page data when ready.
-            requestLayout();
-        } else {
-            cancelAllTasks(false);
-            invalidatePageData();
-        }
-    }
-
-    public void reset() {
-        // If we have reset, then we should not continue to restore the previous state
-        mSaveInstanceStateItemIndex = -1;
-
-        if (mContentType != ContentType.Widgets) {
-            setContentType(ContentType.Widgets);
-        }
-
-        if (mCurrentPage != 0) {
-            invalidatePageData(0);
-        }
-    }
-
-    private AppsCustomizeTabHost getTabHost() {
-        return (AppsCustomizeTabHost) mLauncher.findViewById(R.id.apps_customize_pane);
-    }
-
-    public void dumpState() {
-        // TODO: Dump information related to current list of Applications, Widgets, etc.
-        dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets);
-    }
-
-    private void dumpAppWidgetProviderInfoList(String tag, String label,
-            ArrayList<Object> list) {
-        Log.d(tag, label + " size=" + list.size());
-        for (Object i: list) {
-            if (i instanceof AppWidgetProviderInfo) {
-                AppWidgetProviderInfo info = (AppWidgetProviderInfo) i;
-                Log.d(tag, "   label=\"" + info.label + "\" previewImage=" + info.previewImage
-                        + " resizeMode=" + info.resizeMode + " configure=" + info.configure
-                        + " initialLayout=" + info.initialLayout
-                        + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight);
-            } else if (i instanceof ResolveInfo) {
-                ResolveInfo info = (ResolveInfo) i;
-                Log.d(tag, "   label=\"" + info.loadLabel(mPackageManager) + "\" icon="
-                        + info.icon);
-            }
-        }
-    }
-
-    public void surrender() {
-        // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we
-        // should stop this now.
-
-        // Stop all background tasks
-        cancelAllTasks(true);
-    }
-
-    /*
-     * We load an extra page on each side to prevent flashes from scrolling and loading of the
-     * widget previews in the background with the AsyncTasks.
-     */
-    final static int sLookBehindPageCount = 2;
-    final static int sLookAheadPageCount = 2;
-    protected int getAssociatedLowerPageBound(int page) {
-        final int count = getChildCount();
-        int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1);
-        int windowMinIndex = Math.max(Math.min(page - sLookBehindPageCount, count - windowSize), 0);
-        return windowMinIndex;
-    }
-    protected int getAssociatedUpperPageBound(int page) {
-        final int count = getChildCount();
-        int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1);
-        int windowMaxIndex = Math.min(Math.max(page + sLookAheadPageCount, windowSize - 1),
-                count - 1);
-        return windowMaxIndex;
-    }
-
-    protected String getCurrentPageDescription() {
-        int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
-        int stringId = R.string.default_scroll_format;
-        int count = 0;
-
-        if (mContentType == ContentType.Widgets) {
-            stringId = R.string.apps_customize_widgets_scroll_format;
-            count = mNumWidgetPages;
-        } else {
-            throw new RuntimeException("Invalid ContentType");
-        }
-
-        return String.format(getContext().getString(stringId), page + 1, count);
-    }
-}
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
deleted file mode 100644
index 5e2f05c..0000000
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.FrameLayout;
-
-public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable  {
-    static final String LOG_TAG = "AppsCustomizeTabHost";
-
-    private static final String WIDGETS_TAB_TAG = "WIDGETS";
-
-    private AppsCustomizePagedView mPagedView;
-    private View mContent;
-    private boolean mInTransition = false;
-
-    private final Rect mInsets = new Rect();
-
-    public AppsCustomizeTabHost(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    /**
-     * Convenience methods to select specific tabs.  We want to set the content type immediately
-     * in these cases, but we note that we still call setCurrentTabByTag() so that the tab view
-     * reflects the new content (but doesn't do the animation and logic associated with changing
-     * tabs manually).
-     */
-    void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) {
-        mPagedView.setContentType(type);
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        mInsets.set(insets);
-        LayoutParams flp = (LayoutParams) mContent.getLayoutParams();
-        flp.topMargin = insets.top;
-        flp.bottomMargin = insets.bottom;
-        flp.leftMargin = insets.left;
-        flp.rightMargin = insets.right;
-        mContent.setLayoutParams(flp);
-    }
-
-    /**
-     * Setup the tab host and create all necessary tabs.
-     */
-    @Override
-    protected void onFinishInflate() {
-        mPagedView = (AppsCustomizePagedView) findViewById(R.id.apps_customize_pane_content);
-        mContent = findViewById(R.id.content);
-    }
-
-    public String getContentTag() {
-        return getTabTagForContentType(mPagedView.getContentType());
-    }
-
-    /**
-     * Returns the content view used for the launcher transitions.
-     */
-    public View getContentView() {
-        return findViewById(R.id.apps_customize_pane_content);
-    }
-
-    /**
-     * Returns the reveal view used for the launcher transitions.
-     */
-    public View getRevealView() {
-        return findViewById(R.id.fake_page);
-    }
-
-    /**
-     * Returns the page indicators view.
-     */
-    public View getPageIndicators() {
-        return findViewById(R.id.apps_customize_page_indicator);
-    }
-
-    /**
-     * Returns the content type for the specified tab tag.
-     */
-    public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) {
-        return AppsCustomizePagedView.ContentType.Widgets;
-    }
-
-    /**
-     * Returns the tab tag for a given content type.
-     */
-    public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) {
-        return WIDGETS_TAB_TAG;
-    }
-
-    /**
-     * Disable focus on anything under this view in the hierarchy if we are not visible.
-     */
-    @Override
-    public int getDescendantFocusability() {
-        if (getVisibility() != View.VISIBLE) {
-            return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
-        }
-        return super.getDescendantFocusability();
-    }
-
-    void reset() {
-        // Reset immediately
-        mPagedView.reset();
-    }
-
-    void trimMemory() {
-        mPagedView.trimMemory();
-    }
-
-    public void onWindowVisible() {
-        if (getVisibility() == VISIBLE) {
-            mContent.setVisibility(VISIBLE);
-            // We unload the widget previews when the UI is hidden, so need to reload pages
-            // Load the current page synchronously, and the neighboring pages asynchronously
-            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true);
-            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
-        }
-    }
-    @Override
-    public ViewGroup getContent() {
-        return mPagedView;
-    }
-
-    public boolean isInTransition() {
-        return mInTransition;
-    }
-
-    /* LauncherTransitionable overrides */
-    @Override
-    public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
-        mPagedView.onLauncherTransitionPrepare(l, animated, toWorkspace);
-        mInTransition = true;
-
-        if (toWorkspace) {
-            // Going from All Apps -> Workspace
-            setVisibilityOfSiblingsWithLowerZOrder(VISIBLE);
-        } else {
-            // Going from Workspace -> All Apps
-            mContent.setVisibility(VISIBLE);
-
-            // Make sure the current page is loaded (we start loading the side pages after the
-            // transition to prevent slowing down the animation)
-            // TODO: revisit this
-            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
-        }
-    }
-
-    @Override
-    public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
-        mPagedView.onLauncherTransitionStart(l, animated, toWorkspace);
-    }
-
-    @Override
-    public void onLauncherTransitionStep(Launcher l, float t) {
-        mPagedView.onLauncherTransitionStep(l, t);
-    }
-
-    @Override
-    public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
-        mPagedView.onLauncherTransitionEnd(l, animated, toWorkspace);
-        mInTransition = false;
-
-        if (!toWorkspace) {
-            // Make sure adjacent pages are loaded (we wait until after the transition to
-            // prevent slowing down the animation)
-            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
-
-            // Opening apps, need to announce what page we are on.
-            AccessibilityManager am = (AccessibilityManager)
-                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-            if (am.isEnabled()) {
-                // Notify the user when the page changes
-                announceForAccessibility(mPagedView.getCurrentPageDescription());
-            }
-
-            // Going from Workspace -> All Apps
-            // NOTE: We should do this at the end since we check visibility state in some of the
-            // cling initialization/dismiss code above.
-            setVisibilityOfSiblingsWithLowerZOrder(INVISIBLE);
-        }
-    }
-
-    private void setVisibilityOfSiblingsWithLowerZOrder(int visibility) {
-        ViewGroup parent = (ViewGroup) getParent();
-        if (parent == null) return;
-
-        View appsView = ((Launcher) getContext()).getAppsView();
-        View overviewPanel = ((Launcher) getContext()).getOverviewPanel();
-        final int count = parent.getChildCount();
-        if (!isChildrenDrawingOrderEnabled()) {
-            for (int i = 0; i < count; i++) {
-                final View child = parent.getChildAt(i);
-                if (child == this) {
-                    break;
-                } else {
-                    if (child.getVisibility() == GONE || child == overviewPanel ||
-                            child == appsView) {
-                        continue;
-                    }
-                    child.setVisibility(visibility);
-                }
-            }
-        } else {
-            throw new RuntimeException("Failed; can't get z-order of views");
-        }
-    }
-}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 63afa30..f4afb95 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -45,7 +45,6 @@
 import android.util.SparseArray;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
@@ -56,6 +55,7 @@
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -3025,7 +3025,7 @@
      *
      * @return True if a vacant cell of the specified dimension was found, false otherwise.
      */
-    boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
+    public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
         return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied);
     }
 
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 1f0dad2..62aa285 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -42,6 +42,7 @@
 
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.WidgetsContainerView;
 
 public class DeleteDropTarget extends ButtonDropTarget {
     private static int DELETE_ANIMATION_DURATION = 285;
@@ -100,8 +101,9 @@
     private boolean isAllAppsApplication(DragSource source, Object info) {
         return source.supportsAppInfoDropTarget() && (info instanceof AppInfo);
     }
-    private boolean isAllAppsWidget(DragSource source, Object info) {
-        if (source instanceof AppsCustomizePagedView) {
+
+    private boolean isWidget(DragSource source, Object info) {
+        if (source instanceof WidgetsContainerView) {
             if (info instanceof PendingAddItemInfo) {
                 PendingAddItemInfo addInfo = (PendingAddItemInfo) info;
                 switch (addInfo.itemType) {
@@ -173,7 +175,7 @@
         // If we are dragging an application from AppsCustomize, only show the control if we can
         // delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
         // Hide the delete target if it is a widget from AppsCustomize.
-        if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) {
+        if (!willAcceptDrop(info) || isWidget(source, info)) {
             isVisible = false;
         }
         if (useUninstallLabel) {
@@ -489,13 +491,14 @@
     }
 
     public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) {
-        final boolean isAllApps = d.dragSource instanceof AppsCustomizePagedView;
+        final boolean isWidgets = d.dragSource instanceof WidgetsContainerView;
+        final boolean isAllapps = d.dragSource instanceof AppsContainerView;
 
         // Don't highlight the icon as it's animating
         d.dragView.setColor(0);
         d.dragView.updateInitialScaleToCurrentScale();
         // Don't highlight the target if we are flinging from AllApps
-        if (isAllApps) {
+        if (isWidgets || isAllapps) {
             resetHoverColor();
         }
 
@@ -545,7 +548,7 @@
             public void run() {
                 // If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up
                 // itself, otherwise, complete the drop to initiate the deletion process
-                if (!isAllApps) {
+                if (!isWidgets || !isAllapps) {
                     mLauncher.exitSpringLoadedDragMode();
                     completeDrop(d);
                 }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 331695a..ea28520 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -106,8 +106,8 @@
     public int cellWidthPx;
     public int cellHeightPx;
 
-    int iconSizePx;
-    int iconTextSizePx;
+    public int iconSizePx;
+    public int iconTextSizePx;
     int iconDrawablePaddingPx;
     int allAppsIconSizePx;
     int allAppsIconTextSizePx;
@@ -803,64 +803,6 @@
             }
         }
 
-        // Layout AllApps
-        AppsCustomizeTabHost host = (AppsCustomizeTabHost)
-                launcher.findViewById(R.id.apps_customize_pane);
-        if (host != null) {
-            // Center the all apps page indicator
-            int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f,
-                    (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX)));
-            pageIndicator = host.findViewById(R.id.apps_customize_page_indicator);
-            if (pageIndicator != null) {
-                LinearLayout.LayoutParams lllp = (LinearLayout.LayoutParams) pageIndicator.getLayoutParams();
-                lllp.width = LayoutParams.WRAP_CONTENT;
-                lllp.height = pageIndicatorHeight;
-                pageIndicator.setLayoutParams(lllp);
-            }
-
-            AppsCustomizePagedView pagedView = (AppsCustomizePagedView)
-                    host.findViewById(R.id.apps_customize_pane_content);
-
-            FrameLayout fakePageContainer = (FrameLayout)
-                    host.findViewById(R.id.fake_page_container);
-            FrameLayout fakePage = (FrameLayout) host.findViewById(R.id.fake_page);
-
-            padding = new Rect();
-            if (pagedView != null) {
-                // Constrain the dimensions of all apps so that it does not span the full width
-                int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) /
-                        (2 * (allAppsNumCols + 1));
-                int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) /
-                        (2 * (allAppsNumRows + 1));
-                paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f));
-                paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f));
-                int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR));
-                int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2;
-                // Only adjust the side paddings on landscape phones, or tablets
-                if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) {
-                    padding.left = padding.right = gridPaddingLR;
-                }
-
-                // The icons are centered, so we can't just offset by the page indicator height
-                // because the empty space will actually be pageIndicatorHeight + paddingTB
-                padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB);
-
-                pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight);
-                fakePage.setBackground(res.getDrawable(R.drawable.quantum_panel));
-
-                // Horizontal padding for the whole paged view
-                int pagedFixedViewPadding =
-                        res.getDimensionPixelSize(R.dimen.apps_customize_horizontal_padding);
-
-                padding.left += pagedFixedViewPadding;
-                padding.right += pagedFixedViewPadding;
-
-                pagedView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
-                fakePageContainer.setPadding(padding.left, padding.top, padding.right, padding.bottom);
-
-            }
-        }
-
         // Layout the Overview Mode
         ViewGroup overviewMode = launcher.getOverviewPanel();
         if (overviewMode != null) {
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index 7369eea..2a1346e 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -22,9 +22,9 @@
 
 /**
  * Interface defining an object that can originate a drag.
- *
  */
 public interface DragSource {
+
     /**
      * @return whether items dragged from this source supports
      */
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index ff02bbb..28e923e 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -32,7 +32,7 @@
 import android.graphics.drawable.Drawable;
 import android.util.SparseArray;
 
-class FastBitmapDrawable extends Drawable {
+public class FastBitmapDrawable extends Drawable {
 
     static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
 
@@ -72,7 +72,7 @@
     private boolean mPressed = false;
     private ObjectAnimator mPressedAnimator;
 
-    FastBitmapDrawable(Bitmap b) {
+    public FastBitmapDrawable(Bitmap b) {
         mAlpha = 255;
         mBitmap = b;
         setBounds(0, 0, b.getWidth(), b.getHeight());
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index f4af7f5..f6238da 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PackageItemInfo;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -454,12 +455,13 @@
      * Fill in {@param appInfo} with the icon and label for {@param packageName}
      */
     public synchronized void getTitleAndIconForApp(
-            String packageName, UserHandleCompat user, boolean useLowResIcon, AppInfo appInfoOut) {
+            String packageName, UserHandleCompat user, boolean useLowResIcon,
+            PackageItemInfo infoOut) {
         CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon);
-        appInfoOut.iconBitmap = entry.icon;
-        appInfoOut.title = entry.title;
-        appInfoOut.usingLowResIcon = entry.isLowResIcon;
-        appInfoOut.contentDescription = entry.contentDescription;
+        infoOut.iconBitmap = entry.icon;
+        infoOut.title = entry.title;
+        infoOut.usingLowResIcon = entry.isLowResIcon;
+        infoOut.contentDescription = entry.contentDescription;
     }
 
     public synchronized Bitmap getDefaultIcon(UserHandleCompat user) {
diff --git a/src/com/android/launcher3/Insettable.java b/src/com/android/launcher3/Insettable.java
index 1d2356c..3b8ef2f 100644
--- a/src/com/android/launcher3/Insettable.java
+++ b/src/com/android/launcher3/Insettable.java
@@ -18,6 +18,10 @@
 
 import android.graphics.Rect;
 
+/**
+ * Allows the implementing {@link View} to not draw underneath system bars.
+ * e.g., notification bar on top and home key area on the bottom.
+ */
 public interface Insettable {
 
     void setInsets(Rect insets);
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index f114de2..f7e0ea4 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -36,7 +36,7 @@
      */
     static final String EXTRA_PROFILE = "profile";
     
-    static final int NO_ID = -1;
+    public static final int NO_ID = -1;
     
     /**
      * The id in the settings database for this item
@@ -82,7 +82,7 @@
     /**
      * Indicates the Y cell span.
      */
-    int spanY = 1;
+    public int spanY = 1;
 
     /**
      * Indicates the minimum X cell span.
@@ -107,21 +107,21 @@
     /**
      * Title of the item
      */
-    CharSequence title;
+    public CharSequence title;
 
     /**
      * Content description of the item.
      */
-    CharSequence contentDescription;
+    public CharSequence contentDescription;
 
     /**
      * The position of the item in a drag-and-drop operation.
      */
-    int[] dropPos = null;
+    public int[] dropPos = null;
 
-    UserHandleCompat user;
+    public UserHandleCompat user;
 
-    ItemInfo() {
+    public ItemInfo() {
         user = UserHandleCompat.myUserHandle();
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2fa2f4a..068934e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -103,6 +103,8 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetsContainerView;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -130,11 +132,11 @@
         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
                    View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
                    LauncherStateTransitionAnimation.Callbacks {
-    static final String TAG = "Launcher";
-    static final boolean LOGD = false;
+    static final String TAG = "Launcher - MERONG";
+    static final boolean LOGD = true;
 
     static final boolean PROFILE_STARTUP = false;
-    static final boolean DEBUG_WIDGETS = false;
+    static final boolean DEBUG_WIDGETS = true;
     static final boolean DEBUG_STRICT_MODE = false;
     static final boolean DEBUG_RESUME_TIME = false;
     static final boolean DEBUG_DUMP_LOG = false;
@@ -264,9 +266,13 @@
     private View mAllAppsButton;
 
     private SearchDropTargetBar mSearchDropTargetBar;
+
+    // Main container view for the all apps screen.
     @Thunk AppsContainerView mAppsView;
-    @Thunk AppsCustomizeTabHost mAppsCustomizeTabHost;
-    private AppsCustomizePagedView mAppsCustomizeContent;
+
+    // Main container view for the widget tray screen.
+    private WidgetsContainerView mWidgetsView;
+
     private boolean mAutoAdvanceRunning = false;
     private AppWidgetHostView mQsb;
 
@@ -672,7 +678,7 @@
         return mInflater;
     }
 
-    boolean isDraggingEnabled() {
+    public boolean isDraggingEnabled() {
         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
         // that is subsequently removed from the workspace in startBinding().
         return !mModel.isLoadingWorkspace();
@@ -1013,15 +1019,9 @@
                 startTimeCallbacks = System.currentTimeMillis();
             }
 
-            if (mAppsCustomizeContent != null) {
-                mAppsCustomizeContent.setBulkBind(true);
-            }
             for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
                 mBindOnResumeCallbacks.get(i).run();
             }
-            if (mAppsCustomizeContent != null) {
-                mAppsCustomizeContent.setBulkBind(false);
-            }
             mBindOnResumeCallbacks.clear();
             if (DEBUG_RESUME_TIME) {
                 Log.d(TAG, "Time spent processing callbacks in onResume: " +
@@ -1213,9 +1213,8 @@
         if (mModel.isCurrentCallbacks(this)) {
             mModel.stopLoader();
         }
-        if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.surrender();
-        }
+        //TODO(hyunyoungs): stop the widgets loader when there is a rotation.
+
         return Boolean.TRUE;
     }
 
@@ -1336,19 +1335,6 @@
             mRestoring = true;
         }
 
-        // Restore the AppsCustomize tab
-        if (mAppsCustomizeTabHost != null) {
-            String curTab = savedState.getString("apps_customize_currentTab");
-            if (curTab != null) {
-                mAppsCustomizeTabHost.setContentTypeImmediate(
-                        mAppsCustomizeTabHost.getContentTypeForTabTag(curTab));
-                mAppsCustomizeContent.loadAssociatedPages(
-                        mAppsCustomizeContent.getCurrentPage());
-            }
-
-            int currentIndex = savedState.getInt("apps_customize_currentIndex");
-            mAppsCustomizeContent.restorePageForIndex(currentIndex);
-        }
         mItemIdToViewId = (HashMap<Integer, Integer>)
                 savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
     }
@@ -1434,10 +1420,7 @@
         mAppsView = (AppsContainerView) findViewById(R.id.apps_view);
 
         // Setup AppsCustomize
-        mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane);
-        mAppsCustomizeContent = (AppsCustomizePagedView)
-                mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
-        mAppsCustomizeContent.setup(this, dragController);
+        mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         dragController.setDragScoller(mWorkspace);
@@ -1651,7 +1634,7 @@
 
                 // Reset AllApps to its initial state only if we are not in the middle of
                 // processing a multi-step drop
-                if (mAppsView != null && mAppsCustomizeTabHost != null &&
+                if (mAppsView != null && mWidgetsView != null &&
                         mPendingAddInfo.container == ItemInfo.NO_ID) {
                     showWorkspace(false);
                 }
@@ -1735,7 +1718,6 @@
         // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
         // is a more appropriate event to handle
         if (mVisible) {
-            mAppsCustomizeTabHost.onWindowVisible();
             if (!mWorkspaceLoading) {
                 final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
                 // We want to let Launcher draw itself at least once before we force it to build
@@ -1839,7 +1821,7 @@
         launcherInfo.hostView = null;
     }
 
-    void showOutOfSpaceMessage(boolean isHotseatLayout) {
+    public void showOutOfSpaceMessage(boolean isHotseatLayout) {
         int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
     }
@@ -1852,8 +1834,8 @@
         return mAppsView;
     }
 
-    public AppsCustomizeTabHost getWidgetsView() {
-        return mAppsCustomizeTabHost;
+    public WidgetsContainerView getWidgetsView() {
+        return mWidgetsView;
     }
 
     public Workspace getWorkspace() {
@@ -1946,9 +1928,9 @@
                 mAppsView.scrollToTop();
             }
 
-            // Reset the apps customize page
-            if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
-                mAppsCustomizeTabHost.reset();
+            // Reset the widgets view
+            if (!alreadyOnHome && mWidgetsView != null) {
+                mWidgetsView.scrollToTop();
             }
 
             if (mLauncherCallbacks != null) {
@@ -2003,16 +1985,8 @@
             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
         }
 
-        // Save the current AppsCustomize tab
-        if (mAppsCustomizeTabHost != null) {
-            AppsCustomizePagedView.ContentType type = mAppsCustomizeContent.getContentType();
-            String currentTabTag = mAppsCustomizeTabHost.getTabTagForContentType(type);
-            if (currentTabTag != null) {
-                outState.putString("apps_customize_currentTab", currentTabTag);
-            }
-            int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
-            outState.putInt("apps_customize_currentIndex", currentIndex);
-        }
+        // Save the current widgets tray?
+        // TODO(hyunyoungs)
         outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
 
         if (mLauncherCallbacks != null) {
@@ -3276,9 +3250,7 @@
             SQLiteDatabase.releaseMemory();
 
             // This clears all widget bitmaps from the widget tray
-            if (mAppsCustomizeTabHost != null) {
-                mAppsCustomizeTabHost.trimMemory();
-            }
+            // TODO(hyunyoungs)
         }
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onTrimMemory(level);
@@ -3355,15 +3327,16 @@
      * Shows the widgets view.
      */
     void showWidgetsView(boolean animated, boolean resetPageToZero) {
+        Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero);
         if (resetPageToZero) {
-            mAppsCustomizeTabHost.reset();
+            mWidgetsView.scrollToTop();
         }
         showAppsOrWidgets(animated, State.WIDGETS);
-        mAppsCustomizeTabHost.post(new Runnable() {
+
+        mWidgetsView.post(new Runnable() {
             @Override
             public void run() {
-                // We post this in-case the all apps view isn't yet constructed.
-                mAppsCustomizeTabHost.requestFocus();
+                mWidgetsView.requestFocus();
             }
         });
     }
@@ -3394,7 +3367,9 @@
                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
-    void enterSpringLoadedDragMode() {
+    public void enterSpringLoadedDragMode() {
+        Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s",
+                mState.name()));
         if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED ||
                 mState == State.WIDGETS_SPRING_LOADED) {
             return;
@@ -3405,7 +3380,7 @@
         mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
     }
 
-    void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
+    public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
             final Runnable onCompleteRunnable) {
         if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return;
 
@@ -3413,10 +3388,12 @@
             @Override
             public void run() {
                 if (successfulDrop) {
+                    // TODO(hyunyoungs): verify if this hack is still needed, if not, delete.
+                    //
                     // Before we show workspace, hide all apps again because
                     // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
                     // clean up our state transition functions
-                    mAppsCustomizeTabHost.setVisibility(View.GONE);
+                    mWidgetsView.setVisibility(View.GONE);
                     showWorkspace(true, onCompleteRunnable);
                 } else {
                     exitSpringLoadedDragMode();
@@ -3918,8 +3895,8 @@
             pendingInfo.spanY = item.spanY;
             pendingInfo.minSpanX = item.minSpanX;
             pendingInfo.minSpanY = item.minSpanY;
-            Bundle options =
-                    AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
+            Bundle options = null;
+            //        AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
 
             int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
@@ -4122,9 +4099,9 @@
         if (mAppsView != null) {
             mAppsView.setApps(apps);
         }
-        if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.onPackagesUpdated(
-                    LauncherModel.getSortedWidgetsAndShortcuts(this, false /* refresh */));
+        if (mWidgetsView != null) {
+            mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false),
+                    getPackageManager());
         }
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.bindAllApplications(apps);
@@ -4276,15 +4253,16 @@
                 mWidgetsAndShortcuts = null;
             }
         };
+
     public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
             mWidgetsAndShortcuts = widgetsAndShortcuts;
             return;
         }
 
-        // Update the widgets pane
-        if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts);
+        if (mWidgetsView != null) {
+            mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false),
+                    getPackageManager());
         }
     }
 
@@ -4577,10 +4555,8 @@
         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
         Log.d(TAG, "sFolders.size=" + sFolders.size());
         mModel.dumpState();
+        // TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
 
-        if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.dumpState();
-        }
         Log.d(TAG, "END launcher3 dump state");
     }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 2a08b81..3bd3850 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -141,7 +141,7 @@
         return mModel;
     }
 
-    LauncherAccessibilityDelegate getAccessibilityDelegate() {
+    public LauncherAccessibilityDelegate getAccessibilityDelegate() {
         return mAccessibilityDelegate;
     }
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index aeef0da..bb4580c 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -16,10 +16,10 @@
 public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
 
     public boolean isCustomWidget = false;
-    int spanX = -1;
-    int spanY = -1;
-    int minSpanX = -1;
-    int minSpanY = -1;
+    public int spanX = -1;
+    public int spanY = -1;
+    public int minSpanX = -1;
+    public int minSpanY = -1;
 
     public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
             AppWidgetProviderInfo info) {
@@ -78,10 +78,11 @@
         return super.loadIcon(context, cache.getFullResIconDpi());
     }
 
-    public String toString() {
+    public String toString(PackageManager pm) {
         if (isCustomWidget) {
-            return "LauncherAppWidgetProviderInfo(" + provider + ")";
+            return "WidgetProviderInfo(" + provider + ")";
         }
-        return super.toString();
+        return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s span(%d, %d) minSpan(%d, %d)",
+                provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm), spanX, spanY, minSpanX, minSpanY);
     }
  }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 1f36331..98ba09b 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -3628,7 +3628,7 @@
         private final HashMap<Object, String> mLabelCache;
         private final Collator mCollator;
 
-        WidgetAndShortcutNameComparator(Context context) {
+        public WidgetAndShortcutNameComparator(Context context) {
             mManager = AppWidgetManagerCompat.getInstance(context);
             mPackageManager = context.getPackageManager();
             mLabelCache = new HashMap<Object, String>();
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index d657cb5..111de40 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -22,7 +22,7 @@
 /**
  * Settings related utilities.
  */
-class LauncherSettings {
+public class LauncherSettings {
     /** Columns required on table staht will be subject to backup and restore. */
     static interface ChangeLogColumns extends BaseColumns {
         /**
@@ -121,7 +121,7 @@
     /**
      * Favorites.
      */
-    static final class Favorites implements BaseLauncherColumns {
+    public static final class Favorites implements BaseLauncherColumns {
         /**
          * The content:// style URL for this table
          */
@@ -217,12 +217,12 @@
         /**
          * The favorite is a widget
          */
-        static final int ITEM_TYPE_APPWIDGET = 4;
+        public 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;
+        public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
 
         /**
          * The favorite is a clock
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index eacf341..e92bfb0 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -23,6 +23,7 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
 import android.content.res.Resources;
+import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewAnimationUtils;
@@ -30,6 +31,7 @@
 import android.view.animation.DecelerateInterpolator;
 
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.WidgetsContainerView;
 
 import java.util.HashMap;
 
@@ -179,20 +181,12 @@
      * Starts an animation to the widgets view.
      */
     public void startAnimationToWidgets(final boolean animated) {
-        final AppsCustomizeTabHost toView = mLauncher.getWidgetsView();
+        final WidgetsContainerView toView = mLauncher.getWidgetsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
             @Override
             public void onRevealViewVisible(View revealView, View contentView,
                     View allAppsButtonView) {
-                // Hide the real page background, and swap in the fake one
-                ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false);
-                revealView.setBackground(
-                        mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark));
-            }
-            @Override
-            public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {
-                // Show the real page background
-                ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true);
+                revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark));
             }
             @Override
             public float getMaterialRevealViewFinalAlpha(View revealView) {
@@ -204,7 +198,7 @@
             }
         };
         startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(),
-                toView.getRevealView(), toView.getPageIndicators(), animated, cb);
+                toView.getRevealView(), null, animated, cb);
     }
 
     /**
@@ -500,45 +494,9 @@
     private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
               final Workspace.State toWorkspaceState, final boolean animated,
               final Runnable onCompleteRunnable) {
-        AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView();
+        WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
             @Override
-            public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {
-                AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView);
-
-                // Hide the real page background, and swap in the fake one
-                pagedView.stopScrolling();
-                pagedView.setPageBackgroundsVisible(false);
-                revealView.setBackground(
-                        mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark));
-
-                // Hide the side pages of the Widget tray to avoid some ugly edge cases
-                final View currentPage = pagedView.getPageAt(pagedView.getNextPage());
-                int count = pagedView.getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View child = pagedView.getChildAt(i);
-                    if (child != currentPage) {
-                        child.setVisibility(View.INVISIBLE);
-                    }
-                }
-            }
-            @Override
-            public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {
-                AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView);
-
-                // Show the real page background and force-update the page
-                pagedView.setPageBackgroundsVisible(true);
-                pagedView.setCurrentPage(pagedView.getNextPage());
-                pagedView.updateCurrentPageScroll();
-
-                // Unhide the side pages
-                int count = pagedView.getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View child = pagedView.getChildAt(i);
-                    child.setVisibility(View.VISIBLE);
-                }
-            }
-            @Override
             public float getMaterialRevealViewFinalYDrift(View revealView) {
                 return revealView.getMeasuredHeight() / 2;
             }
@@ -559,7 +517,7 @@
         };
         startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView,
                 widgetsView.getContentView(), widgetsView.getRevealView(),
-                widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb);
+                null, animated, onCompleteRunnable, cb);
     }
 
     /**
diff --git a/src/com/android/launcher3/PagedViewGridLayout.java b/src/com/android/launcher3/PagedViewGridLayout.java
deleted file mode 100644
index f69fa56..0000000
--- a/src/com/android/launcher3/PagedViewGridLayout.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.GridLayout;
-
-/**
- * The grid based layout used strictly for the widget/wallpaper tab of the AppsCustomize pane
- */
-public class PagedViewGridLayout extends GridLayout implements Page {
-    static final String TAG = "PagedViewGridLayout";
-
-    private int mCellCountX;
-    private int mCellCountY;
-    private Runnable mOnLayoutListener;
-
-    public PagedViewGridLayout(Context context, int cellCountX, int cellCountY) {
-        super(context, null, 0);
-        mCellCountX = cellCountX;
-        mCellCountY = cellCountY;
-    }
-
-    int getCellCountX() {
-        return mCellCountX;
-    }
-
-    int getCellCountY() {
-        return mCellCountY;
-    }
-
-    /**
-     * Clears all the key listeners for the individual widgets.
-     */
-    public void resetChildrenOnKeyListeners() {
-        int childCount = getChildCount();
-        for (int j = 0; j < childCount; ++j) {
-            getChildAt(j).setOnKeyListener(null);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mOnLayoutListener = null;
-    }
-
-    public void setOnLayoutListener(Runnable r) {
-        mOnLayoutListener = r;
-    }
-
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        if (mOnLayoutListener != null) {
-            mOnLayoutListener.run();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean result = super.onTouchEvent(event);
-        int count = getPageChildCount();
-        if (count > 0) {
-            // We only intercept the touch if we are tapping in empty space after the final row
-            View child = getChildOnPageAt(count - 1);
-            int bottom = child.getBottom();
-            result = result || (event.getY() < bottom);
-        }
-        return result;
-    }
-
-    @Override
-    public void removeAllViewsOnPage() {
-        removeAllViews();
-        mOnLayoutListener = null;
-        setLayerType(LAYER_TYPE_NONE, null);
-    }
-
-    @Override
-    public void removeViewOnPageAt(int index) {
-        removeViewAt(index);
-    }
-
-    @Override
-    public int getPageChildCount() {
-        return getChildCount();
-    }
-
-    @Override
-    public View getChildOnPageAt(int i) {
-        return getChildAt(i);
-    }
-
-    @Override
-    public int indexOfChildOnPage(View v) {
-        return indexOfChild(v);
-    }
-
-    public static class LayoutParams extends FrameLayout.LayoutParams {
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/PagedViewWithDraggableItems.java b/src/com/android/launcher3/PagedViewWithDraggableItems.java
deleted file mode 100644
index f0743cf..0000000
--- a/src/com/android/launcher3/PagedViewWithDraggableItems.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-
-
-/* Class that does most of the work of enabling dragging items out of a PagedView by performing a
- * vertical drag. Used by both CustomizePagedView and AllAppsPagedView.
- * Subclasses must do the following:
- *   * call setDragSlopeThreshold after making an instance of the PagedViewWithDraggableItems
- *   * call child.setOnLongClickListener(this) and child.setOnTouchListener(this) on all children
- *       (good place to do it is in syncPageItems)
- *   * override beginDragging(View) (but be careful to call super.beginDragging(View)
- *
- */
-public abstract class PagedViewWithDraggableItems extends PagedView
-    implements View.OnLongClickListener, View.OnTouchListener {
-    private View mLastTouchedItem;
-    private boolean mIsDragging;
-    private boolean mIsDragEnabled;
-    private float mDragSlopeThreshold;
-    private Launcher mLauncher;
-
-    public PagedViewWithDraggableItems(Context context) {
-        this(context, null);
-    }
-
-    public PagedViewWithDraggableItems(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PagedViewWithDraggableItems(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mLauncher = (Launcher) context;
-    }
-
-    protected boolean beginDragging(View v) {
-        boolean wasDragging = mIsDragging;
-        mIsDragging = true;
-        return !wasDragging;
-    }
-
-    protected void cancelDragging() {
-        mIsDragging = false;
-        mLastTouchedItem = null;
-        mIsDragEnabled = false;
-    }
-
-    private void handleTouchEvent(MotionEvent ev) {
-        final int action = ev.getAction();
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN:
-                cancelDragging();
-                mIsDragEnabled = true;
-                break;
-            case MotionEvent.ACTION_MOVE:
-                if (mTouchState != TOUCH_STATE_SCROLLING && !mIsDragging && mIsDragEnabled) {
-                    determineDraggingStart(ev);
-                }
-                break;
-        }
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        handleTouchEvent(ev);
-        return super.onInterceptTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        handleTouchEvent(ev);
-        return super.onTouchEvent(ev);
-    }
-
-    public void trimMemory() {
-        mLastTouchedItem = null;
-    }
-
-    @Override
-    public boolean onTouch(View v, MotionEvent event) {
-        mLastTouchedItem = v;
-        mIsDragEnabled = true;
-        return false;
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        // Return early if this is not initiated from a touch
-        if (!v.isInTouchMode()) return false;
-        // Return early if we are still animating the pages
-        if (mNextPage != INVALID_PAGE) return false;
-        // When we have exited all apps or are in transition, disregard long clicks
-        if (!mLauncher.isWidgetsViewVisible() ||
-                mLauncher.getWorkspace().isSwitchingState()) return false;
-        // Return if global dragging is not enabled
-        if (!mLauncher.isDraggingEnabled()) return false;
-
-        return beginDragging(v);
-    }
-
-    /*
-     * Determines if we should change the touch state to start scrolling after the
-     * user moves their touch point too far.
-     */
-    protected void determineScrollingStart(MotionEvent ev) {
-        if (!mIsDragging) super.determineScrollingStart(ev);
-    }
-
-    /*
-     * Determines if we should change the touch state to start dragging after the
-     * user moves their touch point far enough.
-     */
-    protected void determineDraggingStart(MotionEvent ev) {
-        /*
-         * Locally do absolute value. mLastMotionX is set to the y value
-         * of the down event.
-         */
-        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-        final float x = ev.getX(pointerIndex);
-        final float y = ev.getY(pointerIndex);
-        final int xDiff = (int) Math.abs(x - mLastMotionX);
-        final int yDiff = (int) Math.abs(y - mLastMotionY);
-
-        final int touchSlop = mTouchSlop;
-        boolean yMoved = yDiff > touchSlop;
-        boolean isUpwardMotion = (yDiff / (float) xDiff) > mDragSlopeThreshold;
-
-        if (isUpwardMotion && yMoved && mLastTouchedItem != null) {
-            // Drag if the user moved far enough along the Y axis
-            beginDragging(mLastTouchedItem);
-
-            // Cancel any pending long press
-            if (mAllowLongPress) {
-                mAllowLongPress = false;
-                // Try canceling the long press. It could also have been scheduled
-                // by a distant descendant, so use the mAllowLongPress flag to block
-                // everything
-                final View currentPage = getPageAt(mCurrentPage);
-                if (currentPage != null) {
-                    currentPage.cancelLongPress();
-                }
-            }
-        }
-    }
-
-    public void setDragSlopeThreshold(float dragSlopeThreshold) {
-        mDragSlopeThreshold = dragSlopeThreshold;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        cancelDragging();
-        super.onDetachedFromWindow();
-    }
-}
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index ac54a26..1aaf85b 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -16,93 +16,17 @@
 
 package com.android.launcher3;
 
-import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
-import android.os.Bundle;
-import android.os.Parcelable;
 
 /**
- * We pass this object with a drag from the customization tray
+ * Meta data that is used for deferred binding.
+ * e.g., this object is used to pass information on dragable targets when they are dropped onto
+ * the workspace from another container.
  */
-class PendingAddItemInfo extends ItemInfo {
+public class PendingAddItemInfo extends ItemInfo {
+
     /**
      * The component that will be created.
      */
-    ComponentName componentName;
-}
-
-class PendingAddShortcutInfo extends PendingAddItemInfo {
-
-    ActivityInfo shortcutActivityInfo;
-
-    public PendingAddShortcutInfo(ActivityInfo activityInfo) {
-        shortcutActivityInfo = activityInfo;
-    }
-
-    @Override
-    public String toString() {
-        return "Shortcut: " + shortcutActivityInfo.packageName;
-    }
-}
-
-class PendingAddWidgetInfo extends PendingAddItemInfo {
-    int minWidth;
-    int minHeight;
-    int minResizeWidth;
-    int minResizeHeight;
-    int previewImage;
-    int icon;
-    LauncherAppWidgetProviderInfo info;
-    AppWidgetHostView boundWidget;
-    Bundle bindOptions = null;
-
-    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;
-        minHeight = i.minHeight;
-        minResizeWidth = i.minResizeWidth;
-        minResizeHeight = i.minResizeHeight;
-        previewImage = i.previewImage;
-        icon = i.icon;
-
-        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
-    public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
-        minWidth = copy.minWidth;
-        minHeight = copy.minHeight;
-        minResizeWidth = copy.minResizeWidth;
-        minResizeHeight = copy.minResizeHeight;
-        previewImage = copy.previewImage;
-        icon = copy.icon;
-        info = copy.info;
-        boundWidget = copy.boundWidget;
-        componentName = copy.componentName;
-        itemType = copy.itemType;
-        spanX = copy.spanX;
-        spanY = copy.spanY;
-        minSpanX = copy.minSpanX;
-        minSpanY = copy.minSpanY;
-        bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
-    }
-
-    @Override
-    public String toString() {
-        return "Widget: " + componentName.toShortString();
-    }
+    public ComponentName componentName;
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 1043e2e..5c3ed92 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.WidgetCell;
 
 import java.lang.ref.WeakReference;
 import java.util.Collections;
@@ -45,6 +46,7 @@
 public class WidgetPreviewLoader {
 
     private static final String TAG = "WidgetPreviewLoader";
+    private static final boolean DEBUG = false;
 
     private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
 
@@ -78,7 +80,7 @@
      * @return a request id which can be used to cancel the request.
      */
     public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight,
-            PagedViewWidget caller, Bitmap[] immediateResult) {
+            WidgetCell caller, Bitmap[] immediateResult) {
         String size = previewWidth + "x" + previewHeight;
         WidgetCacheKey key = getObjectKey(o, size);
 
@@ -576,21 +578,26 @@
         private final Object mInfo;
         private final int mPreviewHeight;
         private final int mPreviewWidth;
-        private final PagedViewWidget mCaller;
+        private final WidgetCell mCaller;
 
         PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth,
-                int previewHeight, PagedViewWidget caller) {
+                int previewHeight, WidgetCell caller) {
             mKey = key;
             mInfo = info;
             mPreviewHeight = previewHeight;
             mPreviewWidth = previewWidth;
             mCaller = caller;
+            if (DEBUG) {
+                Log.d(TAG, String.format("%s, %s, %d, %d",
+                        mKey, mInfo, mPreviewHeight, mPreviewWidth));
+            }
         }
 
-
         @Override
         protected Bitmap doInBackground(Void... params) {
             Bitmap unusedBitmap = null;
+
+            // TODO(hyunyoungs): Figure out why this path causes concurrency issue.
             synchronized (mUnusedBitmaps) {
                 // Check if we can use a bitmap
                 for (Bitmap candidate : mUnusedBitmaps) {
@@ -608,7 +615,6 @@
                     mUnusedBitmaps.remove(unusedBitmap);
                 }
             }
-
             if (isCancelled()) {
                 return null;
             }
diff --git a/src/com/android/launcher3/WidgetsContainerView.java b/src/com/android/launcher3/WidgetsContainerView.java
deleted file mode 100644
index 7004d8b..0000000
--- a/src/com/android/launcher3/WidgetsContainerView.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package com.android.launcher3;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-
-class SectionedWidgetsRow {
-    String section;
-    List<List<Object>> widgets;
-
-    public SectionedWidgetsRow(String sc) {
-        section = sc;
-    }
-}
-
-class SectionedWidgetsAlgorithm {
-    public List<SectionedWidgetsRow> computeSectionedWidgetRows(List<Object> sortedWidgets,
-            int widgetsPerRow) {
-        List<SectionedWidgetsRow> rows = new ArrayList<>();
-        LinkedHashMap<String, List<Object>> sections = computeSectionedApps(sortedWidgets);
-        for (Map.Entry<String, List<Object>> sectionEntry : sections.entrySet()) {
-            String section = sectionEntry.getKey();
-            SectionedWidgetsRow row = new SectionedWidgetsRow(section);
-            List<Object> widgets = sectionEntry.getValue();
-            int numRows = (int) Math.ceil((float) widgets.size() / widgetsPerRow);
-            for (int i = 0; i < numRows; i++) {
-                List<Object> widgetsInRow = new ArrayList<>();
-                int offset = i * widgetsPerRow;
-                for (int j = 0; j < widgetsPerRow; j++) {
-                    widgetsInRow.add(widgets.get(offset + j));
-                }
-                row.widgets.add(widgetsInRow);
-            }
-        }
-        return rows;
-    }
-
-    private LinkedHashMap<String, List<Object>> computeSectionedApps(List<Object> sortedWidgets) {
-        LinkedHashMap<String, List<Object>> sections = new LinkedHashMap<>();
-        for (Object info : sortedWidgets) {
-            String section = getSection(info);
-            List<Object> sectionedWidgets = sections.get(section);
-            if (sectionedWidgets == null) {
-                sectionedWidgets = new ArrayList<>();
-                sections.put(section, sectionedWidgets);
-            }
-            sectionedWidgets.add(info);
-        }
-        return sections;
-    }
-
-    private String getSection(Object widgetOrShortcut) {
-        return "UNKNOWN";
-    }
-}
-
-/**
- * The widgets list view container.
- */
-public class WidgetsContainerView extends FrameLayout {
-
-
-    public WidgetsContainerView(Context context) {
-        this(context, null);
-    }
-
-    public WidgetsContainerView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-    }
-}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a79add0..8cc99a0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -71,6 +71,8 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.WallpaperUtils;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java
new file mode 100644
index 0000000..d7edf22
--- /dev/null
+++ b/src/com/android/launcher3/widget/PackageItemInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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.widget;
+
+import android.content.ComponentName;
+import android.graphics.Bitmap;
+
+import com.android.launcher3.ItemInfo;
+
+import java.util.Arrays;
+
+/**
+ * Represents a {@link Package} in the widget tray section.
+ */
+public class PackageItemInfo extends ItemInfo {
+    private static final String TAG = "PackageInfo";
+
+    /**
+     * A bitmap version of the application icon.
+     */
+    public Bitmap iconBitmap;
+
+    /**
+     * Indicates whether we're using a low res icon
+     */
+    public boolean usingLowResIcon;
+
+    public ComponentName componentName;
+
+    int flags = 0;
+
+    PackageItemInfo() {
+    }
+
+    @Override
+    public String toString() {
+        return "PackageItemInfo(title=" + title.toString() + " id=" + this.id
+                + " type=" + this.itemType + " container=" + this.container
+                + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
+                + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+                + " user=" + user + ")";
+    }
+}
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
new file mode 100644
index 0000000..a569850
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.widget;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PendingAddItemInfo;
+
+/**
+ * Meta data used for late binding of the short cuts.
+ *
+ * @see {@link PendingAddItemInfo}
+ */
+public class PendingAddShortcutInfo extends PendingAddItemInfo {
+
+    ActivityInfo activityInfo;
+
+    public PendingAddShortcutInfo(ActivityInfo activityInfo) {
+        this.activityInfo = activityInfo;
+        componentName = new ComponentName(activityInfo.packageName, activityInfo.name);
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PendingAddShortcutInfo package=%s, name=%s",
+                activityInfo.packageName, activityInfo.name);
+    }
+}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
new file mode 100644
index 0000000..db16998
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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.widget;
+
+import android.appwidget.AppWidgetHostView;
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PendingAddItemInfo;
+
+/**
+ * Meta data used for late binding of {@link LauncherAppWidgetProviderInfo}.
+ *
+ * @see {@link PendingAddItemInfo}
+ */
+public class PendingAddWidgetInfo extends PendingAddItemInfo {
+    public int minWidth;
+    public int minHeight;
+    public int minResizeWidth;
+    public int minResizeHeight;
+    public int previewImage;
+    public int icon;
+    public LauncherAppWidgetProviderInfo info;
+    public AppWidgetHostView boundWidget;
+    public Bundle bindOptions = null;
+
+    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;
+        minHeight = i.minHeight;
+        minResizeWidth = i.minResizeWidth;
+        minResizeHeight = i.minResizeHeight;
+        previewImage = i.previewImage;
+        icon = i.icon;
+
+        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
+    public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
+        minWidth = copy.minWidth;
+        minHeight = copy.minHeight;
+        minResizeWidth = copy.minResizeWidth;
+        minResizeHeight = copy.minResizeHeight;
+        previewImage = copy.previewImage;
+        icon = copy.icon;
+        info = copy.info;
+        boundWidget = copy.boundWidget;
+        componentName = copy.componentName;
+        itemType = copy.itemType;
+        spanX = copy.spanX;
+        spanY = copy.spanY;
+        minSpanX = copy.minSpanX;
+        minSpanY = copy.minSpanY;
+        bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PendingAddWidgetInfo package=%s, name=%s",
+                componentName.getPackageName(), componentName.getShortClassName());
+    }
+}
diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/widget/WidgetCell.java
similarity index 78%
rename from src/com/android/launcher3/PagedViewWidget.java
rename to src/com/android/launcher3/widget/WidgetCell.java
index d9ca7be..ccd67ce 100644
--- a/src/com/android/launcher3/PagedViewWidget.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.widget;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -23,6 +23,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.view.View;
@@ -31,15 +32,31 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 
 /**
- * The linear layout used strictly for the widget/wallpaper tab of the customization tray
+ * The linear layout used strictly for the widget tray.
  */
-public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListener {
+public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
 
-    private static PagedViewWidget sShortpressTarget = null;
+    private static final String TAG = "PagedViewWidget";
+    private static final boolean DEBUG = false;
+
+    // Temporary preset width and height of the image to keep them aligned.
+    //private static final int PRESET_PREVIEW_HEIGHT = 480;
+    //private static final int PRESET_PREVIEW_WIDTH = 480;
+
+    private int mPresetPreviewSize;
+
+    private static WidgetCell sShortpressTarget = null;
 
     private final Rect mOriginalImagePadding = new Rect();
 
@@ -53,23 +70,25 @@
     private WidgetPreviewLoader mWidgetPreviewLoader;
     private PreviewLoadRequest mActiveRequest;
 
-    public PagedViewWidget(Context context) {
+    public WidgetCell(Context context) {
         this(context, null);
     }
 
-    public PagedViewWidget(Context context, AttributeSet attrs) {
+    public WidgetCell(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public PagedViewWidget(Context context, AttributeSet attrs, int defStyle) {
+    public WidgetCell(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         final Resources r = context.getResources();
         mDimensionsFormatString = r.getString(R.string.widget_dims_format);
+        mPresetPreviewSize = r.getDimensionPixelSize(R.dimen.widget_preview_size);
 
         setWillNotDraw(false);
         setClipToPadding(false);
         setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+
     }
 
     @Override
@@ -97,8 +116,11 @@
 
     @Override
     protected void onDetachedFromWindow() {
+        if (DEBUG) {
+            Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString()));
+        }
         super.onDetachedFromWindow();
-        deletePreview(true);
+        deletePreview(false);
     }
 
     public void deletePreview(boolean recycleImage) {
@@ -154,15 +176,19 @@
     public int[] getPreviewSize() {
         final ImageView i = (ImageView) findViewById(R.id.widget_preview);
         int[] maxSize = new int[2];
-        maxSize[0] = i.getWidth() - mOriginalImagePadding.left - mOriginalImagePadding.right;
-        maxSize[1] = i.getHeight() - mOriginalImagePadding.top;
+        maxSize[0] = mPresetPreviewSize;
+        maxSize[1] = mPresetPreviewSize;
         return maxSize;
     }
 
     public void applyPreview(Bitmap bitmap) {
         FastBitmapDrawable preview = new FastBitmapDrawable(bitmap);
-        final PagedViewWidgetImageView image =
-            (PagedViewWidgetImageView) findViewById(R.id.widget_preview);
+        final WidgetImageView image =
+            (WidgetImageView) findViewById(R.id.widget_preview);
+        if (DEBUG) {
+            Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s",
+                    getTagToString(), preview));
+        }
         if (preview != null) {
             image.mAllowRequestLayout = false;
             image.setImageDrawable(preview);
@@ -177,6 +203,7 @@
             }
             image.setAlpha(1f);
             image.mAllowRequestLayout = true;
+            image.requestLayout();
         }
     }
 
@@ -193,8 +220,8 @@
         public void run() {
             if (sShortpressTarget != null) return;
             if (mShortPressListener != null) {
-                mShortPressListener.onShortPress(PagedViewWidget.this);
-                sShortpressTarget = PagedViewWidget.this;
+                mShortPressListener.onShortPress(WidgetCell.this);
+                sShortpressTarget = WidgetCell.this;
             }
             mShortPressTriggered = true;
         }
@@ -221,7 +248,7 @@
         removeShortPressCallback();
         if (mShortPressTriggered) {
             if (mShortPressListener != null) {
-                mShortPressListener.cleanUpShortPress(PagedViewWidget.this);
+                mShortPressListener.cleanUpShortPress(WidgetCell.this);
             }
             mShortPressTriggered = false;
         }
@@ -264,6 +291,10 @@
             return;
         }
         int[] size = getPreviewSize();
+        if (DEBUG) {
+            Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
+                    getTagToString(), size[0], size[1]));
+        }
 
         if (size[0] <= 0 || size[1] <= 0) {
             addOnLayoutChangeListener(this);
@@ -292,4 +323,16 @@
 
         return Math.min(size[0], info.spanX * cellWidth);
     }
+
+    /**
+     * Helper method to get the string info of the tag.
+     */
+    private String getTagToString() {
+        if (getTag() instanceof PendingAddWidgetInfo) {
+            return ((PendingAddWidgetInfo)getTag()).toString();
+        } else if (getTag() instanceof PendingAddShortcutInfo) {
+            return ((PendingAddShortcutInfo)getTag()).toString();
+        }
+        return "";
+    }
 }
diff --git a/src/com/android/launcher3/PagedViewWidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
similarity index 88%
rename from src/com/android/launcher3/PagedViewWidgetImageView.java
rename to src/com/android/launcher3/widget/WidgetImageView.java
index 7d82795..75167bc 100644
--- a/src/com/android/launcher3/PagedViewWidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.widget;
 
 import android.content.Context;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
-public class PagedViewWidgetImageView extends ImageView {
+public class WidgetImageView extends ImageView {
     public boolean mAllowRequestLayout = true;
 
-    public PagedViewWidgetImageView(Context context, AttributeSet attrs) {
+    public WidgetImageView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
@@ -44,6 +44,5 @@
 
         super.onDraw(canvas);
         canvas.restore();
-
     }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
new file mode 100644
index 0000000..6580ab4
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2015 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.widget;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DragController;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.Folder;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.Workspace;
+
+import java.util.ArrayList;
+
+/**
+ * The widgets list view container.
+ */
+public class WidgetsContainerView extends FrameLayout implements Insettable, View.OnTouchListener,
+        View.OnLongClickListener, DragSource{
+
+    private static final String TAG = "WidgetContainerView";
+    private static final boolean DEBUG = false;
+
+    /* {@link RecyclerView} will keep following # of views in cache, before recycling. */
+    private static final int WIDGET_CACHE_SIZE = 2;
+
+    /* Global instances that are used inside this container. */
+    private Launcher mLauncher;
+    private DragController mDragController;
+    private IconCache mIconCache;
+
+    /* Data model for the widget */
+    private WidgetsModel mWidgets;
+
+    /* Recycler view related member variables */
+    private RecyclerView mView;
+    private WidgetsListAdapter mAdapter;
+
+    /* Dragging related. */
+    private boolean mDraggingWidget = false;    // TODO(hyunyoungs): seems not needed? check!
+    private Point mLastTouchDownPos = new Point();
+
+    /* Rendering related. */
+    private WidgetPreviewLoader mWidgetPreviewLoader;
+    private Rect mPadding = new Rect();
+
+    public WidgetsContainerView(Context context) {
+        this(context, null);
+    }
+
+    public WidgetsContainerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = (Launcher) context;
+        mDragController = mLauncher.getDragController();
+
+        mAdapter = new WidgetsListAdapter(context, this, mLauncher, this, mLauncher);
+        mWidgets = new WidgetsModel(context, mAdapter);
+        mAdapter.setWidgetsModel(mWidgets);
+        mIconCache = (LauncherAppState.getInstance()).getIconCache();
+
+        if (DEBUG) {
+            Log.d(TAG, "WidgetsContainerView constructor");
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        if (DEBUG) {
+            Log.d(TAG, String.format("onFinishInflate [widgets size=%d]",
+                    mWidgets.getPackageSize()));
+        }
+        mView = (RecyclerView) findViewById(R.id.widgets_list_view);
+        mView.setAdapter(mAdapter);
+        mView.setLayoutManager(new LinearLayoutManager(getContext()));
+        mView.setItemViewCacheSize(WIDGET_CACHE_SIZE);
+
+        mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
+                getPaddingBottom());
+    }
+
+    //
+    // Returns views used for launcher transitions.
+    //
+
+    public View getContentView() {
+        return findViewById(R.id.widgets_list_view);
+    }
+
+    public View getRevealView() {
+        // TODO(hyunyoungs): temporarily use apps view transition.
+        return findViewById(R.id.widgets_reveal_view);
+    }
+
+    public void scrollToTop() {
+        mView.scrollToPosition(0);
+        if (DEBUG) {
+            Log.d(TAG, String.format("scrollToTop, [widgets size=%d]",
+                    mWidgets.getPackageSize()));
+        }
+    }
+
+    //
+    // Touch related handling.
+    //
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (DEBUG) {
+            Log.d(TAG, String.format("onLonglick [v=%s]", v));
+        }
+
+        // Return early if this is not initiated from a touch
+        if (!v.isInTouchMode()) return false;
+        // When we have exited all apps or are in transition, disregard long clicks
+        if (!mLauncher.isWidgetsViewVisible() ||
+                mLauncher.getWorkspace().isSwitchingState()) return false;
+        // Return if global dragging is not enabled
+        Log.d(TAG, String.format("onLonglick dragging enabled?.", v));
+        if (!mLauncher.isDraggingEnabled()) return false;
+
+        return beginDragging(v);
+    }
+
+    private boolean beginDragging(View v) {
+        if (v instanceof WidgetCell) {
+            if (!beginDraggingWidget((WidgetCell) v)) {
+                return false;
+            }
+        } else {
+            Log.e(TAG, "Unexpected dragging view: " + v);
+        }
+
+        // We delay entering spring-loaded mode slightly to make sure the UI
+        // thready is free of any work.
+        postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                // We don't enter spring-loaded mode if the drag has been cancelled
+                if (mLauncher.getDragController().isDragging()) {
+                    // Go into spring loaded mode (must happen before we startDrag())
+                    mLauncher.enterSpringLoadedDragMode();
+                }
+            }
+        }, 150);
+
+        return true;
+    }
+
+    private boolean beginDraggingWidget(WidgetCell v) {
+        mDraggingWidget = true;
+        // Get the widget preview as the drag representation
+        ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
+        PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
+
+        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
+        // we abort the drag.
+        if (image.getDrawable() == null) {
+            mDraggingWidget = false;
+            return false;
+        }
+
+        // Compose the drag image
+        Bitmap preview;
+        Bitmap outline;
+        float scale = 1f;
+        Point previewPadding = null;
+
+        if (createItemInfo instanceof PendingAddWidgetInfo) {
+            // This can happen in some weird cases involving multi-touch. We can't start dragging
+            // the widget if this is null, so we break out.
+
+            PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo;
+            int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true);
+
+            FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
+            float minScale = 1.25f;
+            int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
+
+            int[] previewSizeBeforeScale = new int[1];
+            preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
+                    maxWidth, null, previewSizeBeforeScale);
+            // Compare the size of the drag preview to the preview in the AppsCustomize tray
+            int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
+                    v.getActualItemWidth());
+            scale = previewWidthInAppsCustomize / (float) preview.getWidth();
+
+            // The bitmap in the AppsCustomize tray is always the the same size, so there
+            // might be extra pixels around the preview itself - this accounts for that
+            if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) {
+                int padding =
+                        (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2;
+                previewPadding = new Point(padding, 0);
+            }
+        } else {
+            PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
+            Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo);
+            preview = Utilities.createIconBitmap(icon, mLauncher);
+            createItemInfo.spanX = createItemInfo.spanY = 1;
+        }
+
+        // Don't clip alpha values for the drag outline if we're using the default widget preview
+        boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
+                (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
+
+        // Save the preview for the outline generation, then dim the preview
+        outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
+                false);
+
+        // Start the drag
+        mLauncher.lockScreenOrientation();
+        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
+        mDragController.startDrag(image, preview, this, createItemInfo,
+                DragController.DRAG_ACTION_COPY, previewPadding, scale);
+        outline.recycle();
+        preview.recycle();
+        return true;
+    }
+
+    /*
+     * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent)
+     */
+    @Override
+    public boolean onTouch(View v, MotionEvent ev) {
+        Log.d(TAG, String.format("onTouch [MotionEvent=%s]", ev));
+        if (ev.getAction() == MotionEvent.ACTION_DOWN ||
+                ev.getAction() == MotionEvent.ACTION_MOVE) {
+            mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY());
+        }
+        return false;
+    }
+
+    //
+    // Drag related handling methods that implement {@link DragSource} interface.
+    //
+
+    @Override
+    public boolean supportsFlingToDelete() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsAppInfoDropTarget() {
+        return true;
+    }
+
+    /*
+     * Both this method and {@link #supportsFlingToDelete} has to return {@code false} for the
+     * {@link DeleteDropTarget} to be invisible.)
+     */
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return false;
+    }
+
+    @Override
+    public float getIntrinsicIconScaleFactor() {
+        return 0;
+    }
+
+    @Override
+    public void onFlingToDeleteCompleted() {
+        // We just dismiss the drag when we fling, so cleanup here
+        mLauncher.exitSpringLoadedDragModeDelayed(true,
+                Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+        mLauncher.unlockScreenOrientation(false);
+    }
+
+    @Override
+    public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
+            boolean success) {
+        if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
+                !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
+            // Exit spring loaded mode if we have not successfully dropped or have not handled the
+            // drop in Workspace
+            mLauncher.exitSpringLoadedDragModeDelayed(true,
+                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+        }
+        mLauncher.unlockScreenOrientation(false);
+
+        // Display an error message if the drag failed due to there not being enough space on the
+        // target layout we were dropping on.
+        if (!success) {
+            boolean showOutOfSpaceMessage = false;
+            if (target instanceof Workspace) {
+                int currentScreen = mLauncher.getCurrentWorkspaceScreen();
+                Workspace workspace = (Workspace) target;
+                CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
+                ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+                if (layout != null) {
+                    layout.calculateSpans(itemInfo);
+                    showOutOfSpaceMessage =
+                            !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
+                }
+            }
+            if (showOutOfSpaceMessage) {
+                mLauncher.showOutOfSpaceMessage(false);
+            }
+            d.deferDragViewCleanupPostAnimation = false;
+        }
+    }
+
+    //
+    // Container rendering related.
+    //
+
+    /*
+     * @see Insettable#setInsets(Rect)
+     */
+    @Override
+    public void setInsets(Rect insets) {
+        setPadding(mPadding.left + insets.left, mPadding.top + insets.top,
+                mPadding.right + insets.right, mPadding.bottom + insets.bottom);
+    }
+
+    /**
+     * Initialize the widget data model.
+     */
+    public void addWidgets(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
+        mWidgets.addWidgetsAndShortcuts(widgetsShortcuts, pm);
+    }
+
+    private WidgetPreviewLoader getWidgetPreviewLoader() {
+        if (mWidgetPreviewLoader == null) {
+            mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
+        }
+        return mWidgetPreviewLoader;
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
new file mode 100644
index 0000000..d0d1e60
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2015 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.widget;
+
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.util.List;
+
+/**
+ * List view adapter for the widget tray.
+ *
+ * <p>Memory vs. Performance:
+ * The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
+ * happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is
+ * only a single type of view.
+ */
+public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
+
+    private static final String TAG = "WidgetsListAdapter";
+    private static final boolean DEBUG = false;
+
+    private Context mContext;
+    private Launcher mLauncher;
+    private LayoutInflater mLayoutInflater;
+    private IconCache mIconCache;
+
+    private WidgetsModel mWidgetsModel;
+    private WidgetPreviewLoader mWidgetPreviewLoader;
+
+    private View.OnTouchListener mTouchListener;
+    private View.OnClickListener mIconClickListener;
+    private View.OnLongClickListener mIconLongClickListener;
+
+
+    public WidgetsListAdapter(Context context,
+            View.OnTouchListener touchListener,
+            View.OnClickListener iconClickListener,
+            View.OnLongClickListener iconLongClickListener,
+            Launcher launcher) {
+        mLayoutInflater = LayoutInflater.from(context);
+        mContext = context;
+
+        mTouchListener = touchListener;
+        mIconClickListener = iconClickListener;
+        mIconLongClickListener = iconLongClickListener;
+
+        mLauncher = launcher;
+        mIconCache = LauncherAppState.getInstance().getIconCache();
+    }
+
+    public void setWidgetsModel(WidgetsModel w) {
+        mWidgetsModel = w;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mWidgetsModel.getPackageSize();
+    }
+
+    @Override
+    public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
+        String packageName = mWidgetsModel.getPackageName(pos);
+        List<Object> infoList = mWidgetsModel.getSortedWidgets(packageName);
+
+        ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list));
+        if (DEBUG) {
+            Log.d(TAG, String.format(
+                    "onBindViewHolder [pos=%d, packageName=%s, widget#=%d, row.getChildCount=%d]",
+                    pos, packageName, infoList.size(), row.getChildCount()));
+        }
+
+        // Add more views.
+        // if there are too many, hide them.
+        int diff = infoList.size() - row.getChildCount();
+        if (diff > 0) {
+            for (int i = 0; i < diff; i++) {
+                WidgetCell widget = new WidgetCell(mContext);
+                widget = (WidgetCell) mLayoutInflater.inflate(
+                        R.layout.widget_cell, row, false);
+
+                // set up touch.
+                widget.setOnClickListener(mIconClickListener);
+                widget.setOnLongClickListener(mIconLongClickListener);
+                widget.setOnTouchListener(mTouchListener);
+                row.addView(widget);
+            }
+        } else if (diff < 0) {
+            for (int i=infoList.size() ; i < row.getChildCount(); i++) {
+                row.getChildAt(i).setVisibility(View.GONE);
+            }
+        }
+
+        // Bind the views in the application info section.
+        PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(packageName);
+        if (infoOut.usingLowResIcon) {
+            mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
+                false /* useLowResIcon */, infoOut);
+        }
+        ((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title);
+        ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image);
+        iv.setImageBitmap(infoOut.iconBitmap);
+
+        // Bind the view in the widget horizontal tray region.
+        for (int i=0; i < infoList.size(); i++) {
+            WidgetCell widget = (WidgetCell) row.getChildAt(i);
+            if (getWidgetPreviewLoader() == null || widget == null) {
+                return;
+            }
+            if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
+                LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i);
+                PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null);
+                widget.setTag(pawi);
+                widget.applyFromAppWidgetProviderInfo(info, -1, mWidgetPreviewLoader);
+            } else if (infoList.get(i) instanceof ResolveInfo) {
+                ResolveInfo info = (ResolveInfo) infoList.get(i);
+                PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo);
+                widget.setTag(pasi);
+                widget.applyFromResolveInfo(mLauncher.getPackageManager(), info, mWidgetPreviewLoader);
+            }
+            widget.setVisibility(View.VISIBLE);
+            widget.ensurePreview();
+        }
+        // TODO(hyunyoungs): Draw the scrollable indicator.
+    }
+
+    @Override
+    public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        if (DEBUG) {
+            Log.v(TAG, String.format("\nonCreateViewHolder, [widget#=%d]", viewType));
+        }
+
+        ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
+                R.layout.widgets_list_row_view, parent, false);
+        return new WidgetsRowViewHolder(container);
+    }
+
+    @Override
+    public long getItemId(int pos) {
+        return pos;
+    }
+
+    private WidgetPreviewLoader getWidgetPreviewLoader() {
+        if (mWidgetPreviewLoader == null) {
+            mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
+        }
+        return mWidgetPreviewLoader;
+    }
+
+    /**
+     * TODO(hyunyoungs): this is temporary. Figure out the width of each widget cell
+     * and then check if the total sum is longer than the parent width.
+     */
+    private void addScrollableIndicator(int contentSize, ViewGroup parent) {
+        if (contentSize > 2) {
+            ViewGroup indicator = (ViewGroup) parent.findViewById(R.id.scrollable_indicator);
+            indicator.setVisibility(View.VISIBLE);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java
new file mode 100644
index 0000000..c400d63
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsModel.java
@@ -0,0 +1,136 @@
+
+package com.android.launcher3.widget;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherModel.WidgetAndShortcutNameComparator;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Widgets data model that is used by the adapters of the widget views and controllers.
+ *
+ * <p> The widgets and shortcuts are organized using package name as its index.
+ */
+public class WidgetsModel {
+
+    private static final String TAG = "WidgetsModel";
+    private static final boolean DEBUG = false;
+
+    /* List of packages that is tracked by this model. */
+    private List<String> mPackageNames = new ArrayList<>();
+
+    private Map<String, PackageItemInfo> mPackageItemInfoList = new HashMap<>();
+
+    /* Map of widgets and shortcuts that are tracked per package. */
+    private Map<String, ArrayList<Object>> mWidgetsList = new HashMap<>();
+
+    /* Notifies the adapter when data changes. */
+    private RecyclerView.Adapter mAdapter;
+
+    private Comparator mWidgetAndShortcutNameComparator;
+
+    private IconCache mIconCache;
+
+    public WidgetsModel(Context context, RecyclerView.Adapter adapter) {
+        mAdapter = adapter;
+        mWidgetAndShortcutNameComparator = new WidgetAndShortcutNameComparator(context);
+        mIconCache = LauncherAppState.getInstance().getIconCache();
+    }
+
+    // Access methods that may be deleted if the private fields are made package-private.
+    public int getPackageSize() {
+        return mPackageNames.size();
+    }
+
+    // Access methods that may be deleted if the private fields are made package-private.
+    public String getPackageName(int pos) {
+        return mPackageNames.get(pos);
+    }
+
+    public PackageItemInfo getPackageItemInfo(String packageName) {
+        return mPackageItemInfoList.get(packageName);
+    }
+
+    public List<Object> getSortedWidgets(String packageName) {
+        return mWidgetsList.get(packageName);
+    }
+
+    public void addWidgetsAndShortcuts(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
+        if (DEBUG) {
+            Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size());
+        }
+
+        // clear the lists.
+        mPackageNames.clear();
+        mWidgetsList.clear();
+
+        // add and update.
+        for (Object o: widgetsShortcuts) {
+            String packageName = "";
+            if (o instanceof LauncherAppWidgetProviderInfo) {
+                LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
+                packageName = widgetInfo.provider.getPackageName();
+            } else if (o instanceof ResolveInfo) {
+                ResolveInfo resolveInfo = (ResolveInfo) o;
+                packageName = resolveInfo.activityInfo.packageName;
+            } else {
+                Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s",
+                        o.getClass().toString()));
+                
+            }
+
+            ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(packageName);
+            if (widgetsShortcutsList != null) {
+                widgetsShortcutsList.add(o);
+            } else {
+                widgetsShortcutsList = new ArrayList<Object>();
+                widgetsShortcutsList.add(o);
+                mWidgetsList.put(packageName, widgetsShortcutsList);
+                mPackageNames.add(packageName);
+            }
+        }
+        for (String packageName: mPackageNames) {
+            PackageItemInfo pInfo = mPackageItemInfoList.get(packageName);
+            if (pInfo == null) {
+                pInfo = new PackageItemInfo();
+                mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
+                        true /* useLowResIcon */, pInfo);
+                mPackageItemInfoList.put(packageName, pInfo);
+            }
+        }
+
+        // sort.
+        sortPackageList();
+        for (String packageName: mPackageNames) {
+            Collections.sort(mWidgetsList.get(packageName), mWidgetAndShortcutNameComparator);
+        }
+
+        // notify.
+        mAdapter.notifyDataSetChanged();
+    }
+
+    private void sortPackageList() {
+        Collections.sort(mPackageNames, new Comparator<String>() {
+            @Override
+            public int compare(String lhs, String rhs) {
+                String lhsTitle = mPackageItemInfoList.get(lhs).title.toString();
+                String rhsTitle = mPackageItemInfoList.get(rhs).title.toString();
+                return lhsTitle.compareTo(rhsTitle);
+            }
+        });
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsRowView.java b/src/com/android/launcher3/widget/WidgetsRowView.java
new file mode 100644
index 0000000..5466738
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsRowView.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 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.widget;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+
+/**
+ * Layout used for widget tray rows for each app. For performance, this view can be replaced with
+ * a {@link RecyclerView} in the future if we settle on scrollable single row for the widgets.
+ * If we decide on collapsable grid, then HorizontalScrollView can be replaced with a
+ * {@link GridLayout}.
+ */
+public class WidgetsRowView extends HorizontalScrollView {
+    static final String TAG = "WidgetsRow";
+
+    private Runnable mOnLayoutListener;
+    private String mAppName;
+
+    public WidgetsRowView(Context context, String appName) {
+        super(context, null, 0);
+        mAppName = appName;
+    }
+
+    /**
+     * Clears all the key listeners for the individual widgets.
+     */
+    public void resetChildrenOnKeyListeners() {
+        int childCount = getChildCount();
+        for (int j = 0; j < childCount; ++j) {
+            getChildAt(j).setOnKeyListener(null);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        TextView tv = (TextView) findViewById(R.id.widget_name);
+        tv.setText(mAppName);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mOnLayoutListener = null;
+    }
+
+    public void setOnLayoutListener(Runnable r) {
+        mOnLayoutListener = r;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mOnLayoutListener != null) {
+            mOnLayoutListener.run();
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean result = super.onTouchEvent(event);
+        return result;
+    }
+
+    public static class LayoutParams extends FrameLayout.LayoutParams {
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
new file mode 100644
index 0000000..99a192c
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.widget;
+
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class WidgetsRowViewHolder extends ViewHolder {
+
+    ViewGroup mContent;
+
+   public WidgetsRowViewHolder(ViewGroup v) {
+        super(v);
+        mContent = v;
+    }
+
+    ViewGroup getContent() {
+        return mContent;
+    }
+}