diff options
| -rw-r--r-- | core/java/android/view/View.java | 29 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 53 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 13 |
3 files changed, 84 insertions, 11 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 60a8b146e234..dd5915e34554 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -20858,6 +20858,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 @@ -20886,13 +20891,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() { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 987db0ac58ed..7111f28f5741 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -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; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8abaa14810e1..0f161cdbc900 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -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() { |