Android: Re-focus focused accessibility node after orientation change

Before this patch the accessibility focus on a selected element could
be drawn incorrectly after the screen orientation has changed.
The reason for that is that wrong X and Y offsets were used for it.
The offsets are used to take the sizes of different panels (like
buttons and notifications bar) into account.
Normally they are updated only when getNodeForView() is called and the
whole accessibility info is reconstructed.
However, when the screen orientation changes, we get some
getNodeForVirtualViewId(selectedId) calls before the whole accessibility
tree is recreated. This is when redrawing happens, and as I understand,
this is an internal Android event, so we can't do much about it.

The most straightforward fix for the problem would be to query the
offsets also in each getNodeForVirtualViewId() call. However, offset
calculation seems to be quite heavy operation, and
getNodeForVirtualViewId can be called very often (esp. if we have a lot
of UI elements). As a result, this fix can't be implemented.

In this patch I came up with the second approach - once getNodeForView()
is called, and it detects that the offsets have changed, we force
re-focus of the currently selected element. This allows us to retain the
offsets-caching mechanism.

Fixes: QTBUG-93402
Pick-to: 6.3 6.2 5.15
Change-Id: Ic420afe1fe5e80fbdf91b2b2651f2daa71c6e44d
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Ivan Solovev 2022-02-08 14:27:30 +01:00
parent 0a24e2e984
commit 7c00ad4e9a

View File

@ -89,6 +89,8 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
// this is because the Android platform window does not take
// the offset of the view on screen into account (eg status bar on top)
private final int[] m_globalOffset = new int[2];
private int m_oldOffsetX = 0;
private int m_oldOffsetY = 0;
private class HoverEventListener implements View.OnHoverListener
{
@ -327,6 +329,22 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
for (int i = 0; i < ids.length; ++i)
result.addChild(m_view, ids[i]);
// The offset values have changed, so we need to re-focus the
// currently focused item, otherwise it will have an incorrect
// focus frame
if ((m_oldOffsetX != offsetX) || (m_oldOffsetY != offsetY)) {
m_oldOffsetX = offsetX;
m_oldOffsetY = offsetY;
if (m_focusedVirtualViewId != INVALID_ID) {
m_nodeProvider.performAction(m_focusedVirtualViewId,
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
new Bundle());
m_nodeProvider.performAction(m_focusedVirtualViewId,
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS,
new Bundle());
}
}
return result;
}