Merge "Update ExploreByTouchHelper to calculate bounds on screen properly" into nyc-support-25.1-dev
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
index 0f0df96..0b00094 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -16,10 +16,13 @@
package android.support.v4.view.accessibility;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
import android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat;
import android.support.v4.view.ViewCompat;
import android.text.InputType;
@@ -2365,6 +2368,14 @@
private final Object mInfo;
+ /**
+ * android.support.v4.widget.ExploreByTouchHelper.HOST_ID = -1;
+ *
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public int mParentVirtualDescendantId = -1;
+
// Actions introduced in IceCreamSandwich
/**
@@ -3171,6 +3182,7 @@
* @param virtualDescendantId The id of the virtual descendant.
*/
public void setParent(View root, int virtualDescendantId) {
+ mParentVirtualDescendantId = virtualDescendantId;
IMPL.setParent(mInfo, root, virtualDescendantId);
}
diff --git a/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java b/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
index 1b21bc2..526575c 100644
--- a/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
+++ b/core-ui/java/android/support/v4/widget/ExploreByTouchHelper.java
@@ -847,21 +847,45 @@
}
node.setFocused(isFocused);
- // Set the visibility based on the parent bound.
- if (intersectVisibleToUser(mTempParentRect)) {
- node.setVisibleToUser(true);
- node.setBoundsInParent(mTempParentRect);
- }
+ mHost.getLocationOnScreen(mTempGlobalRect);
// If not explicitly specified, calculate screen-relative bounds and
// offset for scroll position based on bounds in parent.
node.getBoundsInScreen(mTempScreenRect);
if (mTempScreenRect.equals(INVALID_PARENT_BOUNDS)) {
- mHost.getLocationOnScreen(mTempGlobalRect);
node.getBoundsInParent(mTempScreenRect);
+
+ // If there is a parent node, adjust bounds based on the parent node.
+ if (node.mParentVirtualDescendantId != HOST_ID) {
+ AccessibilityNodeInfoCompat parentNode = AccessibilityNodeInfoCompat.obtain();
+ // Walk up the node tree to adjust the screen rect.
+ for (int virtualDescendantId = node.mParentVirtualDescendantId;
+ virtualDescendantId != HOST_ID;
+ virtualDescendantId = parentNode.mParentVirtualDescendantId) {
+ // Reset the values in the parent node we'll be using.
+ parentNode.setParent(mHost, HOST_ID);
+ parentNode.setBoundsInParent(INVALID_PARENT_BOUNDS);
+ // Adjust the bounds for the parent node.
+ onPopulateNodeForVirtualView(virtualDescendantId, parentNode);
+ parentNode.getBoundsInParent(mTempParentRect);
+ mTempScreenRect.offset(mTempParentRect.left, mTempParentRect.top);
+ }
+ parentNode.recycle();
+ }
+ // Adjust the rect for the host view's location.
mTempScreenRect.offset(mTempGlobalRect[0] - mHost.getScrollX(),
mTempGlobalRect[1] - mHost.getScrollY());
+ }
+
+ if (mHost.getLocalVisibleRect(mTempVisibleRect)) {
+ mTempVisibleRect.offset(mTempGlobalRect[0] - mHost.getScrollX(),
+ mTempGlobalRect[1] - mHost.getScrollY());
+ mTempScreenRect.intersect(mTempVisibleRect);
node.setBoundsInScreen(mTempScreenRect);
+
+ if (isVisibleToUser(mTempScreenRect)) {
+ node.setVisibleToUser(true);
+ }
}
return node;
@@ -903,7 +927,7 @@
* @param localRect a rectangle in local (parent) coordinates
* @return whether the specified {@link Rect} is visible on the screen
*/
- private boolean intersectVisibleToUser(Rect localRect) {
+ private boolean isVisibleToUser(Rect localRect) {
// Missing or empty bounds mean this view is not visible.
if ((localRect == null) || localRect.isEmpty()) {
return false;
@@ -925,17 +949,7 @@
}
// A null parent implies the view is not visible.
- if (viewParent == null) {
- return false;
- }
-
- // If no portion of the parent is visible, this view is not visible.
- if (!mHost.getLocalVisibleRect(mTempVisibleRect)) {
- return false;
- }
-
- // Check if the view intersects the visible portion of the parent.
- return localRect.intersect(mTempVisibleRect);
+ return viewParent != null;
}
/**
diff --git a/samples/Support4Demos/res/values/strings.xml b/samples/Support4Demos/res/values/strings.xml
index 2d4f0db..24d88d6 100644
--- a/samples/Support4Demos/res/values/strings.xml
+++ b/samples/Support4Demos/res/values/strings.xml
@@ -215,6 +215,7 @@
<string name="sample_item_a">Sample item A</string>
<string name="sample_item_b">Sample item B</string>
<string name="sample_item_c">Sample item C</string>
+ <string name="sample_item_d">Sample item D</string>
<!-- ContentLoadingProgressBar -->
<string name="content_loading_progress_bar">Widget/Content Loading Progress Bar</string>
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java
index 3f7675c..10db8f3 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/ExploreByTouchHelperActivity.java
@@ -86,6 +86,13 @@
CustomView.CustomItem itemC =
customView.addItem(getString(R.string.sample_item_c), 0, 0.75f, 1, 1);
customView.setParentItem(itemC, itemB);
+
+ // Add an item at the left quarter of Item C.
+ CustomView.CustomItem itemD =
+ customView.addItem(getString(R.string.sample_item_d), 0, 0f, 0.25f, 1);
+ customView.setParentItem(itemD, itemC);
+
+ customView.layoutItems();
}
/**
@@ -169,12 +176,28 @@
public void setParentItem(CustomItem item, CustomItem parent) {
item.mParent = parent;
parent.mChildren.add(item.mId);
- RectF bounds = item.mBounds;
- item.mBounds = new RectF(parent.mBounds.left + bounds.left * parent.mBounds.width(),
- parent.mBounds.top + bounds.top * parent.mBounds.height(),
- parent.mBounds.left + bounds.right * parent.mBounds.width(),
- parent.mBounds.top + bounds.bottom * parent.mBounds.height());
+ }
+ /**
+ * Walk the view hierarchy of each item and calculate mBoundsInRoot.
+ */
+ public void layoutItems() {
+ for (CustomItem item : mItems) {
+ layoutItem(item);
+ }
+ }
+
+ void layoutItem(CustomItem item) {
+ item.mBoundsInRoot = new RectF(item.mBounds);
+ CustomItem parent = item.mParent;
+ while (parent != null) {
+ RectF bounds = item.mBoundsInRoot;
+ item.mBoundsInRoot.set(parent.mBounds.left + bounds.left * parent.mBounds.width(),
+ parent.mBounds.top + bounds.top * parent.mBounds.height(),
+ parent.mBounds.left + bounds.right * parent.mBounds.width(),
+ parent.mBounds.top + bounds.bottom * parent.mBounds.height());
+ parent = parent.mParent;
+ }
}
@Override
@@ -193,7 +216,7 @@
paint.setColor(item.mChecked ? Color.MAGENTA : Color.GREEN);
}
paint.setStyle(Style.FILL);
- scaleRectF(item.mBounds, bounds, width, height);
+ scaleRectF(item.mBoundsInRoot, bounds, width, height);
canvas.drawRect(bounds, paint);
paint.setColor(Color.WHITE);
paint.setTextAlign(Align.CENTER);
@@ -232,7 +255,7 @@
// Search in reverse order, so that topmost items are selected first.
for (int i = n - 1; i >= 0; i--) {
final CustomItem item = mItems.get(i);
- if (item.mBounds.contains(scaledX, scaledY)) {
+ if (item.mBoundsInRoot.contains(scaledX, scaledY)) {
return i;
}
}
@@ -321,8 +344,12 @@
// hit detection performed in getVirtualViewAt() and
// onTouchEvent().
final Rect bounds = mTempRect;
- final int height = getHeight();
- final int width = getWidth();
+ int height = getHeight();
+ int width = getWidth();
+ if (item.mParent != null) {
+ width = (int) (width * item.mParent.mBoundsInRoot.width());
+ height = (int) (height * item.mParent.mBoundsInRoot.height());
+ }
scaleRectF(item.mBounds, bounds, width, height);
node.setBoundsInParent(bounds);
@@ -365,6 +392,7 @@
private List<Integer> mChildren = new ArrayList<>();
private String mDescription;
private RectF mBounds;
+ private RectF mBoundsInRoot;
private boolean mChecked;
}
}