Android: remove a11y methods from QtNative, call a11y delegate direct

Remove yet another two layers of delegation, QtNative calling
QtActivityDelegate and that in turn calls QtAccessibilityDelegate.
Now from c++ native code, acquire the a11y delegate and use it to
call a11y operations that live in QtAccessibilityDelegate.

Task-number: QTBUG-118077
Change-Id: I9e84520c2caa281a6f786a687b0106d702f92a67
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
Assam Boudjelthia 2023-09-20 17:01:00 +02:00
parent ddb1c75afe
commit 0f3dbd6dc7
7 changed files with 119 additions and 144 deletions

View File

@ -342,7 +342,14 @@ public class QtActivityDelegate
public void initializeAccessibility() public void initializeAccessibility()
{ {
m_accessibilityDelegate = new QtAccessibilityDelegate(m_activity, m_layout, this); final QtActivityDelegate currentDelegate = this;
QtNative.runAction(new Runnable() {
@Override
public void run() {
m_accessibilityDelegate = new QtAccessibilityDelegate(m_activity, m_layout,
currentDelegate);
}
});
} }
void handleUiModeChange(int uiMode) void handleUiModeChange(int uiMode)

View File

@ -239,7 +239,7 @@ public class QtNative
updateApplicationState(state); updateApplicationState(state);
} }
static void runAction(Runnable action) public static void runAction(Runnable action)
{ {
synchronized (m_mainActivityMutex) { synchronized (m_mainActivityMutex) {
final Looper mainLooper = Looper.getMainLooper(); final Looper mainLooper = Looper.getMainLooper();
@ -364,66 +364,6 @@ public class QtNative
}); });
} }
private static void notifyAccessibilityLocationChange(final int viewId)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null) {
m_activityDelegate.notifyAccessibilityLocationChange(viewId);
}
}
});
}
private static void notifyObjectHide(final int viewId, final int parentId)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null) {
m_activityDelegate.notifyObjectHide(viewId, parentId);
}
}
});
}
private static void notifyObjectFocus(final int viewId)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null) {
m_activityDelegate.notifyObjectFocus(viewId);
}
}
});
}
private static void notifyValueChanged(int viewId, String value)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null) {
m_activityDelegate.notifyValueChanged(viewId, value);
}
}
});
}
private static void notifyScrolledEvent(final int viewId)
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null) {
m_activityDelegate.notifyScrolledEvent(viewId);
}
}
});
}
public static void notifyQtAndroidPluginRunning(final boolean running) public static void notifyQtAndroidPluginRunning(final boolean running)
{ {
if (m_activityDelegate != null) if (m_activityDelegate != null)
@ -569,17 +509,6 @@ public class QtNative
}); });
} }
private static void initializeAccessibility()
{
runAction(new Runnable() {
@Override
public void run() {
if (m_activityDelegate != null)
m_activityDelegate.initializeAccessibility();
}
});
}
private static void hideSplashScreen(final int duration) private static void hideSplashScreen(final int duration)
{ {
runAction(new Runnable() { runAction(new Runnable() {

View File

@ -27,6 +27,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.qtproject.qt.android.QtActivityDelegate; import org.qtproject.qt.android.QtActivityDelegate;
import org.qtproject.qt.android.QtNative;
public class QtAccessibilityDelegate extends View.AccessibilityDelegate public class QtAccessibilityDelegate extends View.AccessibilityDelegate
{ {
@ -164,75 +165,100 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate
public void notifyScrolledEvent(int viewId) public void notifyScrolledEvent(int viewId)
{ {
sendEventForVirtualViewId(viewId, AccessibilityEvent.TYPE_VIEW_SCROLLED); QtNative.runAction(new Runnable() {
@Override
public void run() {
sendEventForVirtualViewId(viewId, AccessibilityEvent.TYPE_VIEW_SCROLLED);
}
});
} }
public void notifyLocationChange(int viewId) public void notifyLocationChange(int viewId)
{ {
if (m_focusedVirtualViewId == viewId) QtNative.runAction(new Runnable() {
invalidateVirtualViewId(m_focusedVirtualViewId); @Override
public void run() {
if (m_focusedVirtualViewId == viewId)
invalidateVirtualViewId(m_focusedVirtualViewId);
}
});
} }
public void notifyObjectHide(int viewId, int parentId) public void notifyObjectHide(int viewId, int parentId)
{ {
// If the object had accessibility focus, we need to clear it. QtNative.runAction(new Runnable() {
// Note: This code is mostly copied from @Override
// AccessibilityNodeProvider::performAction, but we remove the public void run() {
// focus only if the focused view id matches the one that was hidden. // If the object had accessibility focus, we need to clear it.
if (m_focusedVirtualViewId == viewId) { // Note: This code is mostly copied from
m_focusedVirtualViewId = INVALID_ID; // AccessibilityNodeProvider::performAction, but we remove the
m_view.invalidate(); // focus only if the focused view id matches the one that was hidden.
sendEventForVirtualViewId(viewId, if (m_focusedVirtualViewId == viewId) {
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); m_focusedVirtualViewId = INVALID_ID;
} m_view.invalidate();
// When the object is hidden, we need to notify its parent about sendEventForVirtualViewId(viewId,
// content change, not the hidden object itself AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
invalidateVirtualViewId(parentId); }
// When the object is hidden, we need to notify its parent about
// content change, not the hidden object itself
invalidateVirtualViewId(parentId);
}
});
} }
public void notifyObjectFocus(int viewId) public void notifyObjectFocus(int viewId)
{ {
if (m_view == null) QtNative.runAction(new Runnable() {
return; @Override
m_focusedVirtualViewId = viewId; public void run() {
m_view.invalidate(); if (m_view == null)
sendEventForVirtualViewId(viewId, return;
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); m_focusedVirtualViewId = viewId;
m_view.invalidate();
sendEventForVirtualViewId(viewId,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
}
});
} }
public void notifyValueChanged(int viewId, String value) public void notifyValueChanged(int viewId, String value)
{ {
// Send a TYPE_ANNOUNCEMENT event with the new value QtNative.runAction(new Runnable() {
@Override
public void run() {
// Send a TYPE_ANNOUNCEMENT event with the new value
if ((viewId == INVALID_ID) || !m_manager.isEnabled()) { if ((viewId == INVALID_ID) || !m_manager.isEnabled()) {
Log.w(TAG, "notifyValueChanged() for invalid view"); Log.w(TAG, "notifyValueChanged() for invalid view");
return; return;
} }
final ViewGroup group = (ViewGroup)m_view.getParent(); final ViewGroup group = (ViewGroup) m_view.getParent();
if (group == null) { if (group == null) {
Log.w(TAG, "Could not announce value because ViewGroup was null."); Log.w(TAG, "Could not announce value because ViewGroup was null.");
return; return;
} }
final AccessibilityEvent event = final AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT); AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT);
event.setEnabled(true); event.setEnabled(true);
event.setClassName(m_view.getClass().getName() + DEFAULT_CLASS_NAME); event.setClassName(m_view.getClass().getName() + DEFAULT_CLASS_NAME);
event.setContentDescription(value); event.setContentDescription(value);
if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) { if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) {
Log.w(TAG, "No value to announce for " + event.getClassName()); Log.w(TAG, "No value to announce for " + event.getClassName());
return; return;
} }
event.setPackageName(m_view.getContext().getPackageName()); event.setPackageName(m_view.getContext().getPackageName());
event.setSource(m_view, viewId); event.setSource(m_view, viewId);
if (!group.requestSendAccessibilityEvent(m_view, event)) if (!group.requestSendAccessibilityEvent(m_view, event))
Log.w(TAG, "Failed to send value change announcement for " + event.getClassName()); Log.w(TAG, "Failed to send value change announcement for " + event.getClassName());
}
});
} }
public boolean sendEventForVirtualViewId(int virtualViewId, int eventType) public boolean sendEventForVirtualViewId(int virtualViewId, int eventType)

View File

@ -82,8 +82,7 @@ namespace QtAndroidAccessibility
void initialize() void initialize()
{ {
QJniObject::callStaticMethod<void>(QtAndroid::applicationClass(), QtAndroid::qtActivityDelegate().callMethod<void>("initializeAccessibility");
"initializeAccessibility");
} }
bool isActive() bool isActive()

View File

@ -23,8 +23,6 @@ Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
using namespace QtAndroid; using namespace QtAndroid;
Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate")
Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate")
Q_DECLARE_JNI_CLASS(QtLayout, "org/qtproject/qt/android/QtLayout") Q_DECLARE_JNI_CLASS(QtLayout, "org/qtproject/qt/android/QtLayout")
namespace QtAndroidInput namespace QtAndroidInput
@ -96,18 +94,6 @@ namespace QtAndroidInput
g_keyEventListeners()->listeners.removeOne(listener); g_keyEventListeners()->listeners.removeOne(listener);
} }
// FIXME: avoid direct access to QtActivityDelegate
QJniObject qtActivityDelegate()
{
return QtAndroidPrivate::activity().callMethod<QtJniTypes::QtActivityDelegate>(
"getActivityDelegate");
}
QJniObject qtInputDelegate()
{
return qtActivityDelegate().callMethod<QtJniTypes::QtInputDelegate>("getInputDelegate");
}
QJniObject qtLayout() QJniObject qtLayout()
{ {
return qtActivityDelegate().callMethod<QtJniTypes::QtLayout>("getQtLayout"); return qtActivityDelegate().callMethod<QtJniTypes::QtLayout>("getQtLayout");

View File

@ -49,6 +49,9 @@ static jmethodID m_createSurfaceMethodID = nullptr;
static jmethodID m_setSurfaceGeometryMethodID = nullptr; static jmethodID m_setSurfaceGeometryMethodID = nullptr;
static jmethodID m_destroySurfaceMethodID = nullptr; static jmethodID m_destroySurfaceMethodID = nullptr;
static QtJniTypes::QtActivityDelegate m_activityDelegate = nullptr;
static QtJniTypes::QtInputDelegate m_inputDelegate = nullptr;
static int m_pendingApplicationState = -1; static int m_pendingApplicationState = -1;
static QBasicMutex m_platformMutex; static QBasicMutex m_platformMutex;
@ -165,33 +168,52 @@ namespace QtAndroid
QJniObject::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility)); QJniObject::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility));
} }
// FIXME: avoid direct access to QtActivityDelegate
QtJniTypes::QtActivityDelegate qtActivityDelegate()
{
if (!m_activityDelegate.isValid()) {
auto activity = QtAndroidPrivate::activity();
m_activityDelegate = activity.callMethod<QtJniTypes::QtActivityDelegate>(
"getActivityDelegate");
}
return m_activityDelegate;
}
QtJniTypes::QtInputDelegate qtInputDelegate()
{
if (!m_inputDelegate.isValid()) {
m_inputDelegate = qtActivityDelegate().callMethod<QtJniTypes::QtInputDelegate>(
"getInputDelegate");
}
return m_inputDelegate;
}
void notifyAccessibilityLocationChange(uint accessibilityObjectId) void notifyAccessibilityLocationChange(uint accessibilityObjectId)
{ {
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange", qtActivityDelegate().callMethod<void>("notifyLocationChange", accessibilityObjectId);
"(I)V", accessibilityObjectId);
} }
void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId) void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
{ {
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectHide", "(II)V", qtActivityDelegate().callMethod<void>("notifyObjectHide",
accessibilityObjectId, parentObjectId); accessibilityObjectId, parentObjectId);
} }
void notifyObjectFocus(uint accessibilityObjectId) void notifyObjectFocus(uint accessibilityObjectId)
{ {
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId); qtActivityDelegate().callMethod<void>("notifyObjectFocus", accessibilityObjectId);
} }
void notifyValueChanged(uint accessibilityObjectId, jstring value) void notifyValueChanged(uint accessibilityObjectId, jstring value)
{ {
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyValueChanged", qtActivityDelegate().callMethod<void>("notifyValueChanged", accessibilityObjectId, value);
"(ILjava/lang/String;)V", accessibilityObjectId, value);
} }
void notifyScrolledEvent(uint accessibilityObjectId) void notifyScrolledEvent(uint accessibilityObjectId)
{ {
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyScrolledEvent", "(I)V", qtActivityDelegate().callMethod<void>("notifyScrolledEvent", accessibilityObjectId);
accessibilityObjectId);
} }
void notifyQtAndroidPluginRunning(bool running) void notifyQtAndroidPluginRunning(bool running)

View File

@ -12,6 +12,7 @@
#include <QImage> #include <QImage>
#include <private/qjnihelpers_p.h> #include <private/qjnihelpers_p.h>
#include <QtCore/QJniObject>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -25,6 +26,9 @@ class QWindow;
class AndroidSurfaceClient; class AndroidSurfaceClient;
class QBasicMutex; class QBasicMutex;
Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate")
Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate")
namespace QtAndroid namespace QtAndroid
{ {
QBasicMutex *platformInterfaceMutex(); QBasicMutex *platformInterfaceMutex();
@ -32,7 +36,6 @@ namespace QtAndroid
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration); void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration);
void setQtThread(QThread *thread); void setQtThread(QThread *thread);
int createSurface(AndroidSurfaceClient * client, const QRect &geometry, bool onTop, int imageDepth); int createSurface(AndroidSurfaceClient * client, const QRect &geometry, bool onTop, int imageDepth);
int insertNativeView(jobject view, const QRect &geometry); int insertNativeView(jobject view, const QRect &geometry);
void setViewVisibility(jobject view, bool visible); void setViewVisibility(jobject view, bool visible);
@ -51,6 +54,9 @@ namespace QtAndroid
AAssetManager *assetManager(); AAssetManager *assetManager();
jclass applicationClass(); jclass applicationClass();
QtJniTypes::QtActivityDelegate qtActivityDelegate();
QtJniTypes::QtInputDelegate qtInputDelegate();
// Keep synchronized with flags in ActivityDelegate.java // Keep synchronized with flags in ActivityDelegate.java
enum SystemUiVisibility { enum SystemUiVisibility {
SYSTEM_UI_VISIBILITY_NORMAL = 0, SYSTEM_UI_VISIBILITY_NORMAL = 0,