OSDN Git Service

Ensure accessibility focus rect is drawn correctly
authorAlan Viverette <alanv@google.com>
Tue, 28 Oct 2014 20:45:11 +0000 (13:45 -0700)
committerAlan Viverette <alanv@google.com>
Tue, 28 Oct 2014 20:57:47 +0000 (20:57 +0000)
Reverts previous change that draws it as part of View.draw() and ensures
that the display is always redrawn if a view has accessibility focus.
Correctly removes focus from views with accessibility focused ancestors.

Also reverts the focus indicator to use a solid line since the dotted
line looked janky.

BUG: 17675993
Change-Id: I25718334cc1ae1c2cd19d3c23af1c18c6a67504c

core/java/android/view/View.java
core/java/android/view/ViewGroup.java
core/java/android/view/ViewRootImpl.java
core/java/android/widget/AbsListView.java
core/res/res/drawable/view_accessibility_focused.xml

index e7b98ca..06b1e60 100644 (file)
@@ -14062,7 +14062,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                     } else {
                         draw(canvas);
                     }
-                    drawAccessibilityFocus(canvas);
                 }
             } finally {
                 renderNode.end(canvas);
@@ -14357,7 +14356,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
             } else {
                 draw(canvas);
             }
-            drawAccessibilityFocus(canvas);
 
             canvas.restoreToCount(restoreCount);
             canvas.setBitmap(null);
@@ -14432,7 +14430,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         } else {
             draw(canvas);
         }
-        drawAccessibilityFocus(canvas);
 
         mPrivateFlags = flags;
 
@@ -15030,13 +15027,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                     if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                         mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                         dispatchDraw(canvas);
-                        if (mOverlay != null && !mOverlay.isEmpty()) {
-                            mOverlay.getOverlayView().draw(canvas);
-                        }
                     } else {
                         draw(canvas);
                     }
-                    drawAccessibilityFocus(canvas);
                 } else {
                     mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                     ((HardwareCanvas) canvas).drawRenderNode(renderNode, null, flags);
@@ -15288,50 +15281,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     }
 
     /**
-     * Draws the accessibility focus rect onto the specified canvas.
-     *
-     * @param canvas Canvas on which to draw the focus rect
-     */
-    private void drawAccessibilityFocus(Canvas canvas) {
-        if (mAttachInfo == null) {
-            return;
-        }
-
-        final Rect bounds = mAttachInfo.mTmpInvalRect;
-        final ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot == null || viewRoot.getAccessibilityFocusedHost() != this) {
-            return;
-        }
-
-        final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
-        if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
-            return;
-        }
-
-        final Drawable drawable = viewRoot.getAccessibilityFocusedDrawable();
-        if (drawable == null) {
-            return;
-        }
-
-        final AccessibilityNodeInfo virtualView = viewRoot.getAccessibilityFocusedVirtualView();
-        if (virtualView != null) {
-            virtualView.getBoundsInScreen(bounds);
-            final int[] offset = mAttachInfo.mTmpLocation;
-            getLocationOnScreen(offset);
-            bounds.offset(-offset[0], -offset[1]);
-        } else {
-            bounds.set(0, 0, mRight - mLeft, mBottom - mTop);
-        }
-
-        canvas.save();
-        canvas.translate(mScrollX, mScrollY);
-        canvas.clipRect(bounds, Region.Op.REPLACE);
-        drawable.setBounds(bounds);
-        drawable.draw(canvas);
-        canvas.restore();
-    }
-
-    /**
      * Draws the background onto the specified canvas.
      *
      * @param canvas Canvas on which to draw the background
index 7c7e3e7..654a8ed 100644 (file)
@@ -4250,9 +4250,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
             clearChildFocus = true;
         }
 
-        if (view.isAccessibilityFocused()) {
-            view.clearAccessibilityFocus();
-        }
+        view.clearAccessibilityFocus();
 
         cancelTouchTarget(view);
         cancelHoverTarget(view);
@@ -4345,9 +4343,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                 clearChildFocus = true;
             }
 
-            if (view.isAccessibilityFocused()) {
-                view.clearAccessibilityFocus();
-            }
+            view.clearAccessibilityFocus();
 
             cancelTouchTarget(view);
             cancelHoverTarget(view);
@@ -4432,9 +4428,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                 clearChildFocus = true;
             }
 
-            if (view.isAccessibilityFocused()) {
-                view.clearAccessibilityFocus();
-            }
+            view.clearAccessibilityFocus();
 
             cancelTouchTarget(view);
             cancelHoverTarget(view);
index 81fc966..0f268d5 100644 (file)
@@ -2255,6 +2255,7 @@ public final class ViewRootImpl implements ViewParent,
             canvas.drawHardwareLayer(mResizeBuffer, mHardwareXOffset, mHardwareYOffset,
                     mResizePaint);
         }
+        drawAccessibilityFocusedDrawableIfNeeded(canvas);
     }
 
     /**
@@ -2474,18 +2475,38 @@ public final class ViewRootImpl implements ViewParent,
             dirty.offset(surfaceInsets.left, surfaceInsets.right);
         }
 
-        if (!dirty.isEmpty() || mIsAnimating) {
+        boolean accessibilityFocusDirty = false;
+        final Drawable drawable = mAttachInfo.mAccessibilityFocusDrawable;
+        if (drawable != null) {
+            final Rect bounds = mAttachInfo.mTmpInvalRect;
+            final boolean hasFocus = getAccessibilityFocusedRect(bounds);
+            if (!hasFocus) {
+                bounds.setEmpty();
+            }
+            if (!bounds.equals(drawable.getBounds())) {
+                accessibilityFocusDirty = true;
+            }
+        }
+
+        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
             if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
+                // If accessibility focus moved, always invalidate the root.
+                boolean invalidateRoot = accessibilityFocusDirty;
+
                 // Draw with hardware renderer.
                 mIsAnimating = false;
-                boolean invalidateRoot = false;
+
                 if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
                     mHardwareYOffset = yOffset;
                     mHardwareXOffset = xOffset;
-                    mAttachInfo.mHardwareRenderer.invalidateRoot();
+                    invalidateRoot = true;
                 }
                 mResizeAlpha = resizeAlpha;
 
+                if (invalidateRoot) {
+                    mAttachInfo.mHardwareRenderer.invalidateRoot();
+                }
+
                 dirty.setEmpty();
 
                 mBlockResizeBuffer = false;
@@ -2604,6 +2625,8 @@ public final class ViewRootImpl implements ViewParent,
                 attachInfo.mSetIgnoreDirtyState = false;
 
                 mView.draw(canvas);
+
+                drawAccessibilityFocusedDrawableIfNeeded(canvas);
             } finally {
                 if (!attachInfo.mSetIgnoreDirtyState) {
                     // Only clear the flag if it was not set during the mView.draw() call
@@ -2627,7 +2650,56 @@ public final class ViewRootImpl implements ViewParent,
         return true;
     }
 
-    Drawable getAccessibilityFocusedDrawable() {
+    /**
+     * We want to draw a highlight around the current accessibility focused.
+     * Since adding a style for all possible view is not a viable option we
+     * have this specialized drawing method.
+     *
+     * Note: We are doing this here to be able to draw the highlight for
+     *       virtual views in addition to real ones.
+     *
+     * @param canvas The canvas on which to draw.
+     */
+    private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) {
+        final Rect bounds = mAttachInfo.mTmpInvalRect;
+        if (getAccessibilityFocusedRect(bounds)) {
+            final Drawable drawable = getAccessibilityFocusedDrawable();
+            if (drawable != null) {
+                drawable.setBounds(bounds);
+                drawable.draw(canvas);
+            }
+        } else if (mAttachInfo.mAccessibilityFocusDrawable != null) {
+            mAttachInfo.mAccessibilityFocusDrawable.setBounds(0, 0, 0, 0);
+        }
+    }
+
+    private boolean getAccessibilityFocusedRect(Rect bounds) {
+        final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext);
+        if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
+            return false;
+        }
+
+        final View host = mAccessibilityFocusedHost;
+        if (host == null || host.mAttachInfo == null) {
+            return false;
+        }
+
+        final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
+        if (provider == null) {
+            host.getBoundsOnScreen(bounds);
+        } else if (mAccessibilityFocusedVirtualView != null) {
+            mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
+        } else {
+            return false;
+        }
+
+        final AttachInfo attachInfo = mAttachInfo;
+        bounds.offset(-attachInfo.mWindowLeft, -attachInfo.mWindowTop);
+        bounds.intersect(0, 0, attachInfo.mViewRootImpl.mWidth, attachInfo.mViewRootImpl.mHeight);
+        return !bounds.isEmpty();
+    }
+
+    private Drawable getAccessibilityFocusedDrawable() {
         // Lazily load the accessibility focus drawable.
         if (mAttachInfo.mAccessibilityFocusDrawable == null) {
             final TypedValue value = new TypedValue();
index 4aebaae..6c107a2 100644 (file)
@@ -4910,9 +4910,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                     if (position >= headerViewsCount && position < footerViewsStart) {
                         // The view will be rebound to new data, clear any
                         // system-managed transient state.
-                        if (child.isAccessibilityFocused()) {
-                            child.clearAccessibilityFocus();
-                        }
+                        child.clearAccessibilityFocus();
                         mRecycler.addScrapView(child, position);
                     }
                 }
@@ -4933,9 +4931,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                     if (position >= headerViewsCount && position < footerViewsStart) {
                         // The view will be rebound to new data, clear any
                         // system-managed transient state.
-                        if (child.isAccessibilityFocused()) {
-                            child.clearAccessibilityFocus();
-                        }
+                        child.clearAccessibilityFocus();
                         mRecycler.addScrapView(child, position);
                     }
                 }
@@ -6776,9 +6772,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
         }
 
         private void clearAccessibilityFromScrap(View view) {
-            if (view.isAccessibilityFocused()) {
-                view.clearAccessibilityFocus();
-            }
+            view.clearAccessibilityFocus();
             view.setAccessibilityDelegate(null);
         }
 
index 68e3f1e..025916b 100644 (file)
@@ -18,9 +18,7 @@
 
     <stroke
         android:width="4dp"
-        android:color="@color/accessibility_focus_highlight"
-        android:dashWidth="4dp"
-        android:dashGap="2dp" />
+        android:color="@color/accessibility_focus_highlight" />
 
     <corners android:radius="2dp" />