summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/View.java29
-rw-r--r--core/java/android/view/ViewGroup.java53
-rw-r--r--core/java/android/view/ViewRootImpl.java13
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() {