Fix ViewRootImpl handling of content changes, fix ViewPager parenting

Previously content changes were only handled if they came directly from
the focused host, which meant that changes that occurred higher in the
tree were ignored. As a result, scrolling in ViewPager that contained
virtual nodes would fail to update the focus position.

Change-Id: I028bd8c670f1210339da331626e1986c5b5d4b87
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 887187a..a69384a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9959,6 +9959,8 @@
      * @param oldt Previous vertical scroll origin.
      */
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        notifySubtreeAccessibilityStateChangedIfNeeded();
+
         if (AccessibilityManager.getInstance(mContext).isEnabled()) {
             postSendViewScrolledAccessibilityEventCallback();
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 294174a..4158340 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6268,41 +6268,79 @@
 
 
             case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
-                if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView != null) {
-                    // We care only for changes rooted in the focused host.
-                    final long eventSourceId = event.getSourceNodeId();
-                    final int hostViewId = AccessibilityNodeInfo.getAccessibilityViewId(
-                            eventSourceId);
-                    if (hostViewId != mAccessibilityFocusedHost.getAccessibilityViewId()) {
-                        break;
-                    }
-
-                    // We only care about changes that may change the virtual focused view bounds.
-                    final int changes = event.getContentChangeTypes();
-                    if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0
-                            || changes == AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
-                        AccessibilityNodeProvider provider = mAccessibilityFocusedHost
-                                .getAccessibilityNodeProvider();
-                        if (provider != null) {
-                            final int virtualChildId = AccessibilityNodeInfo.getVirtualDescendantId(
-                                    mAccessibilityFocusedVirtualView.getSourceNodeId());
-                            if (virtualChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                                mAccessibilityFocusedVirtualView = provider
-                                        .createAccessibilityNodeInfo(
-                                                AccessibilityNodeProvider.HOST_VIEW_ID);
-                            } else {
-                                mAccessibilityFocusedVirtualView = provider
-                                        .createAccessibilityNodeInfo(virtualChildId);
-                            }
-                        }
-                    }
-                }
+                handleWindowContentChangedEvent(event);
             } break;
         }
         mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
 
+    /**
+     * Updates the focused virtual view, when necessary, in response to a
+     * content changed event.
+     * <p>
+     * This is necessary to get updated bounds after a position change.
+     *
+     * @param event an accessibility event of type
+     *              {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+     */
+    private void handleWindowContentChangedEvent(AccessibilityEvent event) {
+        // No virtual view focused, nothing to do here.
+        if (mAccessibilityFocusedHost == null || mAccessibilityFocusedVirtualView == null) {
+            return;
+        }
+
+        // If we have a node but no provider, abort.
+        final AccessibilityNodeProvider provider =
+                mAccessibilityFocusedHost.getAccessibilityNodeProvider();
+        if (provider == null) {
+            // TODO: Should we clear the focused virtual view?
+            return;
+        }
+
+        // We only care about change types that may affect the bounds of the
+        // focused virtual view.
+        final int changes = event.getContentChangeTypes();
+        if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) == 0
+                && changes != AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
+            return;
+        }
+
+        final long eventSourceNodeId = event.getSourceNodeId();
+        final int changedViewId = AccessibilityNodeInfo.getAccessibilityViewId(eventSourceNodeId);
+
+        // Search up the tree for subtree containment.
+        boolean hostInSubtree = false;
+        View root = mAccessibilityFocusedHost;
+        while (root != null && !hostInSubtree) {
+            if (changedViewId == root.getAccessibilityViewId()) {
+                hostInSubtree = true;
+            } else {
+                final ViewParent parent = root.getParent();
+                if (parent instanceof View) {
+                    root = (View) parent;
+                } else {
+                    root = null;
+                }
+            }
+        }
+
+        // We care only about changes in subtrees containing the host view.
+        if (!hostInSubtree) {
+            return;
+        }
+
+        final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId();
+        int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId);
+        if (focusedChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+            // TODO: Should we clear the focused virtual view?
+            focusedChildId = AccessibilityNodeProvider.HOST_VIEW_ID;
+        }
+
+        // Refresh the node for the focused virtual view.
+        mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId);
+    }
+
     @Override
     public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
         postSendWindowContentChangedCallback(source, changeType);
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 8018942..8d66191 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -42,6 +42,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityRecord;
 import android.view.animation.Interpolator;
 import android.widget.EdgeEffect;
@@ -371,8 +372,6 @@
         mCloseEnough = (int) (CLOSE_ENOUGH * density);
         mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
 
-        setAccessibilityDelegate(new MyAccessibilityDelegate());
-
         if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         }
@@ -2695,29 +2694,6 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        // Dispatch scroll events from this ViewPager.
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
-            return super.dispatchPopulateAccessibilityEvent(event);
-        }
-
-        // Dispatch all other accessibility events from the current page.
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == VISIBLE) {
-                final ItemInfo ii = infoForChild(child);
-                if (ii != null && ii.position == mCurItem &&
-                        child.dispatchPopulateAccessibilityEvent(event)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    @Override
     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
         return new LayoutParams();
     }
@@ -2737,60 +2713,63 @@
         return new LayoutParams(getContext(), attrs);
     }
 
-    class MyAccessibilityDelegate extends AccessibilityDelegate {
 
-        @Override
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(host, event);
-            event.setClassName(ViewPager.class.getName());
-            final AccessibilityRecord record = AccessibilityRecord.obtain();
-            record.setScrollable(canScroll());
-            if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED
-                    && mAdapter != null) {
-                record.setItemCount(mAdapter.getCount());
-                record.setFromIndex(mCurItem);
-                record.setToIndex(mCurItem);
-            }
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+
+        event.setClassName(ViewPager.class.getName());
+        event.setScrollable(canScroll());
+
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED && mAdapter != null) {
+            event.setItemCount(mAdapter.getCount());
+            event.setFromIndex(mCurItem);
+            event.setToIndex(mCurItem);
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        info.setClassName(ViewPager.class.getName());
+        info.setScrollable(canScroll());
+
+        if (canScrollHorizontally(1)) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
         }
 
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-            super.onInitializeAccessibilityNodeInfo(host, info);
-            info.setClassName(ViewPager.class.getName());
-            info.setScrollable(canScroll());
-            if (canScrollHorizontally(1)) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
-            }
-            if (canScrollHorizontally(-1)) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-            }
+        if (canScrollHorizontally(-1)) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle args) {
+        if (super.performAccessibilityAction(action, args)) {
+            return true;
         }
 
-        @Override
-        public boolean performAccessibilityAction(View host, int action, Bundle args) {
-            if (super.performAccessibilityAction(host, action, args)) {
-                return true;
-            }
-            switch (action) {
-                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                    if (canScrollHorizontally(1)) {
-                        setCurrentItem(mCurItem + 1);
-                        return true;
-                    }
-                } return false;
-                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                    if (canScrollHorizontally(-1)) {
-                        setCurrentItem(mCurItem - 1);
-                        return true;
-                    }
-                } return false;
-            }
-            return false;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                if (canScrollHorizontally(1)) {
+                    setCurrentItem(mCurItem + 1);
+                    return true;
+                }
+                return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                if (canScrollHorizontally(-1)) {
+                    setCurrentItem(mCurItem - 1);
+                    return true;
+                }
+                return false;
         }
 
-        private boolean canScroll() {
-            return (mAdapter != null) && (mAdapter.getCount() > 1);
-        }
+        return false;
+    }
+
+    private boolean canScroll() {
+        return mAdapter != null && mAdapter.getCount() > 1;
     }
 
     private class PagerObserver extends DataSetObserver {