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()
{
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)

View File

@ -239,7 +239,7 @@ public class QtNative
updateApplicationState(state);
}
static void runAction(Runnable action)
public static void runAction(Runnable action)
{
synchronized (m_mainActivityMutex) {
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)
{
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)
{
runAction(new Runnable() {

View File

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

View File

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

View File

@ -23,8 +23,6 @@ Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
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")
namespace QtAndroidInput
@ -96,18 +94,6 @@ namespace QtAndroidInput
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()
{
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_destroySurfaceMethodID = nullptr;
static QtJniTypes::QtActivityDelegate m_activityDelegate = nullptr;
static QtJniTypes::QtInputDelegate m_inputDelegate = nullptr;
static int m_pendingApplicationState = -1;
static QBasicMutex m_platformMutex;
@ -165,33 +168,52 @@ namespace QtAndroid
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)
{
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange",
"(I)V", accessibilityObjectId);
qtActivityDelegate().callMethod<void>("notifyLocationChange", accessibilityObjectId);
}
void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
{
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectHide", "(II)V",
accessibilityObjectId, parentObjectId);
qtActivityDelegate().callMethod<void>("notifyObjectHide",
accessibilityObjectId, parentObjectId);
}
void notifyObjectFocus(uint accessibilityObjectId)
{
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId);
qtActivityDelegate().callMethod<void>("notifyObjectFocus", accessibilityObjectId);
}
void notifyValueChanged(uint accessibilityObjectId, jstring value)
{
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyValueChanged",
"(ILjava/lang/String;)V", accessibilityObjectId, value);
qtActivityDelegate().callMethod<void>("notifyValueChanged", accessibilityObjectId, value);
}
void notifyScrolledEvent(uint accessibilityObjectId)
{
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyScrolledEvent", "(I)V",
accessibilityObjectId);
qtActivityDelegate().callMethod<void>("notifyScrolledEvent", accessibilityObjectId);
}
void notifyQtAndroidPluginRunning(bool running)

View File

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