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 Pick-to: 6.3 6.2 5.15 Change-Id: Ic44abd5f01b9b6f5468962131466edaf6a49d498 Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io> Reviewed-by: Rami Potinkara <rami.potinkara@qt.io>
This commit is contained in:
parent
e5ba838045
commit
b238f83380
@ -921,6 +921,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;
|
||||
|
@ -998,6 +998,18 @@ public class QtNative
|
||||
});
|
||||
}
|
||||
|
||||
private static void notifyValueChanged(int viewId, 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);
|
||||
|
@ -225,6 +225,41 @@ 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()) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1148,6 +1148,11 @@ 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();
|
||||
|
@ -149,6 +149,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);
|
||||
@ -345,6 +353,27 @@ 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);
|
||||
QJniEnvironment env;
|
||||
jstring jstr = env->NewString((jchar*)value.constData(), (jsize)value.size());
|
||||
if (env.checkAndClearExceptions())
|
||||
__android_log_print(ANDROID_LOG_WARN, m_qtTag, "Failed to create jstring");
|
||||
return jstr;
|
||||
}
|
||||
|
||||
static QString descriptionForInterface(QAccessibleInterface *iface)
|
||||
{
|
||||
@ -356,9 +385,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ namespace QtAndroidAccessibility
|
||||
void notifyLocationChange();
|
||||
void notifyObjectHide(uint accessibilityObjectId);
|
||||
void notifyObjectFocus(uint accessibilityObjectId);
|
||||
void notifyValueChanged(uint accessibilityObjectId);
|
||||
void createAccessibilityContextObject(QObject *parent);
|
||||
}
|
||||
|
||||
|
@ -226,6 +226,12 @@ namespace QtAndroid
|
||||
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId);
|
||||
}
|
||||
|
||||
void notifyValueChanged(uint accessibilityObjectId, jstring value)
|
||||
{
|
||||
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyValueChanged",
|
||||
"(ILjava/lang/String;)V", accessibilityObjectId, value);
|
||||
}
|
||||
|
||||
void notifyQtAndroidPluginRunning(bool running)
|
||||
{
|
||||
QJniObject::callStaticMethod<void>(m_applicationClass, "notifyQtAndroidPluginRunning","(Z)V", running);
|
||||
|
@ -103,6 +103,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();
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user