Implement button to get application info for an app shortcut.

For now, it's just a drag target like the delete zone. Once all apps
and the home screen support a selection mode, this (and delete) will
be implemented as buttons in the Contextual Action Bar.

Change-Id: I6bf43d03eefda672ea34c583a7021137da22b184
diff --git a/res/drawable-xlarge/info_button.png b/res/drawable-xlarge/info_button.png
new file mode 100644
index 0000000..5080f6a
--- /dev/null
+++ b/res/drawable-xlarge/info_button.png
Binary files differ
diff --git a/res/layout-xlarge/launcher.xml b/res/layout-xlarge/launcher.xml
index 8133853..4785b0c 100644
--- a/res/layout-xlarge/launcher.xml
+++ b/res/layout-xlarge/launcher.xml
@@ -63,34 +63,52 @@
         android:layout_gravity="top|right">
 
         <ImageView
-            android:id="@+id/configure_button"
+            android:id="@+id/all_apps_button"
+            android:src="@drawable/all_apps_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+
+            android:onClick="onClickAllAppsButton"
+            android:focusable="true"
+            android:clickable="true" />
+
+        <ImageView
+            android:id="@+id/configure_button"
             android:src="@drawable/configure_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@id/all_apps_button"
 
             android:onClick="onClickConfigureButton"
             android:focusable="true"
             android:clickable="true" />
+
         <com.android.launcher2.DeleteZone
             android:id="@+id/delete_zone"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/all_apps_button"
+            android:layout_alignTop="@id/all_apps_button"
             android:paddingTop="@dimen/delete_zone_padding"
 
             android:scaleType="center"
             android:src="@drawable/delete_zone_selector"
             android:visibility="gone"
             launcher:direction="horizontal" />
-        <ImageView
-            android:id="@+id/all_apps_button"
+
+        <com.android.launcher2.ApplicationInfoDropTarget
+            android:id="@+id/info_button"
+            android:src="@drawable/info_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_toRightOf="@id/configure_button"
-            android:src="@drawable/all_apps_button"
+            android:layout_alignLeft="@id/configure_button"
+            android:layout_centerVertical="true"
+            android:visibility="gone"
 
-            android:onClick="onClickAllAppsButton"
             android:focusable="true"
             android:clickable="true" />
+
     </RelativeLayout>
 
     <TabHost
diff --git a/res/values-xlarge/colors.xml b/res/values-xlarge/colors.xml
new file mode 100644
index 0000000..a6cdd06
--- /dev/null
+++ b/res/values-xlarge/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<resources>
+    <color name="app_info_filter">#A50000FE</color>
+</resources>
\ No newline at end of file
diff --git a/src/com/android/launcher2/ApplicationInfoDropTarget.java b/src/com/android/launcher2/ApplicationInfoDropTarget.java
new file mode 100644
index 0000000..737d198
--- /dev/null
+++ b/src/com/android/launcher2/ApplicationInfoDropTarget.java
@@ -0,0 +1,143 @@
+/*
+ * 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.launcher2;
+
+import android.content.Context;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+/**
+ * Implements a DropTarget which allows applications to be dropped on it,
+ * in order to launch the application info for that app.
+ */
+public class ApplicationInfoDropTarget extends ImageView implements DropTarget, DragController.DragListener {
+    private Launcher mLauncher;
+    private DragController mDragController;
+    private boolean mActive = false;
+
+    private View mHandle;
+
+    private final Paint mPaint = new Paint();
+
+    public ApplicationInfoDropTarget(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ApplicationInfoDropTarget(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+    }
+
+    /**
+     * Set the color that will be used as a filter over objects dragged over this object.
+     */
+    public void setDragColor(int color) {
+        mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+    }
+
+    public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+
+        // acceptDrop is called just before onDrop. We do the work here, rather than
+        // in onDrop, because it allows us to reject the drop (by returning false)
+        // so that the object being dragged isn't removed from the home screen.
+
+        String packageName = null;
+        if (dragInfo instanceof ApplicationInfo) {
+            packageName = ((ApplicationInfo)dragInfo).componentName.getPackageName();
+        } else if (dragInfo instanceof ShortcutInfo) {
+            packageName = ((ShortcutInfo)dragInfo).intent.getComponent().getPackageName();
+        }
+        mLauncher.startApplicationDetailsActivity(packageName);
+        return false;
+    }
+
+    public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo, Rect recycle) {
+        return null;
+    }
+
+    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+
+    }
+
+    public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        dragView.setPaint(mPaint);
+    }
+
+    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+    }
+
+    public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        // TODO: Animate out
+        dragView.setPaint(null);
+    }
+
+    public void onDragStart(DragSource source, Object info, int dragAction) {
+        if (info != null) {
+            mActive = true;
+
+            // TODO: Animate these in and out
+
+            // Only show the info icon when an application is selected
+            if (((ItemInfo)info).itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                setVisibility(VISIBLE);
+            }
+            mHandle.setVisibility(INVISIBLE);
+        }
+    }
+
+    public void onDragEnd() {
+        if (mActive) {
+            mActive = false;
+            // TODO: Animate these in and out
+            setVisibility(GONE);
+            mHandle.setVisibility(VISIBLE);
+        }
+    }
+
+    void setLauncher(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    void setDragController(DragController dragController) {
+        mDragController = dragController;
+    }
+
+    void setHandle(View view) {
+        mHandle = view;
+    }
+
+    @Override
+    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+        return null;
+    }
+}
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 0130ba9..651b6f0 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -99,7 +99,7 @@
     /** Who can receive drop events */
     private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
 
-    private DragListener mListener;
+    private ArrayList<DragListener> mListeners = new ArrayList<DragListener>();
 
     /** The window token used as the parent for the DragView. */
     private IBinder mWindowToken;
@@ -213,8 +213,8 @@
         }
         mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
 
-        if (mListener != null) {
-            mListener.onDragStart(source, dragInfo, dragAction);
+        for (DragListener listener : mListeners) {
+            listener.onDragStart(source, dragInfo, dragAction);
         }
 
         int registrationX = ((int)mMotionDownX) - screenX;
@@ -297,8 +297,8 @@
             if (mOriginator != null) {
                 mOriginator.setVisibility(View.VISIBLE);
             }
-            if (mListener != null) {
-                mListener.onDragEnd();
+            for (DragListener listener : mListeners) {
+                listener.onDragEnd();
             }
             if (mDragView != null) {
                 mDragView.remove();
@@ -547,15 +547,15 @@
     /**
      * Sets the drag listner which will be notified when a drag starts or ends.
      */
-    public void setDragListener(DragListener l) {
-        mListener = l;
+    public void addDragListener(DragListener l) {
+        mListeners.add(l);
     }
 
     /**
      * Remove a previously installed drag listener.
      */
     public void removeDragListener(DragListener l) {
-        mListener = null;
+        mListeners.remove(l);
     }
 
     /**
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index 7e54231..e33ec9a 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -72,8 +72,7 @@
 
     /**
      * Check if a drop action can occur at, or near, the requested location.
-     * This may be called repeatedly during a drag, so any calls should return
-     * quickly.
+     * This will be called just before onDrop.
      * 
      * @param source DragSource where the drag started
      * @param x X coordinate of the drop location
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index b5d0dbb..ebe9baa 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher2;
 
+import com.android.common.Search;
+import com.android.launcher.R;
+
 import android.animation.Animatable;
 import android.animation.AnimatableListenerAdapter;
 import android.animation.Animator;
@@ -58,6 +61,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.LiveFolders;
+import android.provider.Settings;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -89,8 +93,14 @@
 import android.widget.TabWidget;
 import android.widget.TextView;
 import android.widget.Toast;
-import com.android.common.Search;
-import com.android.launcher.R;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -875,20 +885,31 @@
         deleteZone.setDragController(dragController);
         int deleteZoneHandleId;
         if (LauncherApplication.isScreenXLarge()) {
-            deleteZoneHandleId = R.id.configure_button;
+            deleteZoneHandleId = R.id.all_apps_button;
         } else {
             deleteZoneHandleId = R.id.all_apps_button_cluster;
         }
         deleteZone.setHandle(findViewById(deleteZoneHandleId));
+        dragController.addDragListener(deleteZone);
+
+        ApplicationInfoDropTarget infoButton = (ApplicationInfoDropTarget)findViewById(R.id.info_button);
+        if (infoButton != null) {
+            infoButton.setLauncher(this);
+            infoButton.setHandle(findViewById(R.id.configure_button));
+            infoButton.setDragColor(getResources().getColor(R.color.app_info_filter));
+            dragController.addDragListener(infoButton);
+        }
 
         dragController.setDragScoller(workspace);
-        dragController.setDragListener(deleteZone);
         dragController.setScrollView(dragLayer);
         dragController.setMoveTarget(workspace);
 
         // The order here is bottom to top.
         dragController.addDropTarget(workspace);
         dragController.addDropTarget(deleteZone);
+        if (infoButton != null) {
+            dragController.addDropTarget(infoButton);
+        }
     }
 
     @SuppressWarnings({"UnusedDeclaration"})
@@ -1686,6 +1707,12 @@
         showAllApps(true);
     }
 
+    void startApplicationDetailsActivity(String packageName) {
+        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+                Uri.fromParts("package", packageName, null));
+        startActivity(intent);
+    }
+
     void startActivitySafely(Intent intent, Object tag) {
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {