On this page

QtJenny: Generating C++ proxy classes to access Android APIs

Demo showcasing the use of QtJenny.

Overview

This demo showcases the use of 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 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 JNI code manually.

How it works

The demo contains two distinctive parts, one is the UI application i.e the Qt Quick project called qtjenny_consumer that uses the code generated by QtJenny, and the other one is a combination of three different Gradle projects called qtjenny_general, qtjenny_callback and qtjenny_baseclass that together handle the code generation using QtJenny. Each of the three gradle projects contain class annonations and Gradle configurations for QtJenny code generation.

The QtJenny code generation is triggered automatically during the CMake configuration of qtjenny_consumer project by executing kaptReleaseKotlin gradle task in each of qtjenny_general, qtjenny_callback and qtjenny_baseclass gradle projects.

if (ANDROID)
    if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
        set (gradlew_cmd "gradlew.bat")
    else()
         set (gradlew_cmd "./gradlew")
    endif()
    set (gradlew_arg "--rerun-tasks")
    set (gradlew_task "kaptReleaseKotlin")
    execute_process(COMMAND ${gradlew_cmd} ${gradlew_arg} ${gradlew_task}
    WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/qtjenny_baseclass")
    execute_process(COMMAND ${gradlew_cmd} ${gradlew_arg} ${gradlew_task}
    WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/qtjenny_general")
    execute_process(COMMAND ${gradlew_cmd} ${gradlew_arg} ${gradlew_task}
    WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/qtjenny_callback")
else()
    message(FATAL_ERROR "Example only works on Android")
endif()

Running the qtjenny_consumer project launches the UI application.

Generating the C++ headers

Running the kaptReleaseKotlin task in the gradle projects triggers an annotation processor that processes the annotations declared in GenerateCppCode.kt.

Code snippet from the GenerateCppCode.kt file of qtjenny_general :

@NativeClass
@NativeProxy(allMethods = false, allFields = false)
@NativeProxyForClasses(namespace = "android::os", classes = [BatteryManager::class, VibratorManager::class,
    Vibrator::class, VibrationEffect::class, Context::class, PowerManager::class, PowerManager.WakeLock::class,
    Handler::class, Looper::class])
@NativeProxyForClasses(namespace = "android::view", classes = [Window::class, WindowManager.LayoutParams::class])
@NativeProxyForClasses(namespace = "android::media", classes = [AudioManager::class])
@NativeProxyForClasses(namespace = "android::drawable", classes = [android.R.drawable::class])
@NativeProxyForClasses(namespace = "android::app", classes = [Activity::class, Notification::class,
    Notification.Builder::class, NotificationChannel::class, NotificationManager::class])
@NativeProxyForClasses(namespace = "android::provider", classes = [Settings.Global::class, Settings.System::class,
    Settings::class])
@NativeProxyForClasses(namespace = "android::content", classes = [Intent::class, ContentResolver::class])

In the case of qtjenny_general the annotation processor generates the C++ headers to qtjenny_output directory. Those C++ headers contain the needed JNI boilerplate code to access the Android APIs used in the qtjenny_consumer.

The app level build.gradle script in qtjenny_general specifies the arguments for kapt which QtJenny implements. These arguments are parsed in the QtJenny compiler and used in the generation process.

kapt {
    arguments {
        // pass arguments to jenny
        arg("jenny.outputDirectory", project.file("../../qtjenny_output"))
        arg("jenny.templateDirectory", project.file("../templates"))
        arg("jenny.headerOnlyProxy", "true")
        arg("jenny.useJniHelper", "false")
        arg("jenny.useTemplates", "true")
    }
}
qtjenny_general

The 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.

qtjenny_baseclass

The qtjenny_baseclass effectively allows extending the Java class ContentObserver in C++ code, using a combination of the baseclass generation and callback generation.

qtjenny_callback

The qtjenny_callback effectively allows implementing a Java interface in C++. The 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.

Using the generated C++ headers in the Qt Quick application

The qtjenny_consumer is the Qt Quick Application that uses the C++ headers generated by the qtjenny_general. We include the generated headers in backend.h file and use them in backend.cpp to access various Android APIs.

The app's UI consists of one Main.qml file, which is connected to the BackEnd class using Q_INVOKABLE and Q_PROPERTY macros.

The UI contains the following controls and actions.

Wake locks

You can activate and deactivate either full or partial wake locks, using Switches. These switches trigger corresponding Q_INVOKABLE functions of Backend class, that activate the wake lock and set the wake lock status text that will be shown to user in the UI.

if (checked) {
    myBackEnd.setFullWakeLock()
    if (partialWakeLock.checked)
        partialWakeLock.click()
    mainWindow.wakeLockStatus = "Full WakeLock active"

Setting partial wake lock uses the WakeLockProxy class that connects to PowerManager.WakeLock Android API. Setting full wake lock is done by using WindowProxy that connects to Window Android API.

Vibrate

You can trigger a vibration using the BackEnd::vibrate() function, that uses VibrationEffectProxy, VibratorManagerProxy and VibratorProxy classes to create the vibration.

Notifications

Sending a notification is handled in the BackEnd::notfy() function in Backend with use of the NotificationManagerProxy class.

The notification is already created during the Backend class initialization in BackEnd::createNotification() function.

Adjusting the brightness

Adjusting brightness with the brightness slider in the UI triggers property change in Backend class that will be handled in BackEnd::setBrightness() function. There the system brightness is adjusted with the use of SettingsProxy, SystemProxy, ContextProxy, LayoutParamsProxy and 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 Activity is started with the ACTION_MANAGE_WRITE_SETTINGS intent. After this Activity is started, user has to manually give permission for the application to write system settings.

Adjusting the volume

Adjusting volume with volume slider in the UI triggers property change in Backend class that will be handled in BackEnd::setVolume() function. There the system volume is adjusted with the use of 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.

Example project @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.