More drag/drop adjustments:

* Make View.onDragEvent() public instead of protected.
* No longer @hide View.startDrag()
* Properly manage the boundaries of DRAG_STARTED / DRAG_ENDED notifications
  to windows (and as a result don't send bogus empty DRAG_STARTED events or
  double-recycle pooled DragEvents)

Change-Id: Ib75f5c1417640c82a5b043c555e02d6e6f4b4d9c
diff --git a/api/current.xml b/api/current.xml
index 08e8670..a866669 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -201443,7 +201443,7 @@
  static="false"
  final="false"
  deprecated="not deprecated"
- visibility="protected"
+ visibility="public"
 >
 <parameter name="event" type="android.view.DragEvent">
 </parameter>
@@ -203185,6 +203185,23 @@
 <parameter name="animation" type="android.view.animation.Animation">
 </parameter>
 </method>
+<method name="startDrag"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.content.ClipData">
+</parameter>
+<parameter name="thumbBuilder" type="android.view.View.DragThumbnailBuilder">
+</parameter>
+<parameter name="myWindowOnly" type="boolean">
+</parameter>
+</method>
 <method name="unscheduleDrawable"
  return="void"
  abstract="false"
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 35b806a..472f7b4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9898,11 +9898,10 @@
     }
 
     /**
-     * Drag and drop.  App calls startDrag(), then callbacks to onMeasureDragThumbnail()
-     * and onDrawDragThumbnail() happen, then the drag operation is handed over to the
-     * OS.
+     * Drag and drop.  App calls startDrag(), then callbacks to the thumbnail builder's
+     * onProvideThumbnailMetrics() and onDrawThumbnail() methods happen, then the drag
+     * operation is handed over to the OS.
      * !!! TODO: real docs
-     * @hide
      */
     public final boolean startDrag(ClipData data, DragThumbnailBuilder thumbBuilder,
             boolean myWindowOnly) {
@@ -10027,7 +10026,7 @@
      * For DRAG_ENDED_EVENT, the 'event' argument may be null.  The view should return
      * to its normal visual state.
      */
-    protected boolean onDragEvent(DragEvent event) {
+    public boolean onDragEvent(DragEvent event) {
         return false;
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5ebc981..5b3a091 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -938,11 +938,16 @@
             if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
             View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
             if (target != null) {
+                if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "   dispatch drop to " + target);
                 event.mX = mLocalPoint.x;
                 event.mY = mLocalPoint.y;
                 retval = target.dispatchDragEvent(event);
                 event.mX = tx;
                 event.mY = ty;
+            } else {
+                if (ViewDebug.DEBUG_DRAG) {
+                    Log.d(View.VIEW_LOG_TAG, "   not dropped on an accepting view");
+                }
             }
         } break;
         }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 30aed69..59f7434 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -505,7 +505,7 @@
         InputChannel mServerChannel, mClientChannel;
         WindowState mTargetWindow;
         ArrayList<WindowState> mNotifiedWindows;
-        boolean mDragEnded;
+        boolean mDragInProgress;
 
         private final Rect tmpRect = new Rect();
 
@@ -562,6 +562,7 @@
             // works correctly in calling out to the apps.
             mDataDescription = new ClipDescription(mData);
             mNotifiedWindows.clear();
+            mDragInProgress = true;
 
             if (DEBUG_DRAG) {
                 Slog.d(TAG, "broadcasting DRAG_STARTED of " + mDataDescription);
@@ -586,7 +587,7 @@
          * process, so it's safe for the caller to call recycle() on the event afterwards.
          */
         private void sendDragStartedLw(WindowState newWin, DragEvent event) {
-            if (!mDragEnded && newWin.isPotentialDragTarget()) {
+            if (mDragInProgress && newWin.isPotentialDragTarget()) {
                 try {
                     // clone for local callees since dispatch will recycle the event
                     if (Process.myPid() == newWin.mSession.mPid) {
@@ -606,20 +607,22 @@
          * was begun.  This is a rare case.
          */
         private void sendDragStartedIfNeededLw(WindowState newWin) {
-            // If we have sent the drag-started, we needn't do so again
-            for (WindowState ws : mNotifiedWindows) {
-                if (ws == newWin) {
-                    return;
+            if (mDragInProgress) {
+                // If we have sent the drag-started, we needn't do so again
+                for (WindowState ws : mNotifiedWindows) {
+                    if (ws == newWin) {
+                        return;
+                    }
                 }
+                if (DEBUG_DRAG) {
+                    Slog.d(TAG, "sending DRAG_STARTED to new window " + newWin);
+                }
+                DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
+                        mDataDescription, null);
+                // sendDragStartedLw() clones 'event' if the window is process-local
+                sendDragStartedLw(newWin, event);
+                event.recycle();
             }
-            if (DEBUG_DRAG) {
-                Slog.d(TAG, "sending DRAG_STARTED to new window " + newWin);
-            }
-            DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
-                    mDataDescription, null);
-            // sendDragStartedLw() clones 'event' if the window is process-local
-            sendDragStartedLw(newWin, event);
-            event.recycle();
         }
 
         void broadcastDragEnded() {
@@ -636,7 +639,7 @@
                     }
                 }
                 mNotifiedWindows.clear();
-                mDragEnded = true;
+                mDragInProgress = false;
             }
             evt.recycle();
         }