Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d1ab883 authored by Chris Tate's avatar Chris Tate
Browse files

Fix drag enter/exit reporting

Now, each ViewGroup is tracking which of its child views [which might
themselves be ViewGroups] is currently under the drag point, and when the
drag leaves that child, a DRAG_EXITED is synthesized and dispatched all
the way down to the leaf view previously under the point.  ENTERED is
still *not* dispatched down like this; instead, it's calculated and
synthesized directly at each level based on the new LOCATION.

The ViewRoot still tracks the leaf drag target, but solely for the
purpose of reporting changes to the OS after full dispatch of a new
LOCATION -- the entered/exited messaging is no longer initiated at the
ViewRoot level.

Change-Id: I0089cc538b7e33a0440187543fcfd2f8b12e197d
parent 4b8d36b3
Loading
Loading
Loading
Loading
+40 −4
Original line number Diff line number Diff line
@@ -870,7 +870,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        switch (event.mAction) {
        case DragEvent.ACTION_DRAG_STARTED: {
            // clear state to recalculate which views we drag over
            root.setDragFocus(event, null);
            mCurrentDragView = null;

            // Now dispatch down to our children, caching the responses
            mChildAcceptsDrag = false;
@@ -915,11 +915,28 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);

            // If we've changed apparent drag target, tell the view root which view
            // we're over now.  This will in turn send out DRAG_ENTERED / DRAG_EXITED
            // notifications as appropriate.
            // we're over now [for purposes of the eventual drag-recipient-changed
            // notifications to the framework] and tell the new target that the drag
            // has entered its bounds.  The root will see setDragFocus() calls all
            // the way down to the final leaf view that is handling the LOCATION event
            // before reporting the new potential recipient to the framework.
            if (mCurrentDragView != target) {
                root.setDragFocus(event, target);
                root.setDragFocus(target);

                final int action = event.mAction;
                // If we've dragged off of a child view, send it the EXITED message
                if (mCurrentDragView != null) {
                    event.mAction = DragEvent.ACTION_DRAG_EXITED;
                    mCurrentDragView.dispatchDragEvent(event);
                }
                mCurrentDragView = target;

                // If we've dragged over a new child view, send it the ENTERED message
                if (target != null) {
                    event.mAction = DragEvent.ACTION_DRAG_ENTERED;
                    target.dispatchDragEvent(event);
                }
                event.mAction = action;  // restore the event's original state
            }

            // Dispatch the actual drag location notice, localized into its coordinates
@@ -934,6 +951,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            }
        } break;

        /* Entered / exited dispatch
         *
         * DRAG_ENTERED is not dispatched downwards from ViewGroup.  The reason for this is
         * that we're about to get the corresponding LOCATION event, which we will use to
         * determine which of our children is the new target; at that point we will
         * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
         *
         * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
         * drag has left this ViewGroup, we know by definition that every contained subview
         * is also no longer under the drag point.
         */

        case DragEvent.ACTION_DRAG_EXITED: {
            if (mCurrentDragView != null) {
                mCurrentDragView.dispatchDragEvent(event);
                mCurrentDragView = null;
            }
        } break;

        case DragEvent.ACTION_DROP: {
            if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
            View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+3 −14
Original line number Diff line number Diff line
@@ -2493,11 +2493,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
                // a window boundary, so the current drag target within this one must have
                // just been exited.  Send it the usual notifications and then we're done
                // for now.
                setDragFocus(event, null);
                mView.dispatchDragEvent(event);
            } else {
                // Cache the drag description when the operation starts, then fill it in
                // on subsequent calls as a convenience
                if (what == DragEvent.ACTION_DRAG_STARTED) {
                    mCurrentDragView = null;    // Start the current-recipient tracking
                    mDragDescription = event.mClipDescription;
                } else {
                    event.mClipDescription = mDragDescription;
@@ -2557,22 +2558,10 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
        outLocation.y = (int) mLastTouchPoint.y;
    }

    public void setDragFocus(DragEvent event, View newDragTarget) {
        final int action = event.mAction;
        // If we've dragged off of a view, send it the EXITED message
    public void setDragFocus(View newDragTarget) {
        if (mCurrentDragView != newDragTarget) {
            if (mCurrentDragView != null) {
                event.mAction = DragEvent.ACTION_DRAG_EXITED;
                mCurrentDragView.dispatchDragEvent(event);
            }
            mCurrentDragView = newDragTarget;
        }
        // If we've dragged over a new view, send it the ENTERED message
        if (newDragTarget != null) {
            event.mAction = DragEvent.ACTION_DRAG_ENTERED;
            newDragTarget.dispatchDragEvent(event);
        }
        event.mAction = action;  // restore the event's original state
    }

    private AudioManager getAudioManager() {
+1 −1
Original line number Diff line number Diff line
@@ -687,7 +687,7 @@ public class WindowManagerService extends IWindowManager.Stub
                    }
                }
                if (touchedWin != null) {
                    if (DEBUG_DRAG) {
                    if (false && DEBUG_DRAG) {
                        Slog.d(TAG, "sending DRAG_LOCATION to " + touchedWin);
                    }
                    DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,