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

Commit ef128115 authored by Vadim Tryshev's avatar Vadim Tryshev
Browse files

For pre-N apps, keep entered all parents of an drag-entered child.

The bug complains that parents of a view under the drag location
don’t get drag events.
This is first of a 2 CLs that will restore the old functionality
(modulus fixing bugs) for pre-N apps.

This CL restores pre-N "nested" model of the entered state for
pre-N apps. It also makes possible restoring "nested" model for
LOCATION and DROP (implemented in a follow-up CL)

The CL replaces (for pre-N) generation of ENTER/EXIT events that
happens at the moment of changing the drag focus with generation
folowing the recursive delivery of coordinate-bearing events.

Bug: 31559942
Change-Id: Iead6bde9c1f88819b30afc78c1f424f7c1b64d51
parent 1edc6daf
Loading
Loading
Loading
Loading
+27 −2
Original line number Diff line number Diff line
@@ -20856,6 +20856,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return false;
    }
    // Dispatches ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED events for pre-Nougat apps.
    boolean dispatchDragEnterExitInPreN(DragEvent event) {
        return callDragEventHandler(event);
    }
    /**
     * Detects if this View is enabled and has a drag event listener.
     * If both are true, then it calls the drag event listener with the
@@ -20884,13 +20889,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    final boolean callDragEventHandler(DragEvent event) {
        final boolean result;
        ListenerInfo li = mListenerInfo;
        //noinspection SimplifiableIfStatement
        if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnDragListener.onDrag(this, event)) {
            return true;
            result = true;
        } else {
            result = onDragEvent(event);
        }
        switch (event.mAction) {
            case DragEvent.ACTION_DRAG_ENTERED: {
                mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
                refreshDrawableState();
            } break;
            case DragEvent.ACTION_DRAG_EXITED: {
                mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
                refreshDrawableState();
            } break;
            case DragEvent.ACTION_DRAG_ENDED: {
                mPrivateFlags2 &= ~View.DRAG_MASK;
                refreshDrawableState();
            } break;
        }
        return onDragEvent(event);
        return result;
    }
    boolean canAcceptDrag() {
+51 −2
Original line number Diff line number Diff line
@@ -153,6 +153,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     */
    Transformation mInvalidationTransformation;

    // Current frontmost child that can accept drag and lies under the drag location.
    // Used only to generate ENTER/EXIT events for pre-Nougat aps.
    private View mCurrentDragChild;

    // Metadata about the ongoing drag
    private DragEvent mCurrentDragStartEvent;
    private boolean mIsInterestedInDrag;
@@ -1352,6 +1356,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return mLocalPoint;
    }

    @Override
    boolean dispatchDragEnterExitInPreN(DragEvent event) {
        if (event.mAction == DragEvent.ACTION_DRAG_EXITED && mCurrentDragChild != null) {
            // The drag exited a sub-tree of views; notify of the exit all descendants that are in
            // entered state.
            // We don't need this recursive delivery for ENTERED events because they get generated
            // from the recursive delivery of LOCATION/DROP events, and hence, don't need their own
            // recursion.
            mCurrentDragChild.dispatchDragEnterExitInPreN(event);
            mCurrentDragChild = null;
        }
        return mIsInterestedInDrag && super.dispatchDragEnterExitInPreN(event);
    }

    // TODO: Write real docs
    @Override
    public boolean dispatchDragEvent(DragEvent event) {
@@ -1364,6 +1382,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager

        switch (event.mAction) {
        case DragEvent.ACTION_DRAG_STARTED: {
            // Clear the state to recalculate which views we drag over.
            mCurrentDragChild = null;

            // Set up our tracking of drag-started notifications
            mCurrentDragStartEvent = DragEvent.obtain(event);
            if (mChildrenInterestedInDrag == null) {
@@ -1408,8 +1429,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                    if (child.dispatchDragEvent(event)) {
                        retval = true;
                    }
                    child.mPrivateFlags2 &= ~View.DRAG_MASK;
                    child.refreshDrawableState();
                }
                childrenInterestedInDrag.clear();
            }
@@ -1430,6 +1449,36 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        case DragEvent.ACTION_DROP: {
            // Find the [possibly new] drag target
            View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);

            if (target != mCurrentDragChild) {
                if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
                    // For pre-Nougat apps, make sure that the whole hierarchy of views that contain
                    // the drag location is kept in the state between ENTERED and EXITED events.
                    // (Starting with N, only the innermost view will be in that state).

                    final int action = event.mAction;
                    // Position should not be available for ACTION_DRAG_ENTERED and
                    // ACTION_DRAG_EXITED.
                    event.mX = 0;
                    event.mY = 0;

                    if (mCurrentDragChild != null) {
                        event.mAction = DragEvent.ACTION_DRAG_EXITED;
                        mCurrentDragChild.dispatchDragEnterExitInPreN(event);
                    }

                    if (target != null) {
                        event.mAction = DragEvent.ACTION_DRAG_ENTERED;
                        target.dispatchDragEnterExitInPreN(event);
                    }

                    event.mAction = action;
                    event.mX = tx;
                    event.mY = ty;
                }
                mCurrentDragChild = target;
            }

            if (target == null && mIsInterestedInDrag) {
                target = this;
            }
+6 −7
Original line number Diff line number Diff line
@@ -5524,6 +5524,9 @@ public final class ViewRootImpl implements ViewParent,
                // A direct EXITED event means that the window manager knows we've just crossed
                // a window boundary, so the current drag target within this one must have
                // just been exited. Send the EXITED notification to the current drag view, if any.
                if (mTargetSdkVersion < Build.VERSION_CODES.N) {
                    mView.dispatchDragEnterExitInPreN(event);
                }
                setDragFocus(null, event);
            } else {
                // For events with a [screen] location, translate into window coordinates
@@ -5641,7 +5644,7 @@ public final class ViewRootImpl implements ViewParent,
    }

    public void setDragFocus(View newDragTarget, DragEvent event) {
        if (mCurrentDragView != newDragTarget) {
        if (mCurrentDragView != newDragTarget && mTargetSdkVersion >= Build.VERSION_CODES.N) {
            // Send EXITED and ENTERED notifications to the old and new drag focus views.

            final float tx = event.mX;
@@ -5654,23 +5657,19 @@ public final class ViewRootImpl implements ViewParent,
            if (mCurrentDragView != null) {
                event.mAction = DragEvent.ACTION_DRAG_EXITED;
                mCurrentDragView.callDragEventHandler(event);
                mCurrentDragView.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
                mCurrentDragView.refreshDrawableState();
            }

            mCurrentDragView = newDragTarget;

            if (newDragTarget != null) {
                event.mAction = DragEvent.ACTION_DRAG_ENTERED;
                newDragTarget.callDragEventHandler(event);
                newDragTarget.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
                newDragTarget.refreshDrawableState();
            }

            event.mAction = action;
            event.mX = tx;
            event.mY = ty;
        }

        mCurrentDragView = newDragTarget;
    }

    private AudioManager getAudioManager() {