Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example demos/qtjennydemo
\title QtJenny: Generating C++ proxy classes to access Android APIs
\target QtJenny Demo
\ingroup qtquickdemos
\brief Demo showcasing the use of QtJenny.
\meta {tag} {demo,qtjenny,generator,android}
\examplecategory {Mobile}
\image qtjennydemo_overview.webp
{QtJenny demo application with volume, brightness, and wake
lock controls}
\section1 Overview
This demo showcases the use of \l {Maven Central: QtJenny}{QtJenny} and
uses it to generate C++ proxy classes to access Android APIs from C++ code.
The generated C++ classes are used in the UI application of this demo to
perform actions such as adjusting the volume and brightness, activating
and deactivating \l {Android: wake lock} {wake locks}, sending notifications
and triggering vibrations. These actions are part of Android APIs that
Qt doesn't implement.
Generating C++ classes using QtJenny removes the need to write
\l {Android: JNI} {JNI} code manually.
\section1 How it works
The demo contains two distinctive parts, one is the UI application
i.e the Qt Quick project called \c qtjenny_consumer that uses the code
generated by QtJenny, and the other one is a combination of three different Gradle
projects called \c qtjenny_general, \c qtjenny_callback and \c qtjenny_baseclass
that together handle the code generation using QtJenny.
Each of the three gradle projects contain class annonations and \c Gradle
configurations for QtJenny code generation.
The QtJenny code generation is triggered automatically during the CMake
configuration of \c qtjenny_consumer project by executing \c kaptReleaseKotlin
gradle task in each of \c qtjenny_general, \c qtjenny_callback
and \c qtjenny_baseclass gradle projects.
\snippet CMakeLists.txt 0
Running the \c qtjenny_consumer project launches the UI application.
\section1 Generating the C++ headers
Running the \c kaptReleaseKotlin task in the gradle projects triggers
an annotation processor that processes the annotations declared in
\c GenerateCppCode.kt.
Code snippet from the \c GenerateCppCode.kt file of \c qtjenny_general :
\snippet qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/GenerateCppCode.kt 1
In the case of \c qtjenny_general the annotation processor generates
the C++ headers to \c qtjenny_output directory. Those C++ headers contain
the needed JNI boilerplate code to access the Android APIs used in the \c qtjenny_consumer.
The app level \c build.gradle script in \c qtjenny_general specifies the
arguments for \l {Android: Kapt compiler plugin}{kapt} which QtJenny
implements. These arguments are parsed in the QtJenny compiler and used
in the generation process.
\snippet qtjenny_general/app/build.gradle 2
\section2 qtjenny_general
The \c qtjenny_general takes care of generating wrappers for Java classes, to
allow constructing Java objects and invoking methods on them. These wrappers
wrap the usual method calls, and for callbacks, the qtjenny_baseclass and
qtjenny_callback are used.
\section2 qtjenny_baseclass
The \c qtjenny_baseclass effectively allows extending the Java class
ContentObserver in C++ code, using a combination of the baseclass generation
and callback generation.
\section2 qtjenny_callback
The \c qtjenny_callback effectively allows implementing a Java interface in C++.
The \c qtjenny_baseclass class extending also uses this mechanism.
For implementing an interface, a Java Proxy class is constructed, using an
InvocationHandler that performs virtual function calls into the generated C++
class hierachy.
For extending a base class, the generated extended class performs virtual
function calls into the generated C++ class hierarchy.
\section1 Using the generated C++ headers in the Qt Quick application
The \c qtjenny_consumer is the Qt Quick Application that uses the C++ headers
generated by the \c qtjenny_general. We include the generated headers in
\c backend.h file and use them in \c backend.cpp to access various Android
APIs.
The app's UI consists of one \c Main.qml file, which is connected to the
\c BackEnd class using \l Q_INVOKABLE and \l Q_PROPERTY macros.
The UI contains the following controls and actions.
\section2 Wake locks
You can activate and deactivate either full or partial wake locks, using
\l {Switch} {Switches}. These switches trigger corresponding \c Q_INVOKABLE
functions of \c Backend class, that activate the wake lock and set the wake
lock status text that will be shown to user in the UI.
\snippet Main.qml 3
Setting partial wake lock uses the \c WakeLockProxy class that connects
to \c PowerManager.WakeLock Android API. Setting full wake lock is done
by using \c WindowProxy that connects to \c Window Android API.
\section2 Vibrate
You can trigger a vibration using the \c BackEnd::vibrate() function,
that uses \c VibrationEffectProxy, \c VibratorManagerProxy and
\c VibratorProxy classes to create the vibration.
\section2 Notifications
Sending a notification is handled in the \c BackEnd::notfy() function in
\c Backend with use of the \c NotificationManagerProxy class.
The notification is already created during the \c Backend class
initialization in \c BackEnd::createNotification() function.
\section2 Adjusting the brightness
Adjusting brightness with the brightness slider in the UI triggers
property change in \c Backend class that will be handled in \c BackEnd::setBrightness()
function. There the system brightness is adjusted with the use of
\c SettingsProxy, \c SystemProxy, \c ContextProxy, \c LayoutParamsProxy and
\c WindowProxy classes.
The brightness slider is dynamically synchronized with the system brightness,
so if the system brightness is changed from outside the application the
brightness slider adjusts to reflect the current system brightness.
If the application does not have permission to write system settings, an
\c Activity is started with the \c ACTION_MANAGE_WRITE_SETTINGS intent. After
this \c Activity is started, user has to manually give permission for the application
to write system settings.
\section2 Adjusting the volume
Adjusting volume with volume slider in the UI triggers property
change in \c Backend class that will be handled in \c BackEnd::setVolume()
function. There the system volume is adjusted with the use of
\c AudioManagerProxy class.
The volume slider is dynamically synchronized with the system music volume,
so if the system music volume is changed from outside the application
the volume slider adjusts to reflect the current system music volume.
*/