Android: Implement accessibility backend interface

This will provide the JNI API used by QtAndroidAccessibility namespace
via QtAndroid namespace.

Removed unnecessary functions like createAccessibilityDelegate from
QtEmbeddedDelegate, moved the interface extension to where it is
actually supported: QtActivityDelegate.

Until now, QtActivityDelegateBase has called the QtEmbeddedDelegate
implementation on createAccessibilityDelegate() in order to create a
QtAccessibilityDelegate - which has not actually created the delegate -
and then done a null check before trying to call the - always null -
QtAccessibilityDelegate member.

The embedding and service-embedding usecases are now dealt with via the
interface validity checks on the C++ side, until an actual
implementation for those is completed.

Task-number: QTBUG-118874
Change-Id: Iea3db0e17ae80c0443e9027bdfe36bba311eed2b
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Petri Virkkunen 2024-03-27 16:05:20 +02:00
parent 46a23b1fae
commit 2322c71cc7
10 changed files with 94 additions and 88 deletions

View File

@ -42,6 +42,7 @@ set(java_sources
src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java
src/org/qtproject/qt/android/BackendRegister.java src/org/qtproject/qt/android/BackendRegister.java
src/org/qtproject/qt/android/QtWindowInterface.java src/org/qtproject/qt/android/QtWindowInterface.java
src/org/qtproject/qt/android/QtAccessibilityInterface.java
) )
qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android

View File

@ -61,6 +61,8 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
} }
// TODO do we want to have one QtAccessibilityDelegate for the whole app (QtRootLayout) or // TODO do we want to have one QtAccessibilityDelegate for the whole app (QtRootLayout) or
// e.g. one per window? // e.g. one per window?
// FIXME make QtAccessibilityDelegate window based or verify current way works
// also for child windows: QTBUG-120685
public QtAccessibilityDelegate(QtLayout layout) public QtAccessibilityDelegate(QtLayout layout)
{ {
m_layout = layout; m_layout = layout;

View File

@ -0,0 +1,14 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android;
@UsedFromNativeCode
public interface QtAccessibilityInterface {
default void initializeAccessibility() { }
default void notifyLocationChange(int viewId) { }
default void notifyObjectHide(int viewId, int parentId) { }
default void notifyObjectFocus(int viewId) { }
default void notifyScrolledEvent(int viewId) { }
default void notifyValueChanged(int viewId, String value) { }
default void notifyObjectShow(int parentId) { }
}

View File

@ -31,7 +31,8 @@ import android.widget.PopupMenu;
import java.util.HashMap; import java.util.HashMap;
class QtActivityDelegate extends QtActivityDelegateBase implements QtWindowInterface class QtActivityDelegate
extends QtActivityDelegateBase implements QtWindowInterface, QtAccessibilityInterface
{ {
private static final String QtTAG = "QtActivityDelegate"; private static final String QtTAG = "QtActivityDelegate";
@ -42,7 +43,7 @@ class QtActivityDelegate extends QtActivityDelegateBase implements QtWindowInter
private View m_dummyView = null; private View m_dummyView = null;
private HashMap<Integer, View> m_nativeViews = new HashMap<Integer, View>(); private HashMap<Integer, View> m_nativeViews = new HashMap<Integer, View>();
private QtAccessibilityDelegate m_accessibilityDelegate = null;
QtActivityDelegate(Activity activity) QtActivityDelegate(Activity activity)
{ {
@ -58,6 +59,8 @@ class QtActivityDelegate extends QtActivityDelegateBase implements QtWindowInter
m_backendsRegistered = true; m_backendsRegistered = true;
BackendRegister.registerBackend(QtWindowInterface.class, BackendRegister.registerBackend(QtWindowInterface.class,
(QtWindowInterface)QtActivityDelegate.this); (QtWindowInterface)QtActivityDelegate.this);
BackendRegister.registerBackend(QtAccessibilityInterface.class,
(QtAccessibilityInterface)QtActivityDelegate.this);
} }
} }
@ -66,6 +69,7 @@ class QtActivityDelegate extends QtActivityDelegateBase implements QtWindowInter
if (m_backendsRegistered) { if (m_backendsRegistered) {
m_backendsRegistered = false; m_backendsRegistered = false;
BackendRegister.unregisterBackend(QtWindowInterface.class); BackendRegister.unregisterBackend(QtWindowInterface.class);
BackendRegister.unregisterBackend(QtAccessibilityInterface.class);
} }
} }
@ -226,7 +230,55 @@ class QtActivityDelegate extends QtActivityDelegateBase implements QtWindowInter
}); });
} }
@UsedFromNativeCode @Override
public void notifyLocationChange(int viewId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyLocationChange(viewId);
}
@Override
public void notifyObjectHide(int viewId, int parentId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyObjectHide(viewId, parentId);
}
@Override
public void notifyObjectShow(int parentId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyObjectShow(parentId);
}
@Override
public void notifyObjectFocus(int viewId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyObjectFocus(viewId);
}
@Override
public void notifyValueChanged(int viewId, String value)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyValueChanged(viewId, value);
}
@Override
public void notifyScrolledEvent(int viewId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyScrolledEvent(viewId);
}
@Override
public void initializeAccessibility() public void initializeAccessibility()
{ {
QtNative.runAction(() -> { QtNative.runAction(() -> {
@ -362,16 +414,6 @@ class QtActivityDelegate extends QtActivityDelegateBase implements QtWindowInter
}); });
} }
@Override
QtAccessibilityDelegate createAccessibilityDelegate()
{
if (m_layout != null)
return new QtAccessibilityDelegate(m_layout);
Log.w(QtTAG, "Null layout, failed to initialize accessibility delegate.");
return null;
}
private void setActivityBackgroundDrawable() private void setActivityBackgroundDrawable()
{ {
TypedValue attr = new TypedValue(); TypedValue attr = new TypedValue();

View File

@ -37,7 +37,6 @@ abstract class QtActivityDelegateBase
{ {
protected Activity m_activity; protected Activity m_activity;
protected HashMap<Integer, QtWindow> m_topLevelWindows; protected HashMap<Integer, QtWindow> m_topLevelWindows;
protected QtAccessibilityDelegate m_accessibilityDelegate = null;
protected QtDisplayManager m_displayManager = null; protected QtDisplayManager m_displayManager = null;
protected QtInputDelegate m_inputDelegate = null; protected QtInputDelegate m_inputDelegate = null;
@ -46,7 +45,6 @@ abstract class QtActivityDelegateBase
// Subclass must implement these // Subclass must implement these
abstract void startNativeApplicationImpl(String appParams, String mainLib); abstract void startNativeApplicationImpl(String appParams, String mainLib);
abstract QtAccessibilityDelegate createAccessibilityDelegate();
abstract QtLayout getQtLayout(); abstract QtLayout getQtLayout();
// With these we are okay with default implementation doing nothing // With these we are okay with default implementation doing nothing
@ -153,62 +151,6 @@ abstract class QtActivityDelegateBase
hideSplashScreen(0); hideSplashScreen(0);
} }
@UsedFromNativeCode
public void notifyLocationChange(int viewId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyLocationChange(viewId);
}
@UsedFromNativeCode
public void notifyObjectHide(int viewId, int parentId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyObjectHide(viewId, parentId);
}
@UsedFromNativeCode
public void notifyObjectShow(int parentId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyObjectShow(parentId);
}
@UsedFromNativeCode
public void notifyObjectFocus(int viewId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyObjectFocus(viewId);
}
@UsedFromNativeCode
public void notifyValueChanged(int viewId, String value)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyValueChanged(viewId, value);
}
@UsedFromNativeCode
public void notifyScrolledEvent(int viewId)
{
if (m_accessibilityDelegate == null)
return;
m_accessibilityDelegate.notifyScrolledEvent(viewId);
}
@UsedFromNativeCode
public void initializeAccessibility()
{
QtNative.runAction(() -> {
m_accessibilityDelegate = createAccessibilityDelegate();
});
}
void handleUiModeChange(int uiMode) void handleUiModeChange(int uiMode)
{ {
// QTBUG-108365 // QTBUG-108365

View File

@ -122,14 +122,6 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase
QtNative.startApplication(appParams, mainLib); QtNative.startApplication(appParams, mainLib);
} }
@Override
QtAccessibilityDelegate createAccessibilityDelegate()
{
// FIXME make QtAccessibilityDelegate window based or verify current way works
// also for child windows: QTBUG-120685
return null;
}
@UsedFromNativeCode @UsedFromNativeCode
@Override @Override
QtLayout getQtLayout() QtLayout getQtLayout()

View File

@ -28,6 +28,7 @@ class QtServiceEmbeddedDelegate implements QtEmbeddedViewInterface, QtNative.App
m_service = service; m_service = service;
QtNative.registerAppStateListener(this); QtNative.registerAppStateListener(this);
QtNative.setService(service); QtNative.setService(service);
// QTBUG-122920 TODO Implement accessibility for service UIs
} }
@UsedFromNativeCode @UsedFromNativeCode

View File

@ -82,7 +82,7 @@ namespace QtAndroidAccessibility
void initialize() void initialize()
{ {
QtAndroid::qtActivityDelegate().callMethod<void>("initializeAccessibility"); QtAndroid::initializeAccessibility();
} }
bool isActive() bool isActive()

View File

@ -95,6 +95,7 @@ Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_I
Q_DECLARE_JNI_CLASS(QtEmbeddedDelegateFactory, "org/qtproject/qt/android/QtEmbeddedDelegateFactory") Q_DECLARE_JNI_CLASS(QtEmbeddedDelegateFactory, "org/qtproject/qt/android/QtEmbeddedDelegateFactory")
Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface") Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface")
Q_DECLARE_JNI_CLASS(QtAccessibilityInterface, "org/qtproject/qt/android/QtAccessibilityInterface");
namespace QtAndroid namespace QtAndroid
{ {
@ -237,36 +238,46 @@ namespace QtAndroid
return true; return true;
} }
void initializeAccessibility()
{
m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
"initializeAccessibility");
}
void notifyAccessibilityLocationChange(uint accessibilityObjectId) void notifyAccessibilityLocationChange(uint accessibilityObjectId)
{ {
qtActivityDelegate().callMethod<void>("notifyLocationChange", accessibilityObjectId); m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
"notifyLocationChange", accessibilityObjectId);
} }
void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId) void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
{ {
qtActivityDelegate().callMethod<void>("notifyObjectHide", m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
accessibilityObjectId, parentObjectId); "notifyObjectHide", accessibilityObjectId, parentObjectId);
} }
void notifyObjectShow(uint parentObjectId) void notifyObjectShow(uint parentObjectId)
{ {
qtActivityDelegate().callMethod<void>("notifyObjectShow", m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
parentObjectId); "notifyObjectShow", parentObjectId);
} }
void notifyObjectFocus(uint accessibilityObjectId) void notifyObjectFocus(uint accessibilityObjectId)
{ {
qtActivityDelegate().callMethod<void>("notifyObjectFocus", accessibilityObjectId); m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
"notifyObjectFocus", accessibilityObjectId);
} }
void notifyValueChanged(uint accessibilityObjectId, jstring value) void notifyValueChanged(uint accessibilityObjectId, jstring value)
{ {
qtActivityDelegate().callMethod<void>("notifyValueChanged", accessibilityObjectId, value); m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
"notifyValueChanged", accessibilityObjectId, value);
} }
void notifyScrolledEvent(uint accessibilityObjectId) void notifyScrolledEvent(uint accessibilityObjectId)
{ {
qtActivityDelegate().callMethod<void>("notifyScrolledEvent", accessibilityObjectId); m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
"notifyScrolledEvent", accessibilityObjectId);
} }
void notifyNativePluginIntegrationReady(bool ready) void notifyNativePluginIntegrationReady(bool ready)

View File

@ -65,6 +65,7 @@ namespace QtAndroid
jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env); jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env);
jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = nullptr); jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = nullptr);
void initializeAccessibility();
void notifyAccessibilityLocationChange(uint accessibilityObjectId); void notifyAccessibilityLocationChange(uint accessibilityObjectId);
void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId); void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId);
void notifyObjectShow(uint parentObjectId); void notifyObjectShow(uint parentObjectId);