public static Point GetOverlayAnchorPosition(AccessibilityNodeInfo anchorNode, AccessibilityNodeInfo root, IEnumerable <AccessibilityWindowInfo> windows) { Point point = null; if (anchorNode != null) { // Update node's info since this is still a reference from an older event anchorNode.Refresh(); if (!anchorNode.VisibleToUser) { return(new Point(-1, -1)); } if (!anchorNode.Focused) { return(null); } // node.VisibleToUser doesn't always give us exactly what we want, so attempt to tighten up the range // of visibility var rootNodeHeight = GetNodeHeight(root); var limitLowY = 0; var limitHighY = rootNodeHeight - GetNodeHeight(anchorNode); if (windows != null) { if (IsStatusBarExpanded(windows)) { return(new Point(-1, -1)); } var inputWindowRect = GetInputMethodWindowRect(windows); if (inputWindowRect != null) { limitLowY += inputWindowRect.Height(); if (Build.VERSION.SdkInt >= BuildVersionCodes.Q) { limitLowY += GetNavigationBarHeight() + GetStatusBarHeight(); } inputWindowRect.Dispose(); } } point = GetOverlayAnchorPosition(root, anchorNode, rootNodeHeight); if (point.Y < limitLowY || point.Y > limitHighY) { point.X = -1; point.Y = -1; } } return(point); }
public static Point GetOverlayAnchorPosition(AccessibilityService service, AccessibilityNodeInfo anchorNode, AccessibilityNodeInfo root, IEnumerable <AccessibilityWindowInfo> windows, int overlayViewHeight, bool isOverlayAboveAnchor) { Point point = null; if (anchorNode != null) { // Update node's info since this is still a reference from an older event anchorNode.Refresh(); if (!anchorNode.VisibleToUser) { return(new Point(-1, -1)); } if (!anchorNode.Focused) { return(null); } // node.VisibleToUser doesn't always give us exactly what we want, so attempt to tighten up the range // of visibility var inputMethodHeight = 0; if (windows != null) { if (IsStatusBarExpanded(windows)) { return(new Point(-1, -1)); } inputMethodHeight = GetInputMethodHeight(windows); } var minY = 0; var rootNodeHeight = GetNodeHeight(root); if (rootNodeHeight == -1) { return(null); } var maxY = rootNodeHeight - GetNavigationBarHeight(service) - GetStatusBarHeight(service) - inputMethodHeight; point = GetOverlayAnchorPosition(service, anchorNode, overlayViewHeight, isOverlayAboveAnchor); if (point.Y < minY) { if (isOverlayAboveAnchor) { // view nearing bounds, anchor to bottom point.X = -1; point.Y = 0; } else { // view out of bounds, hide overlay point.X = -1; point.Y = -1; } } else if (point.Y > (maxY - overlayViewHeight)) { if (isOverlayAboveAnchor) { // view out of bounds, hide overlay point.X = -1; point.Y = -1; } else { // view nearing bounds, anchor to top point.X = 0; point.Y = -1; } } else if (isOverlayAboveAnchor && point.Y < (maxY - (overlayViewHeight * 2) - GetNodeHeight(anchorNode))) { // This else block forces the overlay to return to bottom alignment as soon as space is available // below the anchor view. Removing this will change the behavior to wait until there isn't enough // space above the anchor view before returning to bottom alignment. point.X = -1; point.Y = 0; } } return(point); }