summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2022-02-10 11:53:24 +0100
committerIvan Solovev <ivan.solovev@qt.io>2022-02-22 15:49:08 +0100
commitf546e3e7c98f1aced03ebf1f467ac1a04c579824 (patch)
treef61fe0e4d2fffc75f40cbb75308b54df3f1a518f
parent9a163f038cd4fcadd77518bddfec8601adf076e1 (diff)
downloadqtbase-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)
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java7
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/QtNative.java12
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java27
-rw-r--r--src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java1
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java6
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp35
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.h1
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp7
-rw-r--r--src/plugins/platforms/android/androidjnimain.h1
-rw-r--r--src/plugins/platforms/android/qandroidplatformaccessibility.cpp2
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());
}
}