Adding back the widgets pane to the merged AllApps.

- Fixing issue with holographic drag outlines being drawn offset

Change-Id: Ie0e89d76ce6714b44f9cd2ddf99ad608b51bf005
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 6dc34ca..f0a712a 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -20,6 +20,10 @@
 import java.util.Collections;
 import java.util.List;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -33,14 +37,18 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.Checkable;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.launcher.R;
@@ -71,29 +79,48 @@
 
     // Dimens
     private int mContentWidth;
-    private float mWidgetScale;
+    private int mMaxWidgetSpan, mMinWidgetSpan;
+    private int mWidgetCellWidthGap, mWidgetCellHeightGap;
+    private int mWidgetCountX, mWidgetCountY;
     private PagedViewCellLayout mWidgetSpacingLayout;
 
+    // Animations
+    private final float ANIMATION_SCALE = 0.5f;
+    private final int TRANSLATE_ANIM_DURATION = 400;
+    private final int DROP_ANIM_DURATION = 200;
+
     public AppsCustomizePagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLayoutInflater = LayoutInflater.from(context);
         mPackageManager = context.getPackageManager();
         mContentType = ContentType.Applications;
         mApps = new ArrayList<ApplicationInfo>();
+        mWidgets = new ArrayList<AppWidgetProviderInfo>();
+        mShortcuts = new ArrayList<ResolveInfo>();
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, 0, 0);
         mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 6);
         mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4);
         a.recycle();
+        a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
+        mWidgetCellWidthGap =
+            a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellWidthGap, 10);
+        mWidgetCellHeightGap =
+            a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellHeightGap, 10);
+        mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);
+        mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
+        a.recycle();
 
-        // Create a dummy page and set it up to find out the content width (used by our parent)
-        PagedViewCellLayout layout = new PagedViewCellLayout(context);
-        setupPage(layout);
-        mContentWidth = layout.getContentWidth();
-
-        // Create a dummy page that we can use to approximate the cell dimensions of widgets
+        // Create a dummy page that we can use to approximate the cell dimensions of widgets and
+        // the content width (to be used by our parent)
         mWidgetSpacingLayout = new PagedViewCellLayout(context);
-        mWidgetSpacingLayout.setCellCount(mCellCountX, mCellCountY);
+        setupPage(mWidgetSpacingLayout);
+        mContentWidth = mWidgetSpacingLayout.getContentWidth();
+
+        // The max widget span is the length N, such that NxN is the largest bounds that the widget
+        // preview can be before applying the widget scaling
+        mMinWidgetSpan = 1;
+        mMaxWidgetSpan = 3;
     }
 
     @Override
@@ -117,16 +144,140 @@
         Collections.sort(mShortcuts, new LauncherModel.ShortcutNameComparator(mPackageManager));
     }
 
-    @Override
-    public void onClick(View v) {
-        // Animate some feedback to the click
-        final ApplicationInfo appInfo = (ApplicationInfo) v.getTag();
-        animateClickFeedback(v, new Runnable() {
-            @Override
-            public void run() {
-                mLauncher.startActivitySafely(appInfo.intent, appInfo);
+    /**
+     * Animates the given item onto the center of a home screen, and then scales the item to
+     * look as though it's disappearing onto that screen.
+     */
+    private void animateItemOntoScreen(View dragView,
+            final CellLayout layout, final ItemInfo info) {
+        // On the phone, we only want to fade the widget preview out
+        float[] position = new float[2];
+        position[0] = layout.getWidth() / 2;
+        position[1] = layout.getHeight() / 2;
+
+        mLauncher.getWorkspace().mapPointFromChildToSelf(layout, position);
+
+        int dragViewWidth = dragView.getMeasuredWidth();
+        int dragViewHeight = dragView.getMeasuredHeight();
+        float heightOffset = 0;
+        float widthOffset = 0;
+
+        if (dragView instanceof ImageView) {
+            Drawable d = ((ImageView) dragView).getDrawable();
+            int width = d.getIntrinsicWidth();
+            int height = d.getIntrinsicHeight();
+
+            if ((1.0 * width / height) >= (1.0f * dragViewWidth) / dragViewHeight) {
+                float f = (dragViewWidth / (width * 1.0f));
+                heightOffset = ANIMATION_SCALE * (dragViewHeight - f * height) / 2;
+            } else {
+                float f = (dragViewHeight / (height * 1.0f));
+                widthOffset = ANIMATION_SCALE * (dragViewWidth - f * width) / 2;
+            }
+        }
+        final float toX = position[0] - dragView.getMeasuredWidth() / 2 + widthOffset;
+        final float toY = position[1] - dragView.getMeasuredHeight() / 2 + heightOffset;
+
+        final DragLayer dragLayer = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
+        final View dragCopy = dragLayer.createDragView(dragView);
+        dragCopy.setAlpha(1.0f);
+
+        // Translate the item to the center of the appropriate home screen
+        animateIntoPosition(dragCopy, toX, toY, null);
+
+        // The drop-onto-screen animation begins a bit later, but ends at the same time.
+        final int startDelay = TRANSLATE_ANIM_DURATION - DROP_ANIM_DURATION;
+
+        // Scale down the icon and fade out the alpha
+        animateDropOntoScreen(dragCopy, info, DROP_ANIM_DURATION, startDelay);
+    }
+
+    /**
+     * Animation which scales the view down and animates its alpha, making it appear to disappear
+     * onto a home screen.
+     */
+    private void animateDropOntoScreen(
+            final View view, final ItemInfo info, int duration, int delay) {
+        final DragLayer dragLayer = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
+        final CellLayout layout = mLauncher.getWorkspace().getCurrentDropLayout();
+
+        ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view,
+                PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.0f),
+                PropertyValuesHolder.ofFloat("scaleX", ANIMATION_SCALE),
+                PropertyValuesHolder.ofFloat("scaleY", ANIMATION_SCALE));
+        anim.setInterpolator(new LinearInterpolator());
+        if (delay > 0) {
+            anim.setStartDelay(delay);
+        }
+        anim.setDuration(duration);
+        anim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                dragLayer.removeView(view);
+                mLauncher.addExternalItemToScreen(info, layout);
+                info.dropPos = null;
             }
         });
+        anim.start();
+    }
+
+    /**
+     * Animates the x,y position of the view, and optionally execute a Runnable on animation end.
+     */
+    private void animateIntoPosition(
+            View view, float toX, float toY, final Runnable endRunnable) {
+        ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view,
+                PropertyValuesHolder.ofFloat("x", toX),
+                PropertyValuesHolder.ofFloat("y", toY));
+        anim.setInterpolator(new DecelerateInterpolator(2.5f));
+        anim.setDuration(TRANSLATE_ANIM_DURATION);
+        if (endRunnable != null) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    endRunnable.run();
+                }
+            });
+        }
+        anim.start();
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v instanceof PagedViewIcon) {
+            // Animate some feedback to the click
+            final ApplicationInfo appInfo = (ApplicationInfo) v.getTag();
+            animateClickFeedback(v, new Runnable() {
+                @Override
+                public void run() {
+                    mLauncher.startActivitySafely(appInfo.intent, appInfo);
+                }
+            });
+        } else if (v instanceof PagedViewWidget) {
+            Workspace w = mLauncher.getWorkspace();
+            int currentWorkspaceScreen = mLauncher.getCurrentWorkspaceScreen();
+            final CellLayout cl = (CellLayout) w.getChildAt(currentWorkspaceScreen);
+            final View dragView = v.findViewById(R.id.widget_preview);
+            final ItemInfo itemInfo = (ItemInfo) v.getTag();
+            animateClickFeedback(v, new Runnable() {
+                @Override
+                public void run() {
+                    cl.calculateSpans(itemInfo);
+                    if (cl.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY)) {
+                        if (LauncherApplication.isScreenXLarge()) {
+                            animateItemOntoScreen(dragView, cl, itemInfo);
+                        } else {
+                            mLauncher.addExternalItemToScreen(itemInfo, cl);
+                            itemInfo.dropPos = null;
+                        }
+
+                        // Hide the pane so we can see the workspace we dropped on
+                        mLauncher.showWorkspace(true);
+                    } else {
+                        mLauncher.showOutOfSpaceMessage();
+                    }
+                }
+            });
+        }
     }
 
     /*
@@ -136,10 +287,7 @@
     protected void determineDraggingStart(android.view.MotionEvent ev) {
         // Disable dragging by pulling an app down for now.
     }
-    @Override
-    protected boolean beginDragging(View v) {
-        if (!super.beginDragging(v)) return false;
-
+    private void beginDraggingApplication(View v) {
         // Make a copy of the ApplicationInfo
         ApplicationInfo appInfo = new ApplicationInfo((ApplicationInfo) v.getTag());
 
@@ -186,6 +334,39 @@
         mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b);
         mDragController.startDrag(v, b, this, appInfo, DragController.DRAG_ACTION_COPY, dragRect);
         b.recycle();
+    }
+    private void beginDraggingWidget(View v) {
+        // Get the widget preview as the drag representation
+        ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
+        PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) v.getTag();
+
+        // Compose the drag image
+        Drawable preview = image.getDrawable();
+        int w = preview.getIntrinsicWidth();
+        int h = preview.getIntrinsicHeight();
+        Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+        renderDrawableToBitmap(preview, b, 0, 0, w, h, 1, 1);
+
+        // Start the drag
+        int[] spanXY = CellLayout.rectToCell(getResources(),
+                createWidgetInfo.minWidth, createWidgetInfo.minHeight, null);
+        createWidgetInfo.spanX = spanXY[0];
+        createWidgetInfo.spanY = spanXY[1];
+        mLauncher.lockScreenOrientation();
+        mLauncher.getWorkspace().onDragStartedWithItemSpans(spanXY[0], spanXY[1], b);
+        mDragController.startDrag(image, b, this, createWidgetInfo,
+                DragController.DRAG_ACTION_COPY, null);
+        b.recycle();
+    }
+    @Override
+    protected boolean beginDragging(View v) {
+        if (!super.beginDragging(v)) return false;
+
+        if (v instanceof PagedViewIcon) {
+            beginDraggingApplication(v);
+        } else if (v instanceof PagedViewWidget) {
+            beginDraggingWidget(v);
+        }
 
         // Hide the pane so that the user can drop onto the workspace
         mLauncher.showWorkspace(true);
@@ -298,14 +479,86 @@
         d.setBounds(oldBounds); // Restore the bounds
         c.restore();
     }
-    private FastBitmapDrawable getWidgetPreview(AppWidgetProviderInfo info) {
-        // See CustomizePagedView.getWidgetPreview()
-        return null;
+    private FastBitmapDrawable getWidgetPreview(AppWidgetProviderInfo info, int cellHSpan,
+            int cellVSpan, int cellWidth, int cellHeight) {
+        // Calculate the size of the drawable
+        cellHSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellHSpan));
+        cellVSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellVSpan));
+        int expectedWidth = mWidgetSpacingLayout.estimateCellWidth(cellHSpan);
+        int expectedHeight = mWidgetSpacingLayout.estimateCellHeight(cellVSpan);
+
+        // Scale down the bitmap to fit the space
+        float widgetPreviewScale = (float) cellWidth / expectedWidth;
+        expectedWidth = (int) (widgetPreviewScale * expectedWidth);
+        expectedHeight = (int) (widgetPreviewScale * expectedHeight);
+
+        // Load the preview image if possible
+        String packageName = info.provider.getPackageName();
+        Drawable drawable = null;
+        FastBitmapDrawable newDrawable = null;
+        if (info.previewImage != 0) {
+            drawable = mPackageManager.getDrawable(packageName, info.previewImage, null);
+            if (drawable == null) {
+                Log.w(LOG_TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon)
+                        + " for provider: " + info.provider);
+            } else {
+                // Scale down the preview to the dimensions we want
+                int imageWidth = drawable.getIntrinsicWidth();
+                int imageHeight = drawable.getIntrinsicHeight();
+                float aspect = (float) imageWidth / imageHeight;
+                int newWidth = imageWidth;
+                int newHeight = imageHeight;
+                if (aspect > 1f) {
+                    newWidth = expectedWidth;
+                    newHeight = (int) (imageHeight * ((float) expectedWidth / imageWidth));
+                } else {
+                    newHeight = expectedHeight;
+                    newWidth = (int) (imageWidth * ((float) expectedHeight / imageHeight));
+                }
+
+                Bitmap preview = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
+                renderDrawableToBitmap(drawable, preview, 0, 0, newWidth, newHeight, 1f, 1f);
+                newDrawable = new FastBitmapDrawable(preview);
+                newDrawable.setBounds(0, 0, newWidth, newHeight);
+            }
+        }
+
+        // Generate a preview image if we couldn't load one
+        if (drawable == null) {
+            // The icon itself takes up space, so update expected width/height to have min of 2
+            cellHSpan = Math.max(2, cellHSpan);
+            cellVSpan = Math.max(2, cellVSpan);
+            expectedWidth = (int) (widgetPreviewScale
+                    * mWidgetSpacingLayout.estimateCellWidth(cellHSpan));
+            expectedHeight = (int) (widgetPreviewScale
+                    * mWidgetSpacingLayout.estimateCellHeight(cellVSpan));
+
+            Bitmap preview = Bitmap.createBitmap(expectedWidth, expectedHeight, Config.ARGB_8888);
+            Resources resources = mLauncher.getResources();
+            Drawable background = resources.getDrawable(R.drawable.default_widget_preview);
+            renderDrawableToBitmap(background, preview, 0, 0, expectedWidth, expectedHeight, 1f,1f);
+
+            // Draw the icon in the top left corner
+            try {
+                Drawable icon = null;
+                if (info.icon > 0) icon = mPackageManager.getDrawable(packageName, info.icon, null);
+                if (icon == null) icon = resources.getDrawable(R.drawable.ic_launcher_application);
+
+                int iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size);
+                int offset = iconSize / 4;
+                renderDrawableToBitmap(icon, preview, offset, offset, iconSize, iconSize, 1f, 1f);
+            } catch (Resources.NotFoundException e) {}
+
+            newDrawable = new FastBitmapDrawable(preview);
+            newDrawable.setBounds(0, 0, expectedWidth, expectedHeight);
+        }
+        return newDrawable;
     }
     public void syncWidgetPages() {
         // Ensure that we have the right number of pages
         Context context = getContext();
-        int numPages = mWidgets.size();
+        int numWidgetsPerPage = mWidgetCountX * mWidgetCountY;
+        int numPages = (int) Math.ceil(mWidgets.size() / (float) numWidgetsPerPage);
         for (int i = 0; i < numPages; ++i) {
             PagedViewExtendedLayout layout = new PagedViewExtendedLayout(context);
             setupPage(layout);
@@ -313,17 +566,43 @@
         }
     }
     public void syncWidgetPageItems(int page) {
+        Context context = getContext();
         PagedViewExtendedLayout layout = (PagedViewExtendedLayout) getChildAt(page);
-        layout.removeAllViewsOnPage();
-        for (int i = 0; i < 1; ++i) {
-            AppWidgetProviderInfo info = (AppWidgetProviderInfo) mWidgets.get(page);
-            FastBitmapDrawable icon = getWidgetPreview(info);
+        layout.removeAllViews();
 
-            ImageView image = new ImageView(getContext());
-            image.setBackgroundColor(0x99FF0000);
-            image.setImageDrawable(icon);
-            layout.addView(image, new PagedViewExtendedLayout.LayoutParams());
+        // Calculate the dimensions of each cell we are giving to each widget
+        FrameLayout container = new FrameLayout(context);
+        int numWidgetsPerPage = mWidgetCountX * mWidgetCountY;
+        int offset = page * numWidgetsPerPage;
+        int cellWidth = ((mWidgetSpacingLayout.getContentWidth() - mPageLayoutWidthGap
+                - ((mWidgetCountX - 1) * mWidgetCellWidthGap)) / mWidgetCountX);
+        int cellHeight = ((mWidgetSpacingLayout.getContentHeight() - mPageLayoutHeightGap
+                - ((mWidgetCountY - 1) * mWidgetCellHeightGap)) / mWidgetCountY);
+        for (int i = 0; i < Math.min(numWidgetsPerPage, mWidgets.size() - offset); ++i) {
+            AppWidgetProviderInfo info = (AppWidgetProviderInfo) mWidgets.get(offset + i);
+            PendingAddWidgetInfo createItemInfo = new PendingAddWidgetInfo(info, null, null);
+            final int[] cellSpans = CellLayout.rectToCell(getResources(), info.minWidth,
+                    info.minHeight, null);
+            FastBitmapDrawable preview = getWidgetPreview(info, cellSpans[0], cellSpans[1],
+                    cellWidth, cellHeight);
+            PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate(
+                    R.layout.apps_customize_widget, layout, false);
+            widget.applyFromAppWidgetProviderInfo(info, preview, -1, cellSpans, null, false);
+            widget.setTag(createItemInfo);
+            widget.setOnClickListener(this);
+            widget.setOnLongClickListener(this);
+            widget.setOnTouchListener(this);
+
+            // Layout each widget
+            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(cellWidth, cellHeight);
+            int ix = i % mWidgetCountX;
+            int iy = i / mWidgetCountX;
+            lp.leftMargin = (ix * cellWidth) + (ix * mWidgetCellWidthGap);
+            lp.topMargin = (iy * cellHeight) + (iy * mWidgetCellHeightGap);
+            container.addView(widget, lp);
         }
+        layout.addView(container, new LinearLayout.LayoutParams(
+            LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
     }
     @Override
     public void syncPages() {