From 0f3dbd6dc7d01830bdb78be9bc50169fd1ec5ba3 Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Wed, 20 Sep 2023 17:01:00 +0200 Subject: [PATCH] Android: remove a11y methods from QtNative, call a11y delegate direct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ä --- .../qt/android/QtActivityDelegate.java | 9 +- .../org/qtproject/qt/android/QtNative.java | 73 +---------- .../QtAccessibilityDelegate.java | 116 +++++++++++------- .../android/androidjniaccessibility.cpp | 3 +- .../platforms/android/androidjniinput.cpp | 14 --- .../platforms/android/androidjnimain.cpp | 40 ++++-- .../platforms/android/androidjnimain.h | 8 +- 7 files changed, 119 insertions(+), 144 deletions(-) diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java index 019b9244f1..4c9d61befb 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java @@ -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) diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java index f37228c562..78c19c5ed7 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java @@ -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() { diff --git a/src/android/jar/src/org/qtproject/qt/android/accessibility/QtAccessibilityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/accessibility/QtAccessibilityDelegate.java index 74d5ce5dde..35cfcdb31f 100644 --- a/src/android/jar/src/org/qtproject/qt/android/accessibility/QtAccessibilityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/accessibility/QtAccessibilityDelegate.java @@ -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) diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index 38e8d6dfd0..119e0c9dea 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/androidjniaccessibility.cpp @@ -82,8 +82,7 @@ namespace QtAndroidAccessibility void initialize() { - QJniObject::callStaticMethod(QtAndroid::applicationClass(), - "initializeAccessibility"); + QtAndroid::qtActivityDelegate().callMethod("initializeAccessibility"); } bool isActive() diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index 2c4c95e787..0262f69c5c 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -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( - "getActivityDelegate"); - } - - QJniObject qtInputDelegate() - { - return qtActivityDelegate().callMethod("getInputDelegate"); - } - QJniObject qtLayout() { return qtActivityDelegate().callMethod("getQtLayout"); diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index bd0f2d5ce3..9f44a2ffe0 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -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(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( + "getActivityDelegate"); + } + + return m_activityDelegate; + } + + QtJniTypes::QtInputDelegate qtInputDelegate() + { + if (!m_inputDelegate.isValid()) { + m_inputDelegate = qtActivityDelegate().callMethod( + "getInputDelegate"); + } + + return m_inputDelegate; + } + void notifyAccessibilityLocationChange(uint accessibilityObjectId) { - QJniObject::callStaticMethod(m_applicationClass, "notifyAccessibilityLocationChange", - "(I)V", accessibilityObjectId); + qtActivityDelegate().callMethod("notifyLocationChange", accessibilityObjectId); } void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId) { - QJniObject::callStaticMethod(m_applicationClass, "notifyObjectHide", "(II)V", - accessibilityObjectId, parentObjectId); + qtActivityDelegate().callMethod("notifyObjectHide", + accessibilityObjectId, parentObjectId); } void notifyObjectFocus(uint accessibilityObjectId) { - QJniObject::callStaticMethod(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId); + qtActivityDelegate().callMethod("notifyObjectFocus", accessibilityObjectId); } void notifyValueChanged(uint accessibilityObjectId, jstring value) { - QJniObject::callStaticMethod(m_applicationClass, "notifyValueChanged", - "(ILjava/lang/String;)V", accessibilityObjectId, value); + qtActivityDelegate().callMethod("notifyValueChanged", accessibilityObjectId, value); } void notifyScrolledEvent(uint accessibilityObjectId) { - QJniObject::callStaticMethod(m_applicationClass, "notifyScrolledEvent", "(I)V", - accessibilityObjectId); + qtActivityDelegate().callMethod("notifyScrolledEvent", accessibilityObjectId); } void notifyQtAndroidPluginRunning(bool running) diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h index 6a52a4e319..5765443190 100644 --- a/src/plugins/platforms/android/androidjnimain.h +++ b/src/plugins/platforms/android/androidjnimain.h @@ -12,6 +12,7 @@ #include #include +#include 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,