private final RenderCoordinates mRenderCoordinates;
private long mNativeObj;
private int mAccessibilityFocusId;
+ private Rect mAccessibilityFocusRect;
private boolean mIsHovering;
private int mLastHoverId = View.NO_ID;
private int mCurrentRootId;
private final ViewGroup mView;
private boolean mUserHasTouchExplored;
private boolean mPendingScrollToMakeNodeVisible;
+ private boolean mNotifyFrameInfoInitializedCalled;
/**
* Create a BrowserAccessibilityManager object, which is owned by the C++
switch (action) {
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
- if (mAccessibilityFocusId == virtualViewId) {
- return true;
- }
+ if (!moveAccessibilityFocusToId(virtualViewId)) return true;
- mAccessibilityFocusId = virtualViewId;
- sendAccessibilityEvent(mAccessibilityFocusId,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
if (!mIsHovering) {
nativeScrollToMakeNodeVisible(
mNativeObj, mAccessibilityFocusId);
sendAccessibilityEvent(mAccessibilityFocusId,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
mAccessibilityFocusId = View.NO_ID;
+ mAccessibilityFocusRect = null;
}
return true;
case AccessibilityNodeInfo.ACTION_CLICK:
* web coordinates to screen coordinates.
*/
public void notifyFrameInfoInitialized() {
+ if (mNotifyFrameInfoInitializedCalled)
+ return;
+
+ mNotifyFrameInfoInitializedCalled = true;
+
// Invalidate the container view, since the chrome accessibility tree is now
// ready and listed as the child of the container view.
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
// (Re-) focus focused element, since we weren't able to create an
// AccessibilityNodeInfo for this element before.
if (mAccessibilityFocusId != View.NO_ID) {
- sendAccessibilityEvent(mAccessibilityFocusId,
- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ moveAccessibilityFocusToIdAndRefocusIfNeeded(mAccessibilityFocusId);
}
}
if (id == 0)
return false;
- mAccessibilityFocusId = id;
- sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ moveAccessibilityFocusToId(id);
+ return true;
+ }
+
+ private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) {
+ if (newAccessibilityFocusId == mAccessibilityFocusId)
+ return false;
+
+ mAccessibilityFocusId = newAccessibilityFocusId;
+ mAccessibilityFocusRect = null;
+ sendAccessibilityEvent(mAccessibilityFocusId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
return true;
}
+ private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibilityFocusId) {
+ // Work around a bug in the Android framework where it doesn't fully update the object
+ // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED. To work around
+ // this, clear focus and then set focus again.
+ if (newAccessibilityFocusId == mAccessibilityFocusId) {
+ sendAccessibilityEvent(newAccessibilityFocusId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ mAccessibilityFocusId = View.NO_ID;
+ }
+
+ moveAccessibilityFocusToId(newAccessibilityFocusId);
+ }
+
private void sendAccessibilityEvent(int virtualViewId, int eventType) {
// If we don't have any frame info, then the virtual hierarchy
// doesn't exist in the view of the Android framework, so should
return result;
}
+ /**
+ * Returns whether or not the frame info is initialized, meaning we can safely
+ * convert web coordinates to screen coordinates. When this is first initialized,
+ * notifyFrameInfoInitialized is called - but we shouldn't check whether or not
+ * that method was called as a way to determine if frame info is valid because
+ * notifyFrameInfoInitialized might not be called at all if mRenderCoordinates
+ * gets initialized first.
+ */
private boolean isFrameInfoInitialized() {
return mRenderCoordinates.getContentWidthCss() != 0.0 ||
mRenderCoordinates.getContentHeightCss() != 0.0;
if (mUserHasTouchExplored) return;
if (mContentViewCore.shouldSetAccessibilityFocusOnPageLoad()) {
- mAccessibilityFocusId = id;
- sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ moveAccessibilityFocusToIdAndRefocusIfNeeded(id);
}
}
@CalledByNative
private void handleFocusChanged(int id) {
sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_FOCUSED);
-
- // Update accessibility focus if not already set to this node.
- if (mAccessibilityFocusId != id) {
- sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
- mAccessibilityFocusId = id;
- }
+ moveAccessibilityFocusToId(id);
}
@CalledByNative
@CalledByNative
private void handleNavigate() {
mAccessibilityFocusId = View.NO_ID;
+ mAccessibilityFocusRect = null;
mUserHasTouchExplored = false;
// Invalidate the host, since its child is now gone.
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@CalledByNative
private void handleScrolledToAnchor(int id) {
- if (mAccessibilityFocusId == id) {
- return;
- }
-
- mAccessibilityFocusId = id;
- sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ moveAccessibilityFocusToId(id);
}
@CalledByNative
@CalledByNative
private void setAccessibilityNodeInfoLocation(AccessibilityNodeInfo node,
+ final int virtualViewId,
int absoluteLeft, int absoluteTop, int parentRelativeLeft, int parentRelativeTop,
int width, int height, boolean isRootNode) {
// First set the bounds in parent.
rect.offset(viewLocation[0], viewLocation[1]);
node.setBoundsInScreen(rect);
+
+ // Work around a bug in the Android framework where if the object with accessibility
+ // focus moves, the accessibility focus rect is not updated - both the visual highlight,
+ // and the location on the screen that's clicked if you double-tap. To work around this,
+ // when we know the object with accessibility focus moved, move focus away and then
+ // move focus right back to it, which tricks Android into updating its bounds.
+ if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentRootId) {
+ if (mAccessibilityFocusRect == null) {
+ mAccessibilityFocusRect = rect;
+ } else if (!mAccessibilityFocusRect.equals(rect)) {
+ mAccessibilityFocusRect = rect;
+ moveAccessibilityFocusToIdAndRefocusIfNeeded(virtualViewId);
+ }
+ }
}
@CalledByNative