diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2022-02-10 11:53:24 +0100 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2022-02-22 15:49:08 +0100 |
commit | f546e3e7c98f1aced03ebf1f467ac1a04c579824 (patch) | |
tree | f61fe0e4d2fffc75f40cbb75308b54df3f1a518f | |
parent | 9a163f038cd4fcadd77518bddfec8601adf076e1 (diff) | |
download | qtbase-f546e3e7c98f1aced03ebf1f467ac1a04c579824.tar.gz |
Android A11Y: handle valueChanged events
Before this patch Android A11Y implementation was missing
ValueChanged event handling. As a result, no update was given
when the element's value was changed.
Handling these events allows us to announce value changes on such
objects like Slider, SpinBox, etc...
This is a universal method of value-change announcement, so it
supports all sorts of A11Y gestures.
On the Java side a new function was introduced to announce the
values, because we need to use the actual element's *value*,
not its accessible name or description.
Task-number: QTBUG-93396
Change-Id: Ic44abd5f01b9b6f5468962131466edaf6a49d498
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
Reviewed-by: Rami Potinkara <rami.potinkara@qt.io>
(cherry picked from commit b238f83380dcaa2830999a8f413f4b648db80beb)
10 files changed, 96 insertions, 3 deletions
diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java index 21b6f8f0e4..f7e97930ae 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -900,6 +900,13 @@ public class QtActivityDelegate m_accessibilityDelegate.notifyObjectFocus(viewId); } + public void notifyValueChanged(int viewId, String value) + { + if (m_accessibilityDelegate == null) + return; + m_accessibilityDelegate.notifyValueChanged(viewId, value); + } + public void notifyQtAndroidPluginRunning(boolean running) { m_isPluginRunning = running; diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java index b187e837c1..6742a3781b 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -974,6 +974,18 @@ public class QtNative }); } + private static void notifyValueChanged(final int viewId, final String value) + { + runAction(new Runnable() { + @Override + public void run() { + if (m_activityDelegate != null) { + m_activityDelegate.notifyValueChanged(viewId, value); + } + } + }); + } + public static void notifyQtAndroidPluginRunning(final boolean running) { m_activityDelegate.notifyQtAndroidPluginRunning(running); diff --git a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java index d9a4908298..90e38231fc 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java @@ -225,6 +225,33 @@ public class QtAccessibilityDelegate extends View.AccessibilityDelegate AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); } + public void notifyValueChanged(int viewId, String value) + { + // Send a TYPE_ANNOUNCEMENT event with the new value + 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 AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT); + event.setEnabled(true); + event.setClassName(m_view.getClass().getName() + DEFAULT_CLASS_NAME); + event.setContentDescription(value); + 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); + 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) { if ((virtualViewId == INVALID_ID) || !m_manager.isEnabled()) { diff --git a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java index a83174377d..448b29a98e 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java +++ b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java @@ -55,4 +55,5 @@ class QtNativeAccessibility static native boolean scrollBackward(int objectId); static native boolean populateNode(int objectId, AccessibilityNodeInfo node); + static native String valueForAccessibleObject(int objectId); } diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java index 3abda60152..5b3e330282 100644 --- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -1136,6 +1136,12 @@ public class QtActivity extends Activity { QtNative.activityDelegate().notifyObjectFocus(viewId); } + + public void notifyValueChanged(int viewId, String value) + { + QtNative.activityDelegate().notifyValueChanged(viewId, value); + } + public boolean isKeyboardVisible() { return QtNative.activityDelegate().isKeyboardVisible(); diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index 4b6f1f0f02..c306390a08 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/androidjniaccessibility.cpp @@ -151,6 +151,14 @@ namespace QtAndroidAccessibility QtAndroid::notifyObjectFocus(accessibilityObjectId); } + static jstring jvalueForAccessibleObject(int objectId); // forward declaration + + void notifyValueChanged(uint accessibilityObjectId) + { + jstring value = jvalueForAccessibleObject(accessibilityObjectId); + QtAndroid::notifyValueChanged(accessibilityObjectId, value); + } + static QVarLengthArray<int, 8> childIdListForAccessibleObject_helper(int objectId) { QAccessibleInterface *iface = interfaceFromId(objectId); @@ -347,6 +355,29 @@ if (!clazz) { \ //__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); + static QString textFromValue(QAccessibleInterface *iface) + { + QString valueStr; + QAccessibleValueInterface *valueIface = iface->valueInterface(); + if (valueIface) { + // TODO: fix double-to-string conversion + valueStr = valueIface->currentValue().toString(); + } + return valueStr; + } + + static jstring jvalueForAccessibleObject(int objectId) + { + QAccessibleInterface *iface = interfaceFromId(objectId); + const QString value = textFromValue(iface); + QJNIEnvironmentPrivate env; + jstring jstr = env->NewString((jchar*)value.constData(), (jsize)value.size()); +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif // QT_DEBUG + env->ExceptionClear(); + return jstr; + } static QString descriptionForInterface(QAccessibleInterface *iface) { @@ -358,9 +389,7 @@ if (!clazz) { \ if (desc.isEmpty()) { desc = iface->text(QAccessible::Value); if (desc.isEmpty()) { - if (QAccessibleValueInterface *valueIface = iface->valueInterface()) { - desc= valueIface->currentValue().toString(); - } + desc = textFromValue(iface); } } } diff --git a/src/plugins/platforms/android/androidjniaccessibility.h b/src/plugins/platforms/android/androidjniaccessibility.h index fa2ff6bf52..484786c649 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.h +++ b/src/plugins/platforms/android/androidjniaccessibility.h @@ -54,6 +54,7 @@ namespace QtAndroidAccessibility void notifyLocationChange(); void notifyObjectHide(uint accessibilityObjectId); void notifyObjectFocus(uint accessibilityObjectId); + void notifyValueChanged(uint accessibilityObjectId); void createAccessibilityContextObject(QObject *parent); } diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 5238292942..f8f28c61ca 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -241,6 +241,13 @@ namespace QtAndroid QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId); } + void notifyValueChanged(uint accessibilityObjectId, jstring value) + { + QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyValueChanged", + "(ILjava/lang/String;)V", accessibilityObjectId, + value); + } + void notifyQtAndroidPluginRunning(bool running) { QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyQtAndroidPluginRunning","(Z)V", running); diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h index 3beb1ac215..2fbb6633ab 100644 --- a/src/plugins/platforms/android/androidjnimain.h +++ b/src/plugins/platforms/android/androidjnimain.h @@ -98,6 +98,7 @@ namespace QtAndroid void notifyAccessibilityLocationChange(); void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId); void notifyObjectFocus(uint accessibilityObjectId); + void notifyValueChanged(uint accessibilityObjectId, jstring value); void notifyQtAndroidPluginRunning(bool running); const char *classErrorMsgFmt(); diff --git a/src/plugins/platforms/android/qandroidplatformaccessibility.cpp b/src/plugins/platforms/android/qandroidplatformaccessibility.cpp index 2d4c7294eb..73f3a54e65 100644 --- a/src/plugins/platforms/android/qandroidplatformaccessibility.cpp +++ b/src/plugins/platforms/android/qandroidplatformaccessibility.cpp @@ -66,6 +66,8 @@ void QAndroidPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent * QtAndroidAccessibility::notifyObjectHide(event->uniqueId()); } else if (event->type() == QAccessible::Focus) { QtAndroidAccessibility::notifyObjectFocus(event->uniqueId()); + } else if (event->type() == QAccessible::ValueChanged) { + QtAndroidAccessibility::notifyValueChanged(event->uniqueId()); } } |