summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xapi/current.txt1
-rw-r--r--core/java/android/view/TouchDelegate.java61
-rw-r--r--core/java/android/view/View.java22
-rw-r--r--core/java/android/view/ViewGroup.java10
4 files changed, 86 insertions, 8 deletions
diff --git a/api/current.txt b/api/current.txt
index ead6157b58d7..3c6e8ffeb452 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -48355,6 +48355,7 @@ package android.view {
ctor public TouchDelegate(android.graphics.Rect, android.view.View);
method public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo();
method public boolean onTouchEvent(android.view.MotionEvent);
+ method public boolean onTouchExplorationHoverEvent(android.view.MotionEvent);
field public static final int ABOVE = 1; // 0x1
field public static final int BELOW = 2; // 0x2
field public static final int TO_LEFT = 4; // 0x4
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index 6fb32e36fb3f..bef9f07afb5f 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -103,13 +103,13 @@ public class TouchDelegate {
}
/**
- * Will forward touch events to the delegate view if the event is within the bounds
+ * Forward touch events to the delegate view if the event is within the bounds
* specified in the constructor.
*
* @param event The touch event to forward
- * @return True if the event was forwarded to the delegate, false otherwise.
+ * @return True if the event was consumed by the delegate, false otherwise.
*/
- public boolean onTouchEvent(MotionEvent event) {
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
boolean sendToDelegate = false;
@@ -139,18 +139,65 @@ public class TouchDelegate {
break;
}
if (sendToDelegate) {
- final View delegateView = mDelegateView;
-
if (hit) {
// Offset event coordinates to be inside the target view
- event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);
+ event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.getHeight() / 2);
} else {
// Offset event coordinates to be outside the target view (in case it does
// something like tracking pressed state)
int slop = mSlop;
event.setLocation(-(slop * 2), -(slop * 2));
}
- handled = delegateView.dispatchTouchEvent(event);
+ handled = mDelegateView.dispatchTouchEvent(event);
+ }
+ return handled;
+ }
+
+ /**
+ * Forward hover events to the delegate view if the event is within the bounds
+ * specified in the constructor and touch exploration is enabled.
+ *
+ * @param event The hover event to forward
+ * @return True if the event was consumed by the delegate, false otherwise.
+ *
+ * @see android.view.accessibility.AccessibilityManager#isTouchExplorationEnabled
+ */
+ public boolean onTouchExplorationHoverEvent(@NonNull MotionEvent event) {
+ if (mBounds == null) {
+ return false;
+ }
+
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ boolean hit = true;
+ boolean handled = false;
+
+ final boolean isInbound = mBounds.contains(x, y);
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ mDelegateTargeted = isInbound;
+ break;
+ case MotionEvent.ACTION_HOVER_MOVE:
+ if (isInbound) {
+ mDelegateTargeted = true;
+ } else {
+ // delegated previously
+ if (mDelegateTargeted && !mSlopBounds.contains(x, y)) {
+ hit = false;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ mDelegateTargeted = true;
+ break;
+ }
+ if (mDelegateTargeted) {
+ if (hit) {
+ event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.getHeight() / 2);
+ } else {
+ mDelegateTargeted = false;
+ }
+ handled = mDelegateView.dispatchHoverEvent(event);
}
return handled;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1493cd7f2eda..dffbbb7afb9e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12830,6 +12830,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns true if the given point, in local coordinates, is inside the hovered child.
+ *
+ * @hide
+ */
+ protected boolean pointInHoveredChild(MotionEvent event) {
+ return false;
+ }
+
+ /**
* Dispatch a generic motion event to the view under the first pointer.
* <p>
* Do not call this method directly.
@@ -13584,6 +13593,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #onHoverChanged
*/
public boolean onHoverEvent(MotionEvent event) {
+ // Explore by touch should dispatch events to children under pointer first if any before
+ // dispatching to TouchDelegate. For children non-hoverable that will not consume events,
+ // it should also not delegate when they got the pointer hovered.
+ if (mTouchDelegate != null && !pointInHoveredChild(event)) {
+ final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+ if (manager.isEnabled() && manager.isTouchExplorationEnabled()
+ && mTouchDelegate.onTouchExplorationHoverEvent(event)) {
+ return true;
+ }
+ }
+
// The root view may receive hover (or touch) events that are outside the bounds of
// the window. This code ensures that we only send accessibility events for
// hovers that are actually within the bounds of the root view.
@@ -13598,7 +13618,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
} else {
if (action == MotionEvent.ACTION_HOVER_EXIT
- || (action == MotionEvent.ACTION_MOVE
+ || (action == MotionEvent.ACTION_HOVER_MOVE
&& !pointInView(event.getX(), event.getY()))) {
mSendingHoverAccessibilityEvents = false;
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 58febb050150..1e91aa87bfb7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2383,6 +2383,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return mFirstHoverTarget != null;
}
+ /** @hide */
+ @Override
+ protected boolean pointInHoveredChild(MotionEvent event) {
+ if (mFirstHoverTarget != null) {
+ return isTransformedTouchPointInView(event.getX(), event.getY(),
+ mFirstHoverTarget.child, null);
+ }
+ return false;
+ }
+
@Override
public void addChildrenForAccessibility(ArrayList<View> outChildren) {
if (getAccessibilityNodeProvider() != null) {