Initial changes to merged AllApps/Customize pane in the Phone UI.

Change-Id: Ifa1676736033fe617dd0cca965156c38fa452948
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
new file mode 100644
index 0000000..6dc34ca
--- /dev/null
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -0,0 +1,465 @@
+/*
+ * 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.launcher2;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+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.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+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.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.launcher.R;
+
+public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
+        AllAppsView, View.OnClickListener, DragSource {
+    static final String LOG_TAG = "AppsCustomizePagedView";
+
+    /**
+     * The different content types that this paged view can show.
+     */
+    public enum ContentType {
+        Applications,
+        Widgets
+    }
+
+    // Refs
+    private Launcher mLauncher;
+    private DragController mDragController;
+    private final LayoutInflater mLayoutInflater;
+    private final PackageManager mPackageManager;
+
+    // Content
+    private ContentType mContentType;
+    private ArrayList<ApplicationInfo> mApps;
+    private List<AppWidgetProviderInfo> mWidgets;
+    private List<ResolveInfo> mShortcuts;
+
+    // Dimens
+    private int mContentWidth;
+    private float mWidgetScale;
+    private PagedViewCellLayout mWidgetSpacingLayout;
+
+    public AppsCustomizePagedView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mLayoutInflater = LayoutInflater.from(context);
+        mPackageManager = context.getPackageManager();
+        mContentType = ContentType.Applications;
+        mApps = new ArrayList<ApplicationInfo>();
+
+        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();
+
+        // 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
+        mWidgetSpacingLayout = new PagedViewCellLayout(context);
+        mWidgetSpacingLayout.setCellCount(mCellCountX, mCellCountY);
+    }
+
+    @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 onPackagesUpdated() {
+        // Get the list of widgets
+        mWidgets = AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
+        Collections.sort(mWidgets, LauncherModel.WIDGET_NAME_COMPARATOR);
+
+        // Get the list of shortcuts
+        Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+        mShortcuts = mPackageManager.queryIntentActivities(shortcutsIntent, 0);
+        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);
+            }
+        });
+    }
+
+    /*
+     * PagedViewWithDraggableItems implementation
+     */
+    @Override
+    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;
+
+        // Make a copy of the ApplicationInfo
+        ApplicationInfo appInfo = new ApplicationInfo((ApplicationInfo) v.getTag());
+
+        // Show the uninstall button if the app is uninstallable.
+        if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) != 0) {
+            DeleteZone allAppsDeleteZone = (DeleteZone)
+                    mLauncher.findViewById(R.id.all_apps_delete_zone);
+            allAppsDeleteZone.setDragAndDropEnabled(true);
+
+            if ((appInfo.flags & ApplicationInfo.UPDATED_SYSTEM_APP_FLAG) != 0) {
+                allAppsDeleteZone.setText(R.string.delete_zone_label_all_apps_system_app);
+            } else {
+                allAppsDeleteZone.setText(R.string.delete_zone_label_all_apps);
+            }
+        }
+
+        // Show the info button
+        ApplicationInfoDropTarget allAppsInfoButton =
+            (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.all_apps_info_target);
+        allAppsInfoButton.setDragAndDropEnabled(true);
+
+        // Compose the drag image (top compound drawable, index is 1)
+        final TextView tv = (TextView) v;
+        final Drawable icon = tv.getCompoundDrawables()[1];
+        Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
+                Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(b);
+        c.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
+        icon.draw(c);
+
+        // Compose the visible rect of the drag image
+        Rect dragRect = null;
+        if (v instanceof TextView) {
+            int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+            int top = v.getPaddingTop();
+            int left = (b.getWidth() - iconSize) / 2;
+            int right = left + iconSize;
+            int bottom = top + iconSize;
+            dragRect = new Rect(left, top, right, bottom);
+        }
+
+        // Start the drag
+        mLauncher.lockScreenOrientation();
+        mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b);
+        mDragController.startDrag(v, b, this, appInfo, DragController.DRAG_ACTION_COPY, dragRect);
+        b.recycle();
+
+        // Hide the pane so that the user can drop onto the workspace
+        mLauncher.showWorkspace(true);
+        return true;
+    }
+    private void endDragging(boolean success) {
+        post(new Runnable() {
+            // Once the drag operation has fully completed, hence the post, we want to disable the
+            // deleteZone and the appInfoButton in all apps, and re-enable the instance which
+            // live in the workspace
+            public void run() {
+                // if onDestroy was called on Launcher, we might have already deleted the
+                // all apps delete zone / info button, so check if they are null
+                DeleteZone allAppsDeleteZone =
+                        (DeleteZone) mLauncher.findViewById(R.id.all_apps_delete_zone);
+                ApplicationInfoDropTarget allAppsInfoButton =
+                    (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.all_apps_info_target);
+
+                if (allAppsDeleteZone != null) allAppsDeleteZone.setDragAndDropEnabled(false);
+                if (allAppsInfoButton != null) allAppsInfoButton.setDragAndDropEnabled(false);
+            }
+        });
+        mLauncher.getWorkspace().onDragStopped(success);
+        mLauncher.unlockScreenOrientation();
+    }
+
+    /*
+     * DragSource implementation
+     */
+    @Override
+    public void onDragViewVisible() {}
+    @Override
+    public void onDropCompleted(View target, Object dragInfo, boolean success) {
+        endDragging(success);
+    }
+
+    public void setContentType(ContentType type) {
+        mContentType = type;
+        setCurrentPage(0);
+        invalidatePageData();
+    }
+
+    /*
+     * Apps PagedView implementation
+     */
+    private void setupPage(PagedViewCellLayout layout) {
+        layout.setCellCount(mCellCountX, mCellCountY);
+        layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap);
+        layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
+                mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
+
+        // 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(getMeasuredWidth(), MeasureSpec.AT_MOST);
+        int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST);
+        layout.measure(widthSpec, heightSpec);
+    }
+    public void syncAppsPages() {
+        // Ensure that we have the right number of pages
+        Context context = getContext();
+        int numPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY));
+        for (int i = 0; i < numPages; ++i) {
+            PagedViewCellLayout layout = new PagedViewCellLayout(context);
+            setupPage(layout);
+            addView(layout);
+        }
+    }
+    public void syncAppsPageItems(int page) {
+        // ensure that we have the right number of items on the pages
+        int numPages = getPageCount();
+        int numCells = mCellCountX * mCellCountY;
+        int startIndex = page * numCells;
+        int endIndex = Math.min(startIndex + numCells, mApps.size());
+        PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+        layout.removeAllViewsOnPage();
+        for (int i = startIndex; i < endIndex; ++i) {
+            ApplicationInfo info = mApps.get(i);
+            PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
+                    R.layout.apps_customize_application, layout, false);
+            icon.applyFromApplicationInfo(info, mPageViewIconCache, true, (numPages > 1));
+            icon.setOnClickListener(this);
+            icon.setOnLongClickListener(this);
+            icon.setOnTouchListener(this);
+
+            int index = i - startIndex;
+            int x = index % mCellCountX;
+            int y = index / mCellCountX;
+            setupPage(layout);
+            layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
+        }
+    }
+    /*
+     * Widgets PagedView implementation
+     */
+    private void setupPage(PagedViewExtendedLayout layout) {
+        layout.setGravity(Gravity.LEFT);
+        layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
+                mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
+        layout.setMinimumWidth(getPageContentWidth());
+    }
+    private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h,
+            float scaleX, float scaleY) {
+        Canvas c = new Canvas();
+        if (bitmap != null) c.setBitmap(bitmap);
+        c.save();
+        c.scale(scaleX, scaleY);
+        Rect oldBounds = d.copyBounds();
+        d.setBounds(x, y, x + w, y + h);
+        d.draw(c);
+        d.setBounds(oldBounds); // Restore the bounds
+        c.restore();
+    }
+    private FastBitmapDrawable getWidgetPreview(AppWidgetProviderInfo info) {
+        // See CustomizePagedView.getWidgetPreview()
+        return null;
+    }
+    public void syncWidgetPages() {
+        // Ensure that we have the right number of pages
+        Context context = getContext();
+        int numPages = mWidgets.size();
+        for (int i = 0; i < numPages; ++i) {
+            PagedViewExtendedLayout layout = new PagedViewExtendedLayout(context);
+            setupPage(layout);
+            addView(layout);
+        }
+    }
+    public void syncWidgetPageItems(int page) {
+        PagedViewExtendedLayout layout = (PagedViewExtendedLayout) getChildAt(page);
+        layout.removeAllViewsOnPage();
+        for (int i = 0; i < 1; ++i) {
+            AppWidgetProviderInfo info = (AppWidgetProviderInfo) mWidgets.get(page);
+            FastBitmapDrawable icon = getWidgetPreview(info);
+
+            ImageView image = new ImageView(getContext());
+            image.setBackgroundColor(0x99FF0000);
+            image.setImageDrawable(icon);
+            layout.addView(image, new PagedViewExtendedLayout.LayoutParams());
+        }
+    }
+    @Override
+    public void syncPages() {
+        removeAllViews();
+        switch (mContentType) {
+        case Applications:
+            syncAppsPages();
+            break;
+        case Widgets:
+            syncWidgetPages();
+            break;
+        }
+    }
+    @Override
+    public void syncPageItems(int page) {
+        switch (mContentType) {
+        case Applications:
+            syncAppsPageItems(page);
+            break;
+        case Widgets:
+            syncWidgetPageItems(page);
+            break;
+        }
+    }
+
+    /**
+     * Used by the parent to get the content width to set the tab bar to
+     * @return
+     */
+    public int getPageContentWidth() {
+        return mContentWidth;
+    }
+
+    /*
+     * AllAppsView implementation
+     */
+    @Override
+    public void setup(Launcher launcher, DragController dragController) {
+        mLauncher = launcher;
+        mDragController = dragController;
+    }
+    @Override
+    public void zoom(float zoom, boolean animate) {
+        // TODO-APPS_CUSTOMIZE: Call back to mLauncher.zoomed()
+    }
+    @Override
+    public boolean isVisible() {
+        return (getVisibility() == VISIBLE);
+    }
+    @Override
+    public boolean isAnimating() {
+        return false;
+    }
+    @Override
+    public void setApps(ArrayList<ApplicationInfo> list) {
+        mApps = list;
+        Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
+        invalidatePageData();
+    }
+    private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+        // We add it in place, in alphabetical order
+        int count = list.size();
+        for (int i = 0; i < count; ++i) {
+            ApplicationInfo info = list.get(i);
+            int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR);
+            if (index < 0) {
+                mApps.add(-(index + 1), info);
+            }
+        }
+    }
+    @Override
+    public void addApps(ArrayList<ApplicationInfo> list) {
+        addAppsWithoutInvalidate(list);
+        invalidatePageData();
+    }
+    private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) {
+        ComponentName removeComponent = item.intent.getComponent();
+        int length = list.size();
+        for (int i = 0; i < length; ++i) {
+            ApplicationInfo info = list.get(i);
+            if (info.intent.getComponent().equals(removeComponent)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+        // loop through all the apps and remove apps that have the same component
+        int length = list.size();
+        for (int i = 0; i < length; ++i) {
+            ApplicationInfo info = list.get(i);
+            int removeIndex = findAppByComponent(mApps, info);
+            if (removeIndex > -1) {
+                mApps.remove(removeIndex);
+                mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info));
+            }
+        }
+    }
+    @Override
+    public void removeApps(ArrayList<ApplicationInfo> list) {
+        removeAppsWithoutInvalidate(list);
+        invalidatePageData();
+    }
+    @Override
+    public void updateApps(ArrayList<ApplicationInfo> list) {
+        // We remove and re-add the updated applications list because it's properties may have
+        // changed (ie. the title), and this will ensure that the items will be in their proper
+        // place in the list.
+        removeAppsWithoutInvalidate(list);
+        addAppsWithoutInvalidate(list);
+        invalidatePageData();
+    }
+    @Override
+    public void reset() {
+        setCurrentPage(0);
+        invalidatePageData();
+    }
+    @Override
+    public void dumpState() {
+        // TODO: Dump information related to current list of Applications, Widgets, etc.
+        ApplicationInfo.dumpApplicationInfoList(LOG_TAG, "mApps", mApps);
+        dumpAppWidgetProviderInfoList(LOG_TAG, "mWidgets", mWidgets);
+    }
+    private void dumpAppWidgetProviderInfoList(String tag, String label,
+            List<AppWidgetProviderInfo> list) {
+        Log.d(tag, label + " size=" + list.size());
+        for (AppWidgetProviderInfo info: list) {
+            Log.d(tag, "   label=\"" + info.label + "\" previewImage=" + info.previewImage
+                    + " resizeMode=" + info.resizeMode + " configure=" + info.configure
+                    + " initialLayout=" + info.initialLayout
+                    + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight);
+        }
+    }
+    @Override
+    public void surrender() {
+        // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we
+        // should stop this now.
+    }
+}