Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
androidjniaccessibility.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
10#include "qpa/qplatformaccessibility.h"
11#include <QtGui/private/qaccessiblebridgeutils_p.h>
13#include "qwindow.h"
14#include "qrect.h"
15#include "QtGui/qaccessible.h"
16#include <QtCore/qmath.h>
17#include <QtCore/private/qjnihelpers_p.h>
18#include <QtCore/QJniObject>
19#include <QtGui/private/qhighdpiscaling_p.h>
20
21#include <QtCore/QObject>
22#include <QtCore/qpointer.h>
23#include <QtCore/qscopeguard.h>
24#include <QtCore/qvarlengtharray.h>
25
26#include <atomic>
27
28static const char m_qtTag[] = "Qt A11Y";
29
30QT_BEGIN_NAMESPACE
31
32using namespace Qt::StringLiterals;
33
35{
62
63 static int RANGE_TYPE_INT = 0;
64 static int RANGE_TYPE_FLOAT = 0;
65 static int RANGE_TYPE_PERCENT = 0;
67
70 static int EXPANDED_STATE_FULL = 0;
71
72 static int ACTION_COLLAPSE = 0;
73 static int ACTION_EXPAND = 0;
74
75 static std::atomic<bool> m_accessibilityActivated = false;
76
78
79 // This object is needed to schedule the execution of the code that
80 // deals with accessibility instances to the Qt main thread.
81 // Because of that almost every method here is split into two parts.
82 // The _helper part is executed in the context of m_accessibilityContext
83 // on the main thread. The other part is executed in Java thread.
85
86 // This method is called from the Qt main thread, and normally a
87 // QGuiApplication instance will be used as a parent.
89 {
90 if (m_accessibilityContext)
91 m_accessibilityContext->deleteLater();
92 m_accessibilityContext = new QObject(parent);
93 }
94
95 template <typename Func, typename Ret>
96 void runInObjectContext(QObject *context, Func &&func, Ret *retVal)
97 {
99 __android_log_print(ANDROID_LOG_WARN, m_qtTag,
100 "Could not run accessibility call in object context, no valid surface.");
101 return;
102 }
103
104 QtAndroidPrivate::AndroidDeadlockProtector protector(
105 u"QtAndroidAccessibility::runInObjectContext()"_s);
106 if (!protector.acquire()) {
107 __android_log_print(ANDROID_LOG_WARN, m_qtTag,
108 "Could not run accessibility call in object context, accessing "
109 "main thread could lead to deadlock");
110 return;
111 }
112
113 if (!QtAndroid::blockEventLoopsWhenSuspended()
114 || QGuiApplication::applicationState() != Qt::ApplicationSuspended) {
115 QMetaObject::invokeMethod(context, func, Qt::BlockingQueuedConnection, retVal);
116 } else {
117 __android_log_print(ANDROID_LOG_WARN, m_qtTag,
118 "Could not run accessibility call in object context, event loop suspended.");
119 }
120 }
121
122 bool isActive()
123 {
125 }
126
127 static void setActive(JNIEnv */*env*/, jobject /*thiz*/, jboolean active)
128 {
130
131 if (!m_accessibilityContext) {
132 __android_log_print(ANDROID_LOG_DEBUG, m_qtTag,
133 "Accessibility context is not ready, activation deferred.");
134 return;
135 }
136
137 QMetaObject::invokeMethod(m_accessibilityContext, [active]() {
138 QMutexLocker lock(QtAndroid::platformInterfaceMutex());
139
140 if (!active)
141 m_modalInterfaces.clear();
142
143 auto *platformIntegration = QtAndroid::androidPlatformIntegration();
144 if (!platformIntegration) {
145 __android_log_print(ANDROID_LOG_DEBUG, m_qtTag,
146 "Android platform integration is not ready, accessibility activation deferred.");
147 return;
148 }
149 platformIntegration->accessibility()->setActive(active);
150 }, Qt::QueuedConnection);
151 }
152
153 QAccessibleInterface *interfaceFromId(jint objectId)
154 {
155 QAccessibleInterface *iface = nullptr;
156 if (objectId == -1) {
157 QWindow *win = qApp->focusWindow();
158 if (win)
159 iface = win->accessibleRoot();
160 } else {
161 iface = QAccessible::accessibleInterface(objectId);
162 }
163 return iface;
164 }
165
166 void notifyLocationChange(uint accessibilityObjectId)
167 {
168 QtAndroid::notifyAccessibilityLocationChange(accessibilityObjectId);
169 }
170
171 static int parentId_helper(int objectId); // forward declaration
172
173 void notifyObjectHide(uint accessibilityObjectId)
174 {
175 m_modalInterfaces.removeAll(accessibilityObjectId);
176
177 const auto parentObjectId = parentId_helper(accessibilityObjectId);
178 QtAndroid::notifyObjectHide(accessibilityObjectId, parentObjectId);
179 }
180
181 void notifyObjectShow(uint accessibilityObjectId)
182 {
183 QAccessibleInterface *iface = interfaceFromId(accessibilityObjectId);
184 if (iface && iface->isValid() && iface->state().modal
185 && !m_modalInterfaces.contains(accessibilityObjectId)) {
186 m_modalInterfaces << accessibilityObjectId;
187 }
188
189 const auto parentObjectId = parentId_helper(accessibilityObjectId);
190 QtAndroid::notifyObjectShow(parentObjectId);
191 }
192
193 void notifyObjectFocus(uint accessibilityObjectId)
194 {
195 QtAndroid::notifyObjectFocus(accessibilityObjectId);
196 }
197
198 void notifyObjectDestroyed(uint accessibilityObjectId)
199 {
200 m_modalInterfaces.removeAll(accessibilityObjectId);
201 QtAndroid::notifyObjectDestroyed(accessibilityObjectId,
202 parentId_helper(accessibilityObjectId));
203 }
204
205 void notifyObjectCreated(uint accessibilityObjectId)
206 {
207 QtAndroid::notifyObjectCreated(parentId_helper(accessibilityObjectId));
208 }
209
210 void notifyValueChanged(uint accessibilityObjectId)
211 {
212 QtAndroid::notifyValueChanged(accessibilityObjectId);
213 }
214
215 void notifyDescriptionOrNameChanged(uint accessibilityObjectId)
216 {
217 QtAndroid::notifyDescriptionOrNameChanged(accessibilityObjectId);
218 }
219
220 void notifyScrolledEvent(uint accessiblityObjectId)
221 {
222 QtAndroid::notifyScrolledEvent(accessiblityObjectId);
223 }
224
225 void notifyAnnouncementEvent(uint accessibilityObjectId, const QString &message)
226 {
227 QtAndroid::notifyAnnouncementEvent(accessibilityObjectId, message);
228 }
229
231 {
232 QAccessibleInterface *iface = interfaceFromId(objectId);
233 if (iface && iface->isValid()) {
234 const int childCount = iface->childCount();
235 QVarLengthArray<jint, 8> ifaceIdArray;
236 ifaceIdArray.reserve(childCount);
237 for (int i = 0; i < childCount; ++i) {
238 QAccessibleInterface *child = iface->child(i);
239 if (child && child->isValid())
240 ifaceIdArray.append(QAccessible::uniqueId(child));
241 }
242 return ifaceIdArray;
243 }
244 return {};
245 }
246
247 static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
248 {
249 if (m_accessibilityContext) {
250 QVarLengthArray<jint, 8> ifaceIdArray;
251 runInObjectContext(m_accessibilityContext, [objectId]() {
252 return childIdListForAccessibleObject_helper(objectId);
253 }, &ifaceIdArray);
254 jintArray jArray = env->NewIntArray(jsize(ifaceIdArray.count()));
255 env->SetIntArrayRegion(jArray, 0, ifaceIdArray.count(), ifaceIdArray.data());
256 return jArray;
257 }
258
259 return env->NewIntArray(jsize(0));
260 }
261
262 static int parentId_helper(int objectId)
263 {
264 QAccessibleInterface *iface = interfaceFromId(objectId);
265 if (iface && iface->isValid()) {
266 QAccessibleInterface *parent = iface->parent();
267 if (parent && parent->isValid()) {
268 if (parent->role() == QAccessible::Application)
269 return -1;
270 return QAccessible::uniqueId(parent);
271 }
272 }
273 return -1;
274 }
275
276 static jint parentId(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
277 {
278 jint result = -1;
279 if (m_accessibilityContext) {
280 runInObjectContext(m_accessibilityContext, [objectId]() {
281 return parentId_helper(objectId);
282 }, &result);
283 }
284 return result;
285 }
286
287 static QRect screenRect_helper(int objectId, bool clip = true)
288 {
289 QRect rect;
290 QAccessibleInterface *iface = interfaceFromId(objectId);
291 if (iface && iface->isValid()) {
292 rect = QHighDpi::toNativePixels(iface->rect(), iface->window());
293 }
294 // If the widget is not fully in-bound in its parent then we have to clip the rectangle to draw
295 if (clip && iface && iface->parent() && iface->parent()->isValid()) {
296 const auto parentRect = QHighDpi::toNativePixels(iface->parent()->rect(), iface->parent()->window());
297 rect = rect.intersected(parentRect);
298 }
299 return rect;
300 }
301
302 static jobject screenRect(JNIEnv *env, jobject /*thiz*/, jint objectId)
303 {
304 QRect rect;
305 if (m_accessibilityContext) {
306 runInObjectContext(m_accessibilityContext, [objectId]() {
307 return screenRect_helper(objectId);
308 }, &rect);
309 }
310 jclass rectClass = env->FindClass("android/graphics/Rect");
311 jmethodID ctor = env->GetMethodID(rectClass, "<init>", "(IIII)V");
312 jobject jrect = env->NewObject(rectClass, ctor, rect.left(), rect.top(), rect.right(), rect.bottom());
313 return jrect;
314 }
315
316 static int hitTest_helper(float x, float y)
317 {
318 QAccessibleInterface *root = interfaceFromId(-1);
319 if (root && root->isValid()) {
320 QPoint pos = QHighDpi::fromNativePixels(QPoint(int(x), int(y)), root->window());
321
322 QAccessibleInterface *child = root->childAt(pos.x(), pos.y());
323 QAccessibleInterface *lastChild = nullptr;
324 while (child && (child != lastChild)) {
325 lastChild = child;
326 child = child->childAt(pos.x(), pos.y());
327 }
328 if (lastChild)
329 return QAccessible::uniqueId(lastChild);
330 }
331 return -1;
332 }
333
334 static jint hitTest(JNIEnv */*env*/, jobject /*thiz*/, jfloat x, jfloat y)
335 {
336 jint result = -1;
337 if (m_accessibilityContext) {
338 runInObjectContext(m_accessibilityContext, [x, y]() {
339 return hitTest_helper(x, y);
340 }, &result);
341 }
342 return result;
343 }
344
345 static void invokeActionOnInterfaceInMainThread(QAccessibleActionInterface* actionInterface,
346 const QString& action)
347 {
348 // Queue the action and return back to Java thread, so that we do not
349 // block it for too long
350 QMetaObject::invokeMethod(qApp, [actionInterface, action]() {
351 actionInterface->doAction(action);
352 }, Qt::QueuedConnection);
353 }
354
355 static bool clickAction_helper(int objectId)
356 {
357 QAccessibleInterface *iface = interfaceFromId(objectId);
358 if (!iface || !iface->isValid() || !iface->actionInterface())
359 return false;
360
361 const auto& actionNames = iface->actionInterface()->actionNames();
362
363 if (actionNames.contains(QAccessibleActionInterface::pressAction())) {
364 invokeActionOnInterfaceInMainThread(iface->actionInterface(),
365 QAccessibleActionInterface::pressAction());
366 } else if (actionNames.contains(QAccessibleActionInterface::toggleAction())) {
367 invokeActionOnInterfaceInMainThread(iface->actionInterface(),
368 QAccessibleActionInterface::toggleAction());
369 } else {
370 return false;
371 }
372 return true;
373 }
374
375 static bool focusAction_helper(int objectId)
376 {
377 QAccessibleInterface *iface = interfaceFromId(objectId);
378 if (!iface || !iface->isValid() || !iface->actionInterface())
379 return false;
380
381 const auto& actionNames = iface->actionInterface()->actionNames();
382
383 if (actionNames.contains(QAccessibleActionInterface::setFocusAction())) {
384 QAccessibleActionInterface *actionInterface = iface->actionInterface();
385 // Suppress keyboard activation during accessibility focus navigation.
386 QMetaObject::invokeMethod(qApp, [actionInterface]() {
387 auto *inputContext = QAndroidInputContext::androidInputContext();
388 if (inputContext)
389 inputContext->setAccessibilityFocusInProgress(true);
390 const auto resetGuard = qScopeGuard([inputContext] {
391 if (inputContext)
392 inputContext->setAccessibilityFocusInProgress(false);
393 });
394 actionInterface->doAction(QAccessibleActionInterface::setFocusAction());
395 }, Qt::QueuedConnection);
396 return true;
397 }
398 return false;
399 }
400
401 static jboolean clickAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
402 {
403 bool result = false;
404 if (m_accessibilityContext) {
405 runInObjectContext(m_accessibilityContext, [objectId]() {
406 return clickAction_helper(objectId);
407 }, &result);
408 }
409 return result;
410 }
411
412 static jboolean focusAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
413 {
414 bool result = false;
415 if (m_accessibilityContext) {
416 runInObjectContext(m_accessibilityContext, [objectId]() {
417 return focusAction_helper(objectId);
418 }, &result);
419 }
420 return result;
421 }
422
423 static bool scroll_helper(int objectId, const QString &actionName)
424 {
425 QAccessibleInterface *iface = interfaceFromId(objectId);
426 if (iface && iface->isValid())
427 return QAccessibleBridgeUtils::performEffectiveAction(iface, actionName);
428 return false;
429 }
430
431 static jboolean scrollForward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
432 {
433 bool result = false;
434
435 const auto& ids = childIdListForAccessibleObject_helper(objectId);
436 if (ids.isEmpty())
437 return false;
438
439 const int firstChildId = ids.first();
440 const QRect oldPosition = screenRect_helper(firstChildId, false);
441
442 if (m_accessibilityContext) {
443 runInObjectContext(m_accessibilityContext, [objectId]() {
444 return scroll_helper(objectId, QAccessibleActionInterface::increaseAction());
445 }, &result);
446 }
447
448 // Don't check for position change if the call was not successful
449 return result && oldPosition != screenRect_helper(firstChildId, false);
450 }
451
452 static jboolean scrollBackward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
453 {
454 bool result = false;
455
456 const auto& ids = childIdListForAccessibleObject_helper(objectId);
457 if (ids.isEmpty())
458 return false;
459
460 const int firstChildId = ids.first();
461 const QRect oldPosition = screenRect_helper(firstChildId, false);
462
463 if (m_accessibilityContext) {
464 runInObjectContext(m_accessibilityContext, [objectId]() {
465 return scroll_helper(objectId, QAccessibleActionInterface::decreaseAction());
466 }, &result);
467 }
468
469 // Don't check for position change if the call was not successful
470 return result && oldPosition != screenRect_helper(firstChildId, false);
471 }
472
473 static bool showOnScreen_helper(int objectId)
474 {
475 QAccessibleInterface *iface = interfaceFromId(objectId);
476 if (!iface || !iface->isValid() || !iface->actionInterface())
477 return false;
478
479 const auto actionNames = iface->actionInterface()->actionNames();
480
481 if (actionNames.contains(QAccessibleActionInterface::showOnScreenAction())) {
482 invokeActionOnInterfaceInMainThread(iface->actionInterface(), QAccessibleActionInterface::showOnScreenAction());
483 return true;
484 }
485 return false;
486 }
487
488 static jboolean showOnScreen(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
489 {
490 bool result = false;
491 if (m_accessibilityContext) {
492 runInObjectContext(m_accessibilityContext, [objectId]() {
493 return showOnScreen_helper(objectId);
494 }, &result);
495 }
496 return result;
497 }
498
499 static jboolean expand(JNIEnv * /*env*/, jobject /*thiz*/, jint objectId)
500 {
501 bool result = false;
502 if (m_accessibilityContext) {
503 runInObjectContext(
504 m_accessibilityContext, [objectId]() { return clickAction_helper(objectId); },
505 &result);
506 }
507 return result;
508 }
509
510 static jboolean collapse(JNIEnv * /*env*/, jobject /*thiz*/, jint objectId)
511 {
512 bool result = false;
513 if (m_accessibilityContext) {
514 runInObjectContext(
515 m_accessibilityContext, [objectId]() { return clickAction_helper(objectId); },
516 &result);
517 }
518 return result;
519 }
520
521 static QString textFromValue(QAccessibleInterface *iface)
522 {
523 QString valueStr;
524 QAccessibleValueInterface *valueIface = iface->valueInterface();
525 if (valueIface) {
526 const QVariant valueVar = valueIface->currentValue();
527 const auto type = valueVar.typeId();
528 if (type == QMetaType::Double || type == QMetaType::Float) {
529 // QVariant's toString() formats floating-point values with
530 // FloatingPointShortest, which is not an accessible
531 // representation; nor, in many cases, is it suitable to the UI
532 // element whose value we're looking at. So roll our own
533 // A11Y-friendly conversion to string.
534 const double val = valueVar.toDouble();
535 // Try to use minimumStepSize() to determine precision
536 bool stepIsValid = false;
537 const double step = qAbs(valueIface->minimumStepSize().toDouble(&stepIsValid));
538 if (!stepIsValid || qFuzzyIsNull(step)) {
539 // Ignore step, use default precision
540 valueStr = qFuzzyIsNull(val) ? u"0"_s : QString::number(val, 'f');
541 } else {
542 const int precision = [](double s) {
543 int count = 0;
544 while (s < 1. && !qFuzzyCompare(s, 1.)) {
545 ++count;
546 s *= 10;
547 }
548 // If s is now 1.25, we want to show some more digits,
549 // but don't want to get silly with a step like 1./7;
550 // so only include a few extra digits.
551 const int stop = count + 3;
552 const auto fractional = [](double v) {
553 double whole = 0.0;
554 std::modf(v + 0.5, &whole);
555 return qAbs(v - whole);
556 };
557 s = fractional(s);
558 while (count < stop && !qFuzzyIsNull(s)) {
559 ++count;
560 s = fractional(s * 10);
561 }
562 return count;
563 }(step);
564 valueStr = qFuzzyIsNull(val / step) ? u"0"_s
565 : QString::number(val, 'f', precision);
566 }
567 } else {
568 valueStr = valueVar.toString();
569 }
570 }
571 return valueStr;
572 }
573
574 static QString classNameForRole(QAccessible::Role role, QAccessible::State state) {
575 switch (role) {
576 case QAccessible::Role::Button:
577 case QAccessible::Role::Link:
578 {
579 if (state.checkable)
580 return QStringLiteral("android.widget.ToggleButton");
581 return QStringLiteral("android.widget.Button");
582 }
583 case QAccessible::Role::CheckBox:
584 // As of android/accessibility/utils/Role.java::getRole a CheckBox
585 // is NOT android.widget.CheckBox
586 return QStringLiteral("android.widget.CompoundButton");
587 case QAccessible::Role::Switch:
588 return QStringLiteral("android.widget.Switch");
589 case QAccessible::Role::Clock:
590 return QStringLiteral("android.widget.TextClock");
591 case QAccessible::Role::ComboBox:
592 case QAccessible::Role::ButtonDropDown:
593 return QStringLiteral("android.widget.Spinner");
594 case QAccessible::Role::Graphic:
595 // QQuickImage does not provide this role it inherits Client from QQuickItem
596 return QStringLiteral("android.widget.ImageView");
597 case QAccessible::Role::Grouping:
598 return QStringLiteral("android.view.ViewGroup");
599 case QAccessible::Role::List:
600 // As of android/accessibility/utils/Role.java::getRole a List
601 // is NOT android.widget.ListView
602 return QStringLiteral("android.widget.AbsListView");
603 case QAccessible::Role::MenuItem:
604 return QStringLiteral("android.view.MenuItem");
605 case QAccessible::Role::PopupMenu:
606 return QStringLiteral("android.widget.PopupMenu");
607 case QAccessible::Role::Separator:
608 return QStringLiteral("android.widget.Space");
609 case QAccessible::Role::ToolBar:
610 return QStringLiteral("android.view.Toolbar");
611 case QAccessible::Role::Heading: [[fallthrough]];
612 case QAccessible::Role::StaticText:
613 // Heading vs. regular Text is finally determined by AccessibilityNodeInfo.isHeading()
614 return QStringLiteral("android.widget.TextView");
615 case QAccessible::Role::EditableText:
616 return QStringLiteral("android.widget.EditText");
617 case QAccessible::Role::RadioButton:
618 return QStringLiteral("android.widget.RadioButton");
619 case QAccessible::Role::ProgressBar:
620 return QStringLiteral("android.widget.ProgressBar");
621 case QAccessible::Role::SpinBox:
622 return QStringLiteral("android.widget.NumberPicker");
623 case QAccessible::Role::WebDocument:
624 return QStringLiteral("android.webkit.WebView");
625 case QAccessible::Role::Dialog:
626 return QStringLiteral("android.app.AlertDialog");
627 case QAccessible::Role::PageTab:
628 return QStringLiteral("android.app.ActionBar.Tab");
629 case QAccessible::Role::PageTabList:
630 return QStringLiteral("android.widget.TabWidget");
631 case QAccessible::Role::ScrollBar:
632 return QStringLiteral("android.widget.Scroller");
633 case QAccessible::Role::Slider:
634 return QStringLiteral("android.widget.SeekBar");
635 case QAccessible::Role::Table:
636 // #TODO Evaluate the usage of AccessibleNodeInfo.setCollectionItemInfo() to provide
637 // infos about colums, rows und items.
638 return QStringLiteral("android.widget.GridView");
639 case QAccessible::Role::Pane:
640 // #TODO QQuickScrollView, QQuickListView (see QTBUG-137806)
641 return QStringLiteral("android.view.ViewGroup");
642 case QAccessible::Role::AlertMessage:
643 case QAccessible::Role::Animation:
644 case QAccessible::Role::Application:
645 case QAccessible::Role::Assistant:
646 case QAccessible::Role::BlockQuote:
647 case QAccessible::Role::Border:
648 case QAccessible::Role::ButtonDropGrid:
649 case QAccessible::Role::ButtonMenu:
650 case QAccessible::Role::Canvas:
651 case QAccessible::Role::Caret:
652 case QAccessible::Role::Cell:
653 case QAccessible::Role::Chart:
654 case QAccessible::Role::Client:
655 case QAccessible::Role::ColorChooser:
656 case QAccessible::Role::Column:
657 case QAccessible::Role::ColumnHeader:
658 case QAccessible::Role::ComplementaryContent:
659 case QAccessible::Role::Cursor:
660 case QAccessible::Role::Desktop:
661 case QAccessible::Role::Dial:
662 case QAccessible::Role::Document:
663 case QAccessible::Role::Equation:
664 case QAccessible::Role::Footer:
665 case QAccessible::Role::Form:
666 case QAccessible::Role::Grip:
667 case QAccessible::Role::HelpBalloon:
668 case QAccessible::Role::HotkeyField:
669 case QAccessible::Role::Indicator:
670 case QAccessible::Role::LayeredPane:
671 case QAccessible::Role::ListItem:
672 case QAccessible::Role::MenuBar:
673 case QAccessible::Role::NoRole:
674 case QAccessible::Role::Note:
675 case QAccessible::Role::Notification:
676 case QAccessible::Role::Paragraph:
677 case QAccessible::Role::PropertyPage:
678 case QAccessible::Role::Row:
679 case QAccessible::Role::RowHeader:
680 case QAccessible::Role::Section:
681 case QAccessible::Role::Sound:
682 case QAccessible::Role::Splitter:
683 case QAccessible::Role::StatusBar:
684 case QAccessible::Role::Terminal:
685 case QAccessible::Role::TitleBar:
686 case QAccessible::Role::ToolTip:
687 case QAccessible::Role::Tree:
688 case QAccessible::Role::TreeItem:
689 case QAccessible::Role::UserRole:
690 case QAccessible::Role::Whitespace:
691 case QAccessible::Role::Window:
692 // If unsure, every visible or interactive element in Android
693 // inherits android.view.View and by many extends also TextView.
694 // Android itself does a similar thing e.g. in its Settings-App.
695 return QStringLiteral("android.view.TextView");
696 }
697 }
698
699 static int expandedStateFromState(QAccessible::State state)
700 {
701 if (!state.expandable)
703
704 return state.expanded ? EXPANDED_STATE_FULL : EXPANDED_STATE_COLLAPSED;
705 }
706
707 static QString descriptionForInterface(QAccessibleInterface *iface)
708 {
709 QString desc;
710 if (iface && iface->isValid()) {
711 bool hasValue = false;
712 desc = iface->text(QAccessible::Name);
713 const QString descStr = iface->text(QAccessible::Description);
714 if (!descStr.isEmpty()) {
715 if (!desc.isEmpty())
716 desc.append(QStringLiteral(", "));
717 desc.append(descStr);
718 }
719 if (desc.isEmpty()) {
720 desc = iface->text(QAccessible::Value);
721 hasValue = !desc.isEmpty();
722 }
723 if (!hasValue && iface->valueInterface()) {
724 const QString valueStr = textFromValue(iface);
725 if (!valueStr.isEmpty()) {
726 if (!desc.isEmpty())
727 desc.append(QChar(QChar::Space));
728 desc.append(valueStr);
729 }
730 }
731 }
732 return desc;
733 }
734
735 static QString descriptionForAccessibleObject_helper(int objectId)
736 {
737 QAccessibleInterface *iface = interfaceFromId(objectId);
738 return descriptionForInterface(iface);
739 }
740
741 static jstring descriptionForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
742 {
743 QString desc;
744 if (m_accessibilityContext) {
745 runInObjectContext(m_accessibilityContext, [objectId]() {
746 return descriptionForAccessibleObject_helper(objectId);
747 }, &desc);
748 }
749 return env->NewString((jchar*) desc.constData(), (jsize) desc.size());
750 }
751
752 static QString languageTag_helper(int objectId)
753 {
754 QAccessibleInterface *iface = interfaceFromId(objectId);
755 if (!iface || !iface->isValid())
756 return QString();
757
758 QAccessibleAttributesInterface *attributesIface = iface->attributesInterface();
759 if (!attributesIface || !attributesIface->attributeKeys().contains(QAccessible::Attribute::Locale))
760 return QString();
761
762 return attributesIface->attributeValue(QAccessible::Attribute::Locale).toLocale().bcp47Name();
763 }
764
765 static jstring languageTag(JNIEnv *env, jobject /*thiz*/, jint objectId)
766 {
767 QString tag;
768 if (m_accessibilityContext) {
769 runInObjectContext(m_accessibilityContext, [objectId]() {
770 return languageTag_helper(objectId);
771 }, &tag);
772 }
773 return env->NewString((jchar*)tag.constData(), (jsize)tag.size());
774 }
775
776 static bool childOfModalInterface(QAccessibleInterface *modalInterface,
777 QAccessibleInterface *iface)
778 {
779 if (!iface || !modalInterface)
780 return false;
781
782 if (iface == modalInterface)
783 return true;
784
785 return childOfModalInterface(modalInterface, iface->parent());
786 }
787
788 struct NodeInfo
789 {
790 bool valid = false;
794 QString description;
795 QString text;
796 QString hint;
797 QString identifier;
798 bool hasTextSelection = false;
801 bool hasValue = false;
802 QVariant minValue = 0;
803 QVariant maxValue = 0;
804 QVariant currentValue = 0;
805 QVariant valueStepSize = 0;
806 bool blockedByModal = false;
807 };
808
809 static NodeInfo populateNode_helper(int objectId)
810 {
811 NodeInfo info;
812 QAccessibleInterface *iface = interfaceFromId(objectId);
813 if (iface && iface->isValid()) {
814 info.valid = true;
815 info.state = iface->state();
816 info.role = iface->role();
817 info.actions = QAccessibleBridgeUtils::effectiveActionNames(iface);
818 info.description = descriptionForInterface(iface);
819 info.identifier = QAccessibleBridgeUtils::accessibleId(iface);
820 QAccessibleTextInterface *textIface = iface->textInterface();
821 if (textIface && (textIface->selectionCount() > 0)) {
822 info.hasTextSelection = true;
823 textIface->selection(0, &info.selectionStart, &info.selectionEnd);
824 }
825 // For editable nodes, capture the text (exposed via setText(), which
826 // a screen reader reads to track the caret and echo edits) and the
827 // accessible name (exposed via setHintText(), the label channel a
828 // screen reader reads for a text input).
829 if (info.state.editable) {
830 if (textIface)
831 info.text = textIface->text(0, textIface->characterCount());
832 info.hint = iface->text(QAccessible::Name);
833 }
834 QAccessibleValueInterface *valueInterface = iface->valueInterface();
835 if (valueInterface) {
836 info.hasValue = true;
837 info.minValue = valueInterface->minimumValue();
838 info.maxValue = valueInterface->maximumValue();
839 info.currentValue = valueInterface->currentValue();
840 info.valueStepSize = valueInterface->minimumStepSize();
841 }
842 if (!m_modalInterfaces.isEmpty()
843 && !childOfModalInterface(interfaceFromId(m_modalInterfaces.last()), iface)) {
844 info.blockedByModal = true;
845 }
846 }
847 return info;
848 }
849
850 static jboolean populateNode(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject node)
851 {
852 NodeInfo info;
853 if (m_accessibilityContext) {
854 runInObjectContext(m_accessibilityContext, [objectId]() {
855 return populateNode_helper(objectId);
856 }, &info);
857 }
858 if (!info.valid) {
859 __android_log_print(ANDROID_LOG_WARN, m_qtTag, "Accessibility: populateNode for Invalid ID");
860 return false;
861 }
862
863 const QString role = classNameForRole(info.role, info.state);
864 jstring jrole = env->NewString((jchar*)role.constData(), (jsize)role.size());
865 env->CallVoidMethod(node, m_setClassNameMethodID, jrole);
866
867 const bool hasClickableAction =
868 (info.actions.contains(QAccessibleActionInterface::pressAction())
869 || info.actions.contains(QAccessibleActionInterface::toggleAction()))
870 && !(info.role == QAccessible::StaticText || info.role == QAccessible::Heading);
871 const bool hasIncreaseAction =
872 info.actions.contains(QAccessibleActionInterface::increaseAction());
873 const bool hasDecreaseAction =
874 info.actions.contains(QAccessibleActionInterface::decreaseAction());
875 const bool scrollableRole =
876 info.role == QAccessible::ScrollBar || info.role == QAccessible::List;
877
878 if (info.hasTextSelection && m_setTextSelectionMethodID) {
879 env->CallVoidMethod(node, m_setTextSelectionMethodID, info.selectionStart,
880 info.selectionEnd);
881 }
882
883 if (info.hasValue && m_setRangeInfoMethodID) {
884 int valueType = info.currentValue.typeId();
885 jint rangeType = RANGE_TYPE_INDETERMINATE;
886 switch (valueType) {
887 case QMetaType::Float:
888 case QMetaType::Double:
889 rangeType = RANGE_TYPE_FLOAT;
890 break;
891 case QMetaType::Int:
892 rangeType = RANGE_TYPE_INT;
893 break;
894 }
895
896 float min = info.minValue.toFloat();
897 float max = info.maxValue.toFloat();
898 float current = info.currentValue.toFloat();
899 if (info.role == QAccessible::ProgressBar) {
900 rangeType = RANGE_TYPE_PERCENT;
901 current = 100 * (current - min) / (max - min);
902 min = 0.0f;
903 max = 100.0f;
904 }
905
906 QJniObject rangeInfo("android/view/accessibility/AccessibilityNodeInfo$RangeInfo",
907 "(IFFF)V", rangeType, min, max, current);
908
909 if (rangeInfo.isValid()) {
910 env->CallVoidMethod(node, m_setRangeInfoMethodID, rangeInfo.object());
911 }
912 }
913
914 env->CallVoidMethod(node, m_setCheckableMethodID, (bool)info.state.checkable);
915 env->CallVoidMethod(node, m_setCheckedMethodID, (bool)info.state.checked);
916 if (QtAndroidPrivate::androidSdkVersion() >= 36) {
917 env->CallVoidMethod(node, m_setExpandedStateMethodID,
918 expandedStateFromState(info.state));
919 }
920 env->CallVoidMethod(node, m_setEditableMethodID, info.state.editable);
921 env->CallVoidMethod(node, m_setEnabledMethodID, !info.state.disabled);
922 env->CallVoidMethod(node, m_setFocusableMethodID, (bool)info.state.focusable);
923 env->CallVoidMethod(node, m_setFocusedMethodID, (bool)info.state.focused);
925 env->CallVoidMethod(node, m_setHeadingMethodID, info.role == QAccessible::Heading);
926 env->CallVoidMethod(node, m_setVisibleToUserMethodID,
927 !info.state.invisible && !info.blockedByModal);
928 env->CallVoidMethod(node, m_setScrollableMethodID,
929 hasIncreaseAction || hasDecreaseAction || scrollableRole);
930 env->CallVoidMethod(node, m_setClickableMethodID, hasClickableAction || info.role == QAccessible::Link);
931
932 // Add ACTION_CLICK
933 if (hasClickableAction)
934 env->CallVoidMethod(node, m_addActionMethodID, (int)0x00000010); // ACTION_CLICK defined in AccessibilityNodeInfo
935
936 // Add ACTION_SCROLL_FORWARD
937 if (hasIncreaseAction)
938 env->CallVoidMethod(node, m_addActionMethodID, (int)0x00001000); // ACTION_SCROLL_FORWARD defined in AccessibilityNodeInfo
939
940 // Add ACTION_SCROLL_BACKWARD
941 if (hasDecreaseAction)
942 env->CallVoidMethod(node, m_addActionMethodID, (int)0x00002000); // ACTION_SCROLL_BACKWARD defined in AccessibilityNodeInfo
943
944 // Add ACTION_EXPAND / ACTION_COLLAPSE
945 if (info.state.expandable) {
946 if (info.state.expanded) {
947 env->CallVoidMethod(node, m_addActionMethodID, ACTION_COLLAPSE);
948 } else {
949 env->CallVoidMethod(node, m_addActionMethodID, ACTION_EXPAND);
950 }
951 }
952
953 // try to fill in the text property, this is what the screen reader reads
954 jstring jdesc = env->NewString((jchar*)info.description.constData(),
955 (jsize)info.description.size());
956 //CALL_METHOD(node, "setText", "(Ljava/lang/CharSequence;)V", jdesc)
957 env->CallVoidMethod(node, m_setContentDescriptionMethodID, jdesc);
958
959 // An editable node exposes its content via setText() (read to track the
960 // caret and echo edits), its label via setHintText() (the label channel
961 // for a text input), and an inputType so it is treated as a real text
962 // field. contentDescription is kept above for readers that use it instead.
963 if (info.state.editable) {
964 if (m_setTextMethodID) {
965 jstring jtext = env->NewString((jchar*)info.text.constData(),
966 (jsize)info.text.size());
967 env->CallVoidMethod(node, m_setTextMethodID, jtext);
968 env->DeleteLocalRef(jtext);
969 }
970 // 0x1 == android.text.InputType.TYPE_CLASS_TEXT.
972 env->CallVoidMethod(node, m_setInputTypeMethodID, (jint)0x00000001);
974 jstring jhint = env->NewString((jchar*)info.hint.constData(),
975 (jsize)info.hint.size());
976 env->CallVoidMethod(node, m_setHintTextMethodID, jhint);
977 env->DeleteLocalRef(jhint);
978 }
979 }
980
981 QJniObject(node).callMethod<void>("setViewIdResourceName", info.identifier);
982
983 return true;
984 }
985
994
996 {
997 QAccessibleInterface *iface = interfaceFromId(objectId);
998 if (!iface || !iface->isValid())
999 return { };
1000
1001 auto *viewportIface = iface->viewportInterface();
1002 if (!viewportIface)
1003 return { };
1004
1005 ScrollEventInfo info;
1006 info.isValid = true;
1007 info.isIndexed = viewportIface->isIndexed();
1008 info.contentSize = viewportIface->contentSize();
1009 info.position = viewportIface->position();
1010 info.viewportSize = viewportIface->viewportSize();
1011
1012 return info;
1013 }
1014
1015 static jboolean populateScrollEvent(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject event)
1016 {
1017 ScrollEventInfo info;
1018 if (m_accessibilityContext) {
1019 runInObjectContext(
1020 m_accessibilityContext,
1021 [objectId]() { return populateScrollEvent_helper(objectId); }, &info);
1022 }
1023
1024 if (!info.isValid)
1025 return false;
1026
1027 if (info.isIndexed) {
1028 const int itemCount = std::max(info.contentSize.width(), info.contentSize.height());
1029 env->CallVoidMethod(event, m_setItemCountMethodID, itemCount);
1030 const int fromIndex = std::max(info.position.x(), info.position.y()) * itemCount;
1031 env->CallVoidMethod(event, m_setFromIndexMethodID, fromIndex);
1032 const int toIndex = fromIndex
1033 + std::min(info.viewportSize.width(), info.viewportSize.height() * itemCount);
1034 env->CallVoidMethod(event, m_setToIndexMethodID, toIndex);
1035
1036 } else {
1037 env->CallVoidMethod(event, m_setScrollXMethodID,
1038 (int)(info.position.x() * info.contentSize.width()));
1039 env->CallVoidMethod(event, m_setMaxScrollXMethodID, (int)info.contentSize.width());
1040 env->CallVoidMethod(event, m_setScrollYMethodID,
1041 (int)(info.position.y() * info.contentSize.height()));
1042 env->CallVoidMethod(event, m_setMaxScrollYMethodID, (int)info.contentSize.height());
1043 }
1044
1045 return true;
1046 }
1047
1048 static const JNINativeMethod methods[] = {
1049 {"setActive","(Z)V",(void*)setActive},
1050 {"childIdListForAccessibleObject", "(I)[I", (jintArray)childIdListForAccessibleObject},
1051 {"parentId", "(I)I", (void*)parentId},
1052 {"descriptionForAccessibleObject", "(I)Ljava/lang/String;", (jstring)descriptionForAccessibleObject},
1053 {"languageTag", "(I)Ljava/lang/String;", (jstring)languageTag},
1054 {"screenRect", "(I)Landroid/graphics/Rect;", (jobject)screenRect},
1055 {"hitTest", "(FF)I", (void*)hitTest},
1056 {"populateNode", "(ILandroid/view/accessibility/AccessibilityNodeInfo;)Z", (void*)populateNode},
1057 {"populateScrollEvent", "(ILandroid/view/accessibility/AccessibilityEvent;)Z", (void*)populateScrollEvent},
1058 {"clickAction", "(I)Z", (void*)clickAction},
1059 {"focusAction", "(I)Z", (void*)focusAction},
1060 {"scrollForward", "(I)Z", (void*)scrollForward},
1061 {"scrollBackward", "(I)Z", (void*)scrollBackward},
1062 {"showOnScreen", "(I)Z", (void *)showOnScreen},
1063 {"expand", "(I)Z", (void *)expand},
1064 {"collapse", "(I)Z", (void *)collapse}
1065 };
1066
1067#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
1068 VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE);
1069 if (!VAR) {
1070 __android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(), QtAndroid::methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE);
1071 return false;
1072 }
1073
1074#define CHECK_AND_INIT_STATIC_FIELD(TYPE, VAR, CLASS, FIELD_NAME)
1075 if (env.findStaticField<TYPE>(CLASS, FIELD_NAME) == nullptr) {
1076 __android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(),
1077 QtAndroid::staticFieldErrorMsgFmt(), FIELD_NAME);
1078 return false;
1079 }
1080 VAR = QJniObject::getStaticField<TYPE>(CLASS, FIELD_NAME);
1081
1082 bool registerNatives(QJniEnvironment &env)
1083 {
1084 if (!env.registerNativeMethods("org/qtproject/qt/android/QtNativeAccessibility",
1085 methods, sizeof(methods) / sizeof(methods[0]))) {
1086 __android_log_print(ANDROID_LOG_FATAL,"Qt A11y", "RegisterNatives failed");
1087 return false;
1088 }
1089
1090 jclass nodeInfoClass = env->FindClass("android/view/accessibility/AccessibilityNodeInfo");
1091 GET_AND_CHECK_STATIC_METHOD(m_setClassNameMethodID, nodeInfoClass, "setClassName", "(Ljava/lang/CharSequence;)V");
1092 GET_AND_CHECK_STATIC_METHOD(m_addActionMethodID, nodeInfoClass, "addAction", "(I)V");
1093 GET_AND_CHECK_STATIC_METHOD(m_setCheckableMethodID, nodeInfoClass, "setCheckable", "(Z)V");
1094 GET_AND_CHECK_STATIC_METHOD(m_setCheckedMethodID, nodeInfoClass, "setChecked", "(Z)V");
1095 if (QtAndroidPrivate::androidSdkVersion() >= 36) {
1096 GET_AND_CHECK_STATIC_METHOD(m_setExpandedStateMethodID, nodeInfoClass,
1097 "setExpandedState", "(I)V");
1098 }
1099 GET_AND_CHECK_STATIC_METHOD(m_setClickableMethodID, nodeInfoClass, "setClickable", "(Z)V");
1100 GET_AND_CHECK_STATIC_METHOD(m_setContentDescriptionMethodID, nodeInfoClass, "setContentDescription", "(Ljava/lang/CharSequence;)V");
1101 GET_AND_CHECK_STATIC_METHOD(m_setTextMethodID, nodeInfoClass, "setText", "(Ljava/lang/CharSequence;)V");
1102 GET_AND_CHECK_STATIC_METHOD(m_setInputTypeMethodID, nodeInfoClass, "setInputType", "(I)V");
1103 // setHintText is API 26+, below Qt's API-28 floor, so no SDK guard needed.
1104 GET_AND_CHECK_STATIC_METHOD(m_setHintTextMethodID, nodeInfoClass, "setHintText", "(Ljava/lang/CharSequence;)V");
1105 GET_AND_CHECK_STATIC_METHOD(m_setEditableMethodID, nodeInfoClass, "setEditable", "(Z)V");
1106 GET_AND_CHECK_STATIC_METHOD(m_setEnabledMethodID, nodeInfoClass, "setEnabled", "(Z)V");
1107 GET_AND_CHECK_STATIC_METHOD(m_setFocusableMethodID, nodeInfoClass, "setFocusable", "(Z)V");
1108 GET_AND_CHECK_STATIC_METHOD(m_setFocusedMethodID, nodeInfoClass, "setFocused", "(Z)V");
1109 if (QtAndroidPrivate::androidSdkVersion() >= 28) {
1110 GET_AND_CHECK_STATIC_METHOD(m_setHeadingMethodID, nodeInfoClass, "setHeading", "(Z)V");
1111 }
1112 GET_AND_CHECK_STATIC_METHOD(m_setScrollableMethodID, nodeInfoClass, "setScrollable", "(Z)V");
1113 GET_AND_CHECK_STATIC_METHOD(m_setVisibleToUserMethodID, nodeInfoClass, "setVisibleToUser", "(Z)V");
1114 GET_AND_CHECK_STATIC_METHOD(m_setTextSelectionMethodID, nodeInfoClass, "setTextSelection", "(II)V");
1116 m_setRangeInfoMethodID, nodeInfoClass, "setRangeInfo",
1117 "(Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;)V");
1118
1119 jclass eventClass = env->FindClass("android/view/accessibility/AccessibilityEvent");
1120 GET_AND_CHECK_STATIC_METHOD(m_setMaxScrollXMethodID, eventClass, "setMaxScrollX", "(I)V");
1121 GET_AND_CHECK_STATIC_METHOD(m_setScrollXMethodID, eventClass, "setScrollX", "(I)V");
1122 GET_AND_CHECK_STATIC_METHOD(m_setMaxScrollYMethodID, eventClass, "setMaxScrollY", "(I)V");
1123 GET_AND_CHECK_STATIC_METHOD(m_setScrollYMethodID, eventClass, "setScrollY", "(I)V");
1124 GET_AND_CHECK_STATIC_METHOD(m_setItemCountMethodID, eventClass, "setItemCount", "(I)V");
1125 GET_AND_CHECK_STATIC_METHOD(m_setFromIndexMethodID, eventClass, "setFromIndex", "(I)V");
1126 GET_AND_CHECK_STATIC_METHOD(m_setToIndexMethodID, eventClass, "setToIndex", "(I)V");
1127
1128 jclass rangeInfoClass =
1129 env->FindClass("android/view/accessibility/AccessibilityNodeInfo$RangeInfo");
1130 CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_INT, rangeInfoClass, "RANGE_TYPE_INT");
1131 CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_FLOAT, rangeInfoClass, "RANGE_TYPE_FLOAT");
1132 CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_PERCENT, rangeInfoClass, "RANGE_TYPE_PERCENT");
1133 if (QtAndroidPrivate::androidSdkVersion() >= 36) {
1134 CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_INDETERMINATE, rangeInfoClass,
1135 "RANGE_TYPE_INDETERMINATE");
1136 } else {
1138 }
1139
1140 if (QtAndroidPrivate::androidSdkVersion() >= 36) {
1141 CHECK_AND_INIT_STATIC_FIELD(int, EXPANDED_STATE_UNDEFINED, nodeInfoClass,
1142 "EXPANDED_STATE_UNDEFINED");
1143 CHECK_AND_INIT_STATIC_FIELD(int, EXPANDED_STATE_COLLAPSED, nodeInfoClass,
1144 "EXPANDED_STATE_COLLAPSED");
1145 CHECK_AND_INIT_STATIC_FIELD(int, EXPANDED_STATE_FULL, nodeInfoClass,
1146 "EXPANDED_STATE_FULL");
1147 }
1148
1149 CHECK_AND_INIT_STATIC_FIELD(int, ACTION_COLLAPSE, nodeInfoClass, "ACTION_COLLAPSE");
1150 CHECK_AND_INIT_STATIC_FIELD(int, ACTION_EXPAND, nodeInfoClass, "ACTION_EXPAND");
1151
1152 return true;
1153 }
1154}
1155
1156QT_END_NAMESPACE
static const char m_qtTag[]
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
#define CHECK_AND_INIT_STATIC_FIELD(TYPE, VAR, CLASS, FIELD_NAME)
\inmodule QtCore\reentrant
Definition qpoint.h:30
static jboolean showOnScreen(JNIEnv *, jobject, jint objectId)
void notifyDescriptionOrNameChanged(uint accessibilityObjectId)
void notifyObjectShow(uint accessibilityObjectId)
static bool clickAction_helper(int objectId)
static const JNINativeMethod methods[]
void notifyLocationChange(uint accessibilityObjectId)
void runInObjectContext(QObject *context, Func &&func, Ret *retVal)
static std::atomic< bool > m_accessibilityActivated
static jboolean scrollForward(JNIEnv *, jobject, jint objectId)
static ScrollEventInfo populateScrollEvent_helper(int objectId)
void notifyObjectFocus(uint accessibilityObjectId)
static jboolean scrollBackward(JNIEnv *, jobject, jint objectId)
static QString descriptionForInterface(QAccessibleInterface *iface)
static bool showOnScreen_helper(int objectId)
static int hitTest_helper(float x, float y)
static jboolean expand(JNIEnv *, jobject, jint objectId)
static jstring descriptionForAccessibleObject(JNIEnv *env, jobject, jint objectId)
void notifyObjectDestroyed(uint accessibilityObjectId)
static bool scroll_helper(int objectId, const QString &actionName)
static QString classNameForRole(QAccessible::Role role, QAccessible::State state)
static jmethodID m_setContentDescriptionMethodID
static QString textFromValue(QAccessibleInterface *iface)
void createAccessibilityContextObject(QObject *parent)
static QVarLengthArray< int, 8 > childIdListForAccessibleObject_helper(int objectId)
void notifyObjectCreated(uint accessibilityObjectId)
static NodeInfo populateNode_helper(int objectId)
void notifyObjectHide(uint accessibilityObjectId)
static jboolean focusAction(JNIEnv *, jobject, jint objectId)
static jmethodID m_setExpandedStateMethodID
static QList< QAccessible::Id > m_modalInterfaces
static QString languageTag_helper(int objectId)
static jmethodID m_setVisibleToUserMethodID
static jint hitTest(JNIEnv *, jobject, jfloat x, jfloat y)
static jstring languageTag(JNIEnv *env, jobject, jint objectId)
static bool focusAction_helper(int objectId)
static jboolean clickAction(JNIEnv *, jobject, jint objectId)
static bool childOfModalInterface(QAccessibleInterface *modalInterface, QAccessibleInterface *iface)
static void setActive(JNIEnv *, jobject, jboolean active)
static void invokeActionOnInterfaceInMainThread(QAccessibleActionInterface *actionInterface, const QString &action)
static jmethodID m_setTextSelectionMethodID
bool registerNatives(QJniEnvironment &env)
QAccessibleInterface * interfaceFromId(jint objectId)
void notifyValueChanged(uint accessibilityObjectId)
static jboolean populateScrollEvent(JNIEnv *env, jobject, jint objectId, jobject event)
static int expandedStateFromState(QAccessible::State state)
void notifyAnnouncementEvent(uint accessibilityObjectId, const QString &message)
static int parentId_helper(int objectId)
static QRect screenRect_helper(int objectId, bool clip=true)
static QString descriptionForAccessibleObject_helper(int objectId)
static jint parentId(JNIEnv *, jobject, jint objectId)
static jboolean collapse(JNIEnv *, jobject, jint objectId)
static jobject screenRect(JNIEnv *env, jobject, jint objectId)
static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject, jint objectId)
static jboolean populateNode(JNIEnv *env, jobject, jint objectId, jobject node)
void notifyScrolledEvent(uint accessiblityObjectId)
#define qApp