Add observers to qtjennydemo

Observers make it possible to listen for changes
happening in Android system in Qt application.

Add qtjenny_callback and qtjenny_baseclass projects
which with QtJenny take care of generating the
needed classes for the observer to work.

Change volume and brightness adjusting buttons
to sliders which can now be dynamically synchronized
with the Android system values.

Add volumeandbrightnessobserver.cpp, contentobserver.cpp,
nativedispatch.cpp and NativeInvokationHandler.java
classes.

Add process executables for qtjenny_baseclass and
qtjenny_callback in CMakeLists.txt to automatically
generate the proxy headers.

Rename qtjenny_demo android package name to
qtjennydemo.

Rename qtjenny_generator to qtjenny_general to
make it more clear which part of the generated
classes happen in which gradle project.

Fixes: QTTA-467
Pick-to: 6.10
Change-Id: I885959c31c0f9d118d21105293c464af2883ce97
Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
This commit is contained in:
Konsta Alajärvi 2025-10-31 16:56:10 +02:00
parent 031ee2dc79
commit c71559face
108 changed files with 2324 additions and 212 deletions

View File

@ -30,7 +30,21 @@ SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR BSD-3-Clause"
[[annotations]]
path = ["examples/demos/qtjennydemo/qtjenny_generator/**"]
path = ["examples/demos/qtjennydemo/qtjenny_general/**"]
comment = "this must be after the build system table because example and snippets take precedence over build system"
precedence = "closest"
SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = ["examples/demos/qtjennydemo/qtjenny_callback/**"]
comment = "this must be after the build system table because example and snippets take precedence over build system"
precedence = "closest"
SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = ["examples/demos/qtjennydemo/qtjenny_baseclass/**"]
comment = "this must be after the build system table because example and snippets take precedence over build system"
precedence = "closest"
SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
@ -52,7 +66,21 @@ SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR BSD-3-Clause"
[[annotations]]
path = ["examples/demos/qtjennydemo/qtjenny_generator/**/**.toml"]
path = ["examples/demos/qtjennydemo/qtjenny_general/**/**.toml"]
precedence = "closest"
comment = "infrastructure"
SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = ["examples/demos/qtjennydemo/qtjenny_callback/**/**.toml"]
precedence = "closest"
comment = "infrastructure"
SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = ["examples/demos/qtjennydemo/qtjenny_baseclass/**/**.toml"]
precedence = "closest"
comment = "infrastructure"
SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."

View File

@ -1 +0,0 @@
qtjenny_generator/gradlew text eol=lf

View File

@ -2,6 +2,12 @@ qtjenny_output
build
.gradle
.idea
.settings
.classpath
.project
jenny_callback/
ContentObserverProxy.java
qtjenny_baseclass/jenny
templates_release
templates_debug
local.properties

View File

@ -1,3 +1,5 @@
# Copyright (C) 2025 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.22)
project(QtJenny VERSION 0.1 LANGUAGES CXX)
@ -12,7 +14,11 @@ if (ANDROID)
set (gradlew_arg "--rerun-tasks")
set (gradlew_task "kaptReleaseKotlin")
execute_process(COMMAND ${gradlew_cmd} ${gradlew_arg} ${gradlew_task}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/qtjenny_generator")
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()
@ -36,6 +42,12 @@ qt_add_qml_module(appqtjenny_consumer
SOURCES
backend.h
backend.cpp
volumeandbrightnessobserver.h
volumeandbrightnessobserver.cpp
contentobserver.h
contentobserver.cpp
nativedispatch.h
nativedispatch.cpp
)
set_target_properties(appqtjenny_consumer PROPERTIES

View File

@ -4,6 +4,7 @@ import QtQuick
import qtjenny_consumer
import QtQuick.Controls
import QtQuick.Layouts
import QtQml
ApplicationWindow {
id: mainWindow
@ -21,7 +22,6 @@ ApplicationWindow {
padding: 20
anchors.centerIn: parent
width: parent.width / 1.5
closePolicy: Popup.CloseOnPressOutside
contentItem: Text {
id: popupText
@ -29,14 +29,31 @@ ApplicationWindow {
wrapMode: Text.WordWrap
font.pointSize: 15
}
Timer {
id: popupClosingTimer
interval: 4000
repeat: false
onTriggered: {
myBackEnd.manageWriteSystemSettings()
popup.close()
}
}
}
BackEnd {
id: myBackEnd
onShowPopup: function(volumeDisabledReason) {
onShowPopup: function(message) {
popupText.text = message
popup.open()
popupText.text = volumeDisabledReason
if (!myBackEnd.canWriteSystemSettings) {
popup.closePolicy = Popup.NoAutoClose
popupClosingTimer.start()
} else
popup.closePolicy = Popup.CloseOnPressOutside
}
}
@ -73,33 +90,17 @@ ApplicationWindow {
font.pointSize: 16
}
Row {
id: volumeControlRow
Slider {
id: volumeSlider
spacing: 5
from: myBackEnd.minVolume
to: myBackEnd.maxVolume
stepSize: 1
value: myBackEnd.volume
Button {
id: volumeUpButton
text: "+"
highlighted: true
enabled: !myBackEnd.isFixedVolume
onClicked: {
myBackEnd.adjustVolume(1)
}
}
Button {
id: volumeDownButton
text: "-"
highlighted: true
enabled: !myBackEnd.isFixedVolume
onClicked: {
myBackEnd.adjustVolume(0)
}
onValueChanged: {
if (myBackEnd.volume != volumeSlider.value)
myBackEnd.volume = volumeSlider.value
}
}
@ -111,31 +112,17 @@ ApplicationWindow {
Layout.topMargin: mainWindow.isPortrait ? 20 : 0
}
Row {
id: brightnessControlRow
Slider {
id: brightnessSlider
spacing: 5
from: 0
to: 255
stepSize: 1
value: myBackEnd.brightness
Button {
id: brightnessUpButton
text: "+"
highlighted: true
onClicked: {
myBackEnd.adjustBrightness(1)
}
}
Button {
id: brightnessDownButton
text: "-"
highlighted: true
onClicked: {
myBackEnd.adjustBrightness(0)
}
onValueChanged: {
if (myBackEnd.brightness != brightnessSlider.value)
myBackEnd.brightness = brightnessSlider.value
}
}
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.qtproject.example" android:installLocation="auto" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:versionName="-- %%INSERT_VERSION_NAME%% --">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.qtproject.qtjennydemo" android:installLocation="auto" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:versionName="-- %%INSERT_VERSION_NAME%% --">
<!-- %%INSERT_PERMISSIONS -->
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

View File

@ -0,0 +1,30 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
package org.qtproject.qtjennydemo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Objects;
public class NativeInvocationHandler implements InvocationHandler {
private final long nativeHandle;
public NativeInvocationHandler(long nativeHandle) {
this.nativeHandle = nativeHandle;
}
public int hashCode() {
return Objects.hash(nativeHandle);
}
private native Object invokeNative(long nativeHandle, String method, Object[] args);
public Object invoke(String method, Object[] args) {
return invokeNative(nativeHandle, method, args);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return invokeNative(nativeHandle, method.getName(), args);
}
}

View File

@ -1,7 +1,7 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "backend.h"
#include "volumeandbrightnessobserver.h"
BackEnd::BackEnd(QObject *parent) : QObject{ parent }
{
@ -20,15 +20,28 @@ BackEnd::BackEnd(QObject *parent) : QObject{ parent }
m_layoutParams = m_window.getAttributes();
m_partialWakeLock = powerManager.newWakeLock(powerManager.PARTIAL_WAKE_LOCK,
QJniObject::fromString("PARTIALWAKELOCK").object<jstring>());
m_brightness = m_system.getInt(m_context.getContentResolver().object<jobject>(),
QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object<jstring>());
m_maxVolume = m_audioManager.getStreamMaxVolume(m_audioManager.STREAM_MUSIC);
m_minVolume = m_audioManager.getStreamMinVolume(m_audioManager.STREAM_MUSIC);
m_volume = m_audioManager.getStreamVolume(m_audioManager.STREAM_MUSIC);
// If system implements a fixed volume policy, disable volume slider
// Suggests an audio stream whose volume should be changed by the
// hardware volume controls when this activity is in foreground.
// With this we can focus on only the music stream, which makes adjusting volume much easier.
m_activityContext.setVolumeControlStream(m_audioManager.STREAM_MUSIC);
// If system implements a fixed volume policy, show popup with error message.
if (m_audioManager.isVolumeFixed())
handleVolumeError("Device implements fixed volume setting.", "");
m_qAndroidApp->runOnAndroidMainThread([&]() {
m_layoutParams.setScreenBrightness(0.5);
m_window.setAttributes(m_layoutParams);
});
auto vobs = new VolumeAndBrightnessObserver(this, this);
connect(vobs, &VolumeAndBrightnessObserver::volumeChangeObserved, this, &BackEnd::onVolumeChangeObserved);
connect(vobs, &VolumeAndBrightnessObserver::brightnessChangeObserved, this, &BackEnd::onBrightnessChangeObserved);
connect(this, &BackEnd::manageWriteSystemSettings, this, &BackEnd::onManageWriteSystemSettings);
vobs->start();
createNotification();
}
@ -75,68 +88,6 @@ void BackEnd::notify()
}
}
// Adjust system volume, either lowering or raising based on given direction
void BackEnd::adjustVolume(enum Direction direction)
{
if (m_global.getInt(m_context.getContentResolver().object<jobject>(),
QJniObject::fromString("zen_mode").object<jstring>()) != 0) {
handleVolumeError("Do not Disturb mode is on",
"Disable Do not Disturb mode to adjust the volume");
} else if (m_audioManager.getRingerMode() != m_audioManager.RINGER_MODE_NORMAL) {
if (m_audioManager.getRingerMode() == m_audioManager.RINGER_MODE_VIBRATE) {
handleVolumeError("Vibrate only mode is on",
"Disable vibrate only mode to adjust volume.");
} else if (m_audioManager.getRingerMode() == m_audioManager.RINGER_MODE_SILENT) {
handleVolumeError("Silent mode is on", "Disable Silent mode to adjust the volume.");
}
} else if (direction == Direction::Up) {
m_audioManager.adjustVolume(m_audioManager.ADJUST_RAISE, m_audioManager.FLAG_SHOW_UI);
} else if (direction == Direction::Down) {
m_audioManager.adjustVolume(m_audioManager.ADJUST_LOWER, m_audioManager.FLAG_SHOW_UI);
}
}
// Adjust system brightness, either lowering or raising based on given direction
void BackEnd::adjustBrightness(enum Direction direction)
{
using namespace android::content;
using namespace android::provider;
IntentProxy m_intent = m_intent.newInstance(QJniObject::fromString(
SettingsProxy::ACTION_MANAGE_WRITE_SETTINGS).object<jstring>());
// Check if app has permission to write system settings.
// Start an Activity with the ACTION_MANAGE_WRITE_SETTINGS intent if app
// does not have permission to write said settings, after which user
// has to manually give the permission to this app.
if (!m_system.canWrite(m_context))
m_context.startActivity(m_intent);
int brightness = m_system.getInt(m_context.getContentResolver().object<jobject>(),
QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object<jstring>());
if (direction == Direction::Up) {
if (brightness <= maxBrightness)
brightness += 10;
} else if (direction == Direction::Down) {
if (brightness >= minBrightness)
brightness -= 10;
}
double brightnessToDouble = brightnessStep * (brightness / 10.0);
// We need to set the brightness to system settings and to Window separately, as
// synchronization does not happen automatically and updating one or
// the other only, leaves the other one out of sync.
m_system.putInt(m_context.getContentResolver().object<jobject>(),
QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object<jstring>(),
brightness);
m_qAndroidApp->runOnAndroidMainThread([this, brightness = brightnessToDouble]() {
m_layoutParams.setScreenBrightness(brightness);
m_window.setAttributes(m_layoutParams);
});
}
void BackEnd::setPartialWakeLock()
{
m_partialWakeLock.acquire(m_activityContext);
@ -203,3 +154,77 @@ void BackEnd::handleVolumeError(const QString &problem, const QString &solution)
: problem + "\n" + solution;
emit showPopup(message);
}
int BackEnd::brightness() const
{
return m_brightness;
}
void BackEnd::setBrightness(int newBrightness)
{
if (m_brightness == newBrightness)
return;
// Check if the app has permission to write system settings.
if (!m_system.canWrite(m_context)) {
m_canWriteSystemSettings = false;
emit showPopup("Writing system settings is not allowed."
" Please give permission to write system settings for this application");
return;
}
m_canWriteSystemSettings = true;
m_brightness = newBrightness;
double brightnessToDouble = brightnessStep * newBrightness;
// This keeps the brightness shown in the system bar of Android device in sync.
m_system.putInt(m_context.getContentResolver().object<jobject>(),
QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object<jstring>(),
newBrightness);
m_qAndroidApp->runOnAndroidMainThread([this, brightnessToDouble]() {
m_layoutParams.setScreenBrightness(brightnessToDouble);
m_window.setAttributes(m_layoutParams);
});
}
int BackEnd::volume() const
{
return m_volume;
}
void BackEnd::setVolume(int newVolume)
{
if (m_volume == newVolume)
return;
m_audioManager.setStreamVolume(m_audioManager.STREAM_MUSIC, newVolume, m_audioManager.FLAG_SHOW_UI);
m_volume = newVolume;
}
void BackEnd::onVolumeChangeObserved(int volume)
{
if (m_volume == volume)
return;
m_volume = volume;
emit volumeChanged();
}
void BackEnd::onBrightnessChangeObserved(int brightness)
{
if (m_brightness == brightness)
return;
m_brightness = brightness;
emit brightnessChanged();
}
void BackEnd::onManageWriteSystemSettings()
{
// Start an Activity with the ACTION_MANAGE_WRITE_SETTINGS intent if app
// does not have permission to write system settings, after which user
// has to manually give the permission to write system settings to this app.
android::content::IntentProxy m_intent = m_intent.newInstance(
QJniObject::fromString(android::provider::SettingsProxy::ACTION_MANAGE_WRITE_SETTINGS).object<jstring>());
m_context.startActivity(m_intent);
}

View File

@ -11,7 +11,6 @@
#include <QtCore/private/qandroidextras_p.h>
#include <QtQml/qqml.h>
#include <QtGui/qguiapplication.h>
#include <qtjenny_output/jenny/proxy/android_app_ActivityProxy.h>
#include <qtjenny_output/jenny/proxy/android_app_BuilderProxy.h>
#include <qtjenny_output/jenny/proxy/android_app_NotificationChannelProxy.h>
@ -40,37 +39,64 @@ class BackEnd : public QObject
public:
explicit BackEnd(QObject *parent = nullptr);
enum class Direction {
Down = 0,
Up = 1
};
Q_ENUM(Direction)
Q_INVOKABLE void disableFullWakeLock();
Q_INVOKABLE void disablePartialWakeLock();
Q_INVOKABLE void notify();
Q_INVOKABLE void setFullWakeLock();
Q_INVOKABLE void setPartialWakeLock();
Q_INVOKABLE void vibrate();
Q_INVOKABLE void adjustBrightness(enum Direction);
Q_INVOKABLE void adjustVolume(enum Direction);
Q_PROPERTY(bool isFixedVolume READ isFixedVolume CONSTANT)
Q_PROPERTY(bool canWriteSystemSettings MEMBER m_canWriteSystemSettings NOTIFY canWriteSystemSettingsChanged)
Q_PROPERTY(int maxVolume READ maxVolume CONSTANT)
Q_PROPERTY(int minVolume READ minVolume CONSTANT)
Q_PROPERTY(int brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged)
Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged)
android::os::ContextProxy m_context;
android::provider::SystemProxy m_system;
android::media::AudioManagerProxy m_audioManager;
int m_brightness;
int m_volume;
bool isFixedVolume() const
{ return m_audioManager.isVolumeFixed(); }
int maxVolume() const
{ return m_maxVolume; }
int minVolume() const
{ return m_minVolume; }
int brightness() const;
void setBrightness(int newBrightness);
int volume() const;
void setVolume(int newVolume);
public slots:
void onVolumeChangeObserved(int volume);
void onBrightnessChangeObserved(int brightness);
void onManageWriteSystemSettings();
signals:
void showPopup(const QString &volumeDisabledReason);
void showPopup(const QString &message);
void brightnessChanged();
void volumeChanged();
void manageWriteSystemSettings();
void canWriteSystemSettingsChanged();
private:
const int m_systemVersion = QOperatingSystemVersion::current().version().majorVersion();
static constexpr int vibrateTimeInMillisecs = 1000;
static constexpr int maxBrightness = 245;
static constexpr int minBrightness = 10;
static constexpr double brightnessStep = 10.0 / 255;
static constexpr double brightnessStep = 1.0 / 255;
int m_minVolume;
int m_maxVolume;
bool m_canWriteSystemSettings = true;
void createNotification();
void handleVolumeError(const QString &problem, const QString &solution);
@ -79,11 +105,8 @@ private:
android::app::ActivityProxy m_activityContext;
android::app::NotificationManagerProxy m_notificationManager;
android::app::NotificationProxy m_notification;
android::media::AudioManagerProxy m_audioManager;
android::os::ContextProxy m_context;
android::os::WakeLockProxy m_partialWakeLock;
android::provider::GlobalProxy m_global;
android::provider::SystemProxy m_system;
android::view::LayoutParamsProxy m_layoutParams;
android::view::WindowProxy m_window;
};

View File

@ -0,0 +1,38 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "contentobserver.h"
#include <QDebug>
ContentObserver::ContentObserver() {}
void ContentObserver::setHandler(android::os::HandlerProxy handler)
{
mHandler = handler;
}
jobject ContentObserver::deliverSelfNotifications()
{
QJniObject res("java/lang/Boolean", true);
QJniEnvironment env;
return env->NewGlobalRef(res.object<jobject>());
}
void ContentObserver::onChange(jboolean selfChange)
{
mHandler.sendEmptyMessage(1);
}
void ContentObserver::onChange(jboolean selfChange, jobject uri)
{
mHandler.sendEmptyMessage(1);
}
void ContentObserver::onChange__ZLandroid_net_Uri_2I(jboolean selfChange, jobject uri, jint flags)
{
mHandler.sendEmptyMessage(1);
}
void ContentObserver::onChange__ZLjava_util_Collection_2I(jboolean selfChange, jobject uris, jint flags)
{
mHandler.sendEmptyMessage(1);
}

View File

@ -0,0 +1,24 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CONTENTOBSERVER_H
#define CONTENTOBSERVER_H
#include <qtjenny_output/jenny/proxy/android_database_ContentObserverProxy.h>
#include <qtjenny_output/jenny/proxy/android_os_HandlerProxy.h>
class ContentObserver : public android::database::ContentObserverInterface
{
private:
android::os::HandlerProxy mHandler;
public:
ContentObserver();
void setHandler(android::os::HandlerProxy handler);
virtual jobject deliverSelfNotifications();
virtual void onChange(jboolean selfChange);
virtual void onChange(jboolean selfChange, jobject uri);
virtual void onChange__ZLandroid_net_Uri_2I(jboolean selfChange, jobject uri, jint flags);
virtual void onChange__ZLjava_util_Collection_2I(jboolean selfChange, jobject uris, jint flags);
};
#endif // CONTENTOBSERVER_H

View File

@ -47,7 +47,7 @@
this triggers an annotation processor that processes the annotations in
\c GenerateCppCode.kt.
\snippet demos/qtjennydemo/qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator/GenerateCppCode.kt 1
\snippet demos/qtjennydemo/qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/GenerateCppCode.kt 1
The annotation processor then generates the C++ headers in the
\c qtjenny_output directory. Those C++ headers contain the needed JNI
@ -58,7 +58,7 @@
implements. These arguments are parsed in the QtJenny compiler and used
in the generation process.
\snippet demos/qtjennydemo/qtjenny_generator/app/build.gradle 2
\snippet demos/qtjennydemo/qtjenny_general/app/build.gradle 2
\section2 Using the generated C++ headers in the Qt Quick application

View File

@ -3,6 +3,8 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <nativedispatch.h>
#include "volumeandbrightnessobserver.h"
int main(int argc, char *argv[])
{
@ -14,6 +16,8 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv);
registerNativeInvocationHandler();
QQmlApplicationEngine engine;
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreationFailed, &app,

View File

@ -0,0 +1,39 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "nativedispatch.h"
#include <QDebug>
#include <QJniEnvironment>
jobject JNICALL
invokeNativeInvocationHandler(JNIEnv* env, jobject thiz,
jlong nativeHandle, jstring method, jobjectArray args)
{
NativeInterface* context = reinterpret_cast<NativeInterface*>(nativeHandle);
if (!context) {
qDebug() << "Evil stuff in native invocation handler!!";
return nullptr;
}
return context->qt_invoke(method, args);
}
void registerNativeInvocationHandler() {
QJniEnvironment env;
jclass clazz = env.findClass("org/qtproject/qtjennydemo/NativeInvocationHandler");
if (!clazz) {
qWarning("Failed to find class NativeInvocationHandler");
return;
}
JNINativeMethod method = {
"invokeNative", // Java method name
"(JLjava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", // JNI signature
reinterpret_cast<void*>(invokeNativeInvocationHandler) // Pointer to native function
};
if (!env.registerNativeMethods(clazz, {method})) {
qWarning("Failed to register native methods");
return;
}
}

View File

@ -0,0 +1,14 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef NATIVEDISPATCH_H
#define NATIVEDISPATCH_H
#include <QJniObject>
struct NativeInterface {
virtual jobject qt_invoke(jstring method, jobjectArray args) = 0;
};
void registerNativeInvocationHandler();
#endif // NATIVEDISPATCH_H

View File

@ -0,0 +1,89 @@
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdk 36
namespace "org.qtproject.qt.qtjenny_baseclass"
defaultConfig {
applicationId "org.qtproject.qt.qtjenny_baseclass"
minSdkVersion 28
targetSdkVersion 36
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = 17
targetCompatibility = 17
}
kotlinOptions {
jvmTarget = '17'
}
// make sure java compile is executed before ndk compile
// because we rely on Jenny generating cpp code
}
import org.jetbrains.kotlin.gradle.internal.KaptTask
tasks.withType(KaptTask) { task ->
def suffix = task.name.contains("kaptDebug") ? "_debug" : "_release"
annotationProcessorOptionProviders.add([new CommandLineArgumentProvider() {
@Override
Iterable<String> asArguments() {
return ["jenny.templateBuildSuffix=" + suffix]
}
}])
}
kapt {
arguments {
// pass arguments to jenny
// arg("jenny.threadSafe", "false")
//arg("jenny.errorLoggerFunction", "jennySampleErrorLog")
arg("jenny.outputDirectory", "$rootDir")
arg("jenny.templateDirectory", project.file('templates'))
arg("jenny.headerOnlyProxy", "true")
arg("jenny.useJniHelper", "false")
arg("jenny.useTemplates", "true")
// arg("jenny.fusionProxyHeaderName", "JennyFusionProxy.h")
}
}
dependencies {
implementation libs.qtjenny.annotation
kapt libs.qtjenny.compiler
}
tasks.register('copyAndRename', Copy) {
from "$rootDir/jenny/proxy" // Source directory
into "$rootDir/../android/src/org/qtproject/qtjennydemo/" // Destination directory
include '*_*_*Proxy.h' // Match files with multiple underscores
rename { String fileName ->
fileName.replaceAll(/.*_+/, '') // Remove all prefix parts ending in underscore
.replaceAll(/\.h$/, '.java') // Change extension
}
}
afterEvaluate {
// copy file to source after jenny generate
tasks.kaptReleaseKotlin.finalizedBy copyAndRename
}

View File

@ -0,0 +1 @@
android.useAndroidX=true

View File

@ -0,0 +1,13 @@
[versions]
agp = "8.9.1"
kotlin = "1.9.24"
qtjennyAnnotation = "1.0.0"
qtjennyCompiler = "1.0.0"
[libraries]
qtjenny-annotation = { module = "org.qtproject.qt:qtjenny-annotation", version.ref = "qtjennyAnnotation" }
qtjenny-compiler = { module = "org.qtproject.qt:qtjenny-compiler", version.ref = "qtjennyCompiler" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View File

@ -0,0 +1,6 @@
#Tue Aug 20 17:34:01 CST 2024
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@ -0,0 +1,177 @@
#!/usr/bin/env bash
#
# Copyright (C) 2015 the original author or authors.
# SPDX-License-Identifier: Apache-2.0
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@ -19,5 +19,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "qtjenny_generator"
rootProject.name = "qtjenny_baseclass"
include ':app'

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
</application>
</manifest>

View File

@ -0,0 +1,13 @@
package org.qtproject.qt.qtjenny_baseclass
import org.qtproject.qt.qtjenny.NativeProxy
import org.qtproject.qt.qtjenny.NativeClass
import org.qtproject.qt.qtjenny.NativeProxyForClasses
import android.database.ContentObserver
@NativeClass
@NativeProxy(allMethods = false, allFields = false)
@NativeProxyForClasses(namespace = "android::database", classes = [ContentObserver::class])
class GenerateCppCode {
}

View File

@ -0,0 +1,44 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@param jteData: JteData
!{
val rawParams = jteData.handyHelper.getJavaMethodParam(jteData.method!!.method)
val params = rawParams.split(",").map { it.trim() }.filter { it.isNotEmpty() }
val javaCode = if (params.isEmpty()) {
""
} else {
val parsedParams = params.mapNotNull {
val parts = it.split(" ")
if (parts.size == 2) {
parts[1]
} else null
}
val objectArrayCode = parsedParams.joinToString(", ")
"super($objectArrayCode);"
}
}
${jteData.handyHelper.getModifiers(jteData.method!!.method)} ${jteData.simpleClassName}Proxy(${jteData.handyHelper.getJavaMethodParam(jteData.method!!.method)}, NativeInvocationHandler qt_Handler) {
${javaCode}
mHandler = qt_Handler;
}

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.FieldIdDeclaration
@param fieldIdDeclaration:FieldIdDeclaration

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.FieldIdDeclaration
@param fieldIdDeclaration:FieldIdDeclaration

View File

@ -0,0 +1,21 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
*/

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,3 +16,4 @@
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData

View File

@ -0,0 +1,22 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
}
/*

View File

@ -0,0 +1,29 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
!{
val dotClassName = jteData.slashClassName.replace("/", ".")
}
package org.qtproject.qtjennydemo;
import ${dotClassName};
public class ${jteData.simpleClassName}Proxy extends ${jteData.simpleClassName} {
private NativeInvocationHandler mHandler;

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -0,0 +1,105 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@import javax.lang.model.type.TypeKind
@import javax.lang.model.element.Modifier
@param jteData: JteData
!{
val rawParams = jteData.handyHelper.getJavaMethodParam(jteData.method!!.method)
val params = rawParams.split(",").map { it.trim() }.filter { it.isNotEmpty() }
val javaCode = if (params.isEmpty()) {
"null"
} else {
val parsedParams = params.mapNotNull {
val parts = it.split(" ")
if (parts.size == 2) {
val type = parts[0]
val name = parts[1]
type to name
} else null
}
val objectArrayCode = parsedParams.joinToString(", ") { (type, name) ->
when (type) {
"int" -> "Integer.valueOf($name)"
"long" -> "Long.valueOf($name)"
"double" -> "Double.valueOf($name)"
"float" -> "Float.valueOf($name)"
"boolean" -> "Boolean.valueOf($name)"
"char" -> "Character.valueOf($name)"
"byte" -> "Byte.valueOf($name)"
"short" -> "Short.valueOf($name)"
else -> name
}
}
"new Object[] { $objectArrayCode }"
}
val superInvocationCode = if (params.isEmpty()) {
""
} else {
val parsedParams = params.mapNotNull {
val parts = it.split(" ")
if (parts.size == 2) {
parts[1]
} else null
}
val objectArrayCode = parsedParams.joinToString(", ")
"${objectArrayCode}";
}
}
!{
fun unboxFromObject(returnType: String, objectName: String): String {
return when (returnType) {
"int" -> "((Integer) $objectName).intValue()"
"boolean" -> "((Boolean) $objectName).booleanValue()"
"double" -> "((Double) $objectName).doubleValue()"
"long" -> "((Long) $objectName).longValue()"
"float" -> "((Float) $objectName).floatValue()"
"short" -> "((Short) $objectName).shortValue()"
"byte" -> "((Byte) $objectName).byteValue()"
"char" -> "((Character) $objectName).charValue()"
else -> "$objectName" // No unboxing needed
}
}
}
!{val classParam = if (jteData.rawStaticMod != "") "FULL_CLASS_NAME," else ""}
!{val thizParam = if (jteData.rawStaticMod != "") "QJniObject::" else "m_jniObject."}
@if (!jteData.method!!.method.modifiers.contains(Modifier.FINAL))
public ${jteData.method!!.method.returnType.toString()} ${jteData.method!!.method.simpleName.toString()}(${jteData.handyHelper.getJavaMethodParam(
jteData.method!!.method
)}) {
@if (jteData.method!!.method.returnType.toString() != "void")
Object res = mHandler.invoke("${jteData.method!!.method.simpleName.toString()}${jteData.method!!.resolvedPostFix}", ${javaCode});
if (res != null)
${jteData.returnStatement}${unboxFromObject(jteData.method!!.method.returnType.toString(), "res")};
@else
${jteData.returnStatement}${unboxFromObject(jteData.method!!.method.returnType.toString(), "mHandler.invoke(\"${jteData.method!!.method.simpleName.toString()}${jteData.method!!.resolvedPostFix}\", ${javaCode})")};
@endif
${jteData.returnStatement}super.${jteData.method!!.method.simpleName.toString()}(${superInvocationCode});
}
@endif

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,3 +15,4 @@
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -0,0 +1,73 @@
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdk 36
namespace "org.qtproject.qt.qtjenny_callback"
defaultConfig {
applicationId "org.qtproject.qt.qtjenny_callback"
minSdkVersion 28
targetSdkVersion 36
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = 17
targetCompatibility = 17
}
kotlinOptions {
jvmTarget = '17'
}
// make sure java compile is executed before ndk compile
// because we rely on Jenny generating cpp code
}
import org.jetbrains.kotlin.gradle.internal.KaptTask
tasks.withType(KaptTask) { task ->
def suffix = task.name.contains("kaptDebug") ? "_debug" : "_release"
annotationProcessorOptionProviders.add([new CommandLineArgumentProvider() {
@Override
Iterable<String> asArguments() {
return ["jenny.templateBuildSuffix=" + suffix]
}
}])
}
kapt {
arguments {
// pass arguments to jenny
// arg("jenny.threadSafe", "false")
//arg("jenny.errorLoggerFunction", "jennySampleErrorLog")
arg("jenny.outputDirectory", "$rootDir/../qtjenny_output")
arg("jenny.templateDirectory", project.file('templates'))
arg("jenny.headerOnlyProxy", "true")
arg("jenny.useJniHelper", "false")
arg("jenny.useTemplates", "true")
// arg("jenny.fusionProxyHeaderName", "JennyFusionProxy.h")
}
}
dependencies {
implementation libs.qtjenny.annotation
kapt libs.qtjenny.compiler
}

View File

@ -0,0 +1,5 @@
android.useAndroidX=true
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true

View File

@ -0,0 +1,13 @@
[versions]
agp = "8.9.1"
kotlin = "1.9.24"
qtjennyAnnotation = "1.0.0"
qtjennyCompiler = "1.0.0"
[libraries]
qtjenny-annotation = { module = "org.qtproject.qt:qtjenny-annotation", version.ref = "qtjennyAnnotation" }
qtjenny-compiler = { module = "org.qtproject.qt:qtjenny-compiler", version.ref = "qtjennyCompiler" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View File

@ -0,0 +1,6 @@
#Tue Aug 20 17:34:01 CST 2024
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@ -0,0 +1,177 @@
#!/usr/bin/env bash
#
# Copyright (C) 2015 the original author or authors.
# SPDX-License-Identifier: Apache-2.0
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@ -0,0 +1,23 @@
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "qtjenny_callback"
include ':app'

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
</application>
</manifest>

View File

@ -0,0 +1,15 @@
package org.qtproject.qt.qtjenny_callback
import org.qtproject.qt.qtjenny.NativeProxy
import org.qtproject.qt.qtjenny.NativeClass
import org.qtproject.qt.qtjenny.NativeProxyForClasses
import android.os.Handler
import android.database.ContentObserver
@NativeClass
@NativeProxy(allMethods = false, allFields = false)
@NativeProxyForClasses(namespace = "android::os", classes = [Handler.Callback::class])
@NativeProxyForClasses(namespace = "android::database", classes = [ContentObserver::class])
class GenerateCppCode {
}

View File

@ -0,0 +1,21 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@param jteData: JteData

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -0,0 +1,30 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@import javax.lang.model.type.TypeKind
@param jteData: JteData
!{val classParam = if (jteData.rawStaticMod != "") "FULL_CLASS_NAME," else ""}
!{val thizParam = if (jteData.rawStaticMod != "") "QJniObject::" else "m_jniObject."}
${jteData.fieldComment}
${jteData.rawStaticMod}auto get${jteData.fieldCamelCaseName}(${jteData.param}) ${jteData.constMod}{
return ${thizParam}get${jteData.static}Field<${jteData.jniReturnType}>(${classParam}"${
jteData.field!!.simpleName.toString()}");
}

View File

@ -0,0 +1,30 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@import javax.lang.model.type.TypeKind
@param jteData: JteData
!{val classParam = if (jteData.rawStaticMod != "") "FULL_CLASS_NAME," else ""}
!{val thizParam = if (jteData.rawStaticMod != "") "QJniObject::" else "m_jniObject."}
${jteData.fieldComment}
${jteData.rawStaticMod}void set${jteData.fieldCamelCaseName}(${jteData.param}) ${jteData.constMod}{
${thizParam}set${jteData.static}Field(${classParam}"${
jteData.field!!.simpleName.toString()}", ${jteData.fieldSetterParam});
}

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.FieldIdDeclaration
@param fieldIdDeclaration:FieldIdDeclaration

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.FieldIdDeclaration
@param fieldIdDeclaration:FieldIdDeclaration

View File

@ -0,0 +1,21 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
@if (jteData.environment.configurations.headerOnlyProxy)#endif // ${jteData.namespaceHelper.fileNamePrefix}${jteData.simpleClassName}Interface_H@endif

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -0,0 +1,19 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -19,3 +18,4 @@
@param jteData: JteData
};
${jteData.namespaceHelper.endNamespace()}

View File

@ -0,0 +1,76 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
${Constants.AUTO_GENERATE_NOTICE}
#ifndef ${jteData.namespaceHelper.fileNamePrefix}${jteData.simpleClassName}Interface_H
#define ${jteData.namespaceHelper.fileNamePrefix}${jteData.simpleClassName}Interface_H
#include <QJniObject>
#include <QJniArray>
#include <QString>
#include <cmath>
#include <functional>
#include <vector>
#include <nativedispatch.h>
${jteData.namespaceHelper.beginNamespace()}
class ${jteData.simpleClassName}Interface : public NativeInterface {
private:
struct qt_MethodReturn {bool matched; jobject result;};
QJniObject m_jniObject;
static constexpr double NaN = NAN;
std::vector<std::function<qt_MethodReturn(jstring, jobjectArray)>> m_dispatch;
public:
static constexpr auto FULL_CLASS_NAME = "${jteData.slashClassName}";
QJniObject getJniObject() const {return m_jniObject;}
const QJniObject* operator->() const {return &m_jniObject;}
template <class T> operator T() {return m_jniObject.object<T>();}
QJniObject qt_getProxy(const char* handlerClassName) {
QJniEnvironment env;
jlong nativeHandle = reinterpret_cast<jlong>(static_cast<NativeInterface*>(this));
QJniObject handler(handlerClassName, nativeHandle);
jclass interfaceClass = env.findClass(FULL_CLASS_NAME);
QJniArray<jclass> ifaceArray(1);
ifaceArray.setValue(0, interfaceClass);
QJniObject handlerClass = handler.callObjectMethod<jclass>("getClass");
QJniObject classLoader = handlerClass.callObjectMethod("getClassLoader", "()Ljava/lang/ClassLoader;");
QJniObject proxy = QJniObject::callStaticObjectMethod(
"java/lang/reflect/Proxy",
"newProxyInstance",
"(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;",
classLoader.object<jobject>(),
ifaceArray.arrayObject(),
handler.object<jobject>()
);
return proxy;
}
jobject qt_invoke(jstring method, jobjectArray args) override {
for (auto&& x : m_dispatch) {
qt_MethodReturn res = x(method, args);
if (res.matched)
return res.result;
}
return nullptr;
}

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -0,0 +1,75 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@import javax.lang.model.type.TypeKind
@import javax.lang.model.element.Modifier
@param jteData: JteData
!{
fun unbox(type: String, index: Int): String {
val argName = "arg$index"
return when (type) {
"int" -> "QJniObject(env->GetObjectArrayElement(args, $index)).callMethod<jint>(\"intValue\")"
"boolean" -> "QJniObject(env->GetObjectArrayElement(args, $index)).callMethod<jboolean>(\"booleanValue\")"
"double" -> "QJniOBject(env->GetObjectArrayElement(args, $index)).callMethod<jdouble>(\"doubleValue\")"
else -> "env->GetObjectArrayElement(args, $index)"
}
}
}
!{val classParam = if (jteData.rawStaticMod != "") "FULL_CLASS_NAME," else ""}
!{val thizParam = if (jteData.rawStaticMod != "") "QJniObject::" else "m_jniObject."}
!{val resultAssign = if (jteData.returnStatement != "") "res.result = " else ""}
!{val returnType = if (jteData.jniReturnType != "void") "jobject" else "void"}
@if (!jteData.method!!.method.modifiers.contains(Modifier.FINAL))
// method: ${jteData.handyHelper.getModifiers(jteData.method!!.method)} ${jteData.method!!.method.returnType.toString()} ${jteData.method!!.method.simpleName.toString()}(${
jteData.handyHelper.getJavaMethodParam(
jteData.method!!.method
)
})
virtual ${returnType} ${jteData.method!!.method.simpleName.toString()}${jteData.method!!.resolvedPostFix}(${jteData.param}) = 0;
!{
val methodName = jteData.method!!.method.simpleName.toString()
val paramCount = jteData.method!!.method.parameters.size
}
char ${methodName}${jteData.method!!.resolvedPostFix}_${paramCount}_var = [this] {
m_dispatch.push_back([this](jstring methodName, jobjectArray args) {
QJniEnvironment env;
qt_MethodReturn res{false, nullptr};
if (QJniObject(methodName).toString() == "${methodName}${jteData.method!!.resolvedPostFix}"
&& (!args || env->GetArrayLength(args) == ${paramCount})) {
${resultAssign}this->${methodName}${jteData.method!!.resolvedPostFix}(
@for (entry in jteData.method!!.method.parameters.withIndex())
${unbox(entry.value.asType().toString(), entry.index)}@if (entry.index < (paramCount-1)), @endif
@endfor
);
res.matched = true;
return res;
}
return res;
});
return 0;
}();
@endif

View File

@ -0,0 +1,31 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@param jteData: JteData
@if (jteData.useJniHelper)
@if (jteData.isStatic)
::jenny::Env env; assertInited(env.get());
@else
::jenny::Env env; ::jenny::LocalRef<jobject> jennyLocalRef = getThis(false); jobject thiz = jennyLocalRef.get();
@endif
@else
assertInited(env);
@endif

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -0,0 +1,25 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
@if (!jteData.useJniHelper)
${jteData.param}
@else
${jteData.param}
@endif

View File

@ -6,16 +6,15 @@ plugins {
apply plugin: "kotlin-kapt"
android {
namespace "org.qtproject.qt.qtjenny_generator"
compileSdk 35
namespace "org.qtproject.qt.qtjenny_general"
compileSdk 36
defaultConfig {
applicationId "org.qtproject.qt.qtjenny_generator"
applicationId "org.qtproject.qt.qtjenny_general"
minSdk 28
targetSdk 35
targetSdk 36
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true

View File

@ -1,13 +1,16 @@
package org.qtproject.qt.qtjenny_generator
package org.qtproject.qt.qtjenny_general
import android.app.Activity
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.os.BatteryManager
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.os.VibrationEffect
import android.os.Vibrator
@ -22,13 +25,17 @@ import org.qtproject.qt.qtjenny.NativeProxyForClasses
//! [1]
@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])
@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])
@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])
//! [1]
class GenerateCppCode {
}

View File

@ -1,4 +1,4 @@
package org.qtproject.qt.qtjenny_generator
package org.qtproject.qt.qtjenny_general
import androidx.activity.ComponentActivity

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">qtjenny_general</string>
</resources>

View File

@ -1,5 +1,5 @@
[versions]
agp = "8.7.0"
agp = "8.9.1"
kotlin = "1.9.24"
activity = "1.10.1"
qtjennyAnnotation = "1.0.0"

View File

@ -1,7 +1,7 @@
#Wed Jan 29 09:10:28 EET 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,23 @@
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "qtjenny_general"
include ':app'

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -23,6 +22,6 @@
// construct: ${jteData.handyHelper.getModifiers(jteData.method!!.method)} ${jteData.simpleClassName}(${jteData.handyHelper.getJavaMethodParam(jteData.method!!.method)})
static ${jteData.className} newInstance${jteData.method!!.resolvedPostFix}(${jteData.param}) {
${jteData.className} ret;
ret.m_jniObject = QJniObject(FULL_CLASS_NAME, "${jteData.handyHelper.getBinaryMethodSignature(jteData.method!!.method)}"${jteData.handyHelper.getJniMethodParamVal(jteData.clazz!!, jteData.method!!.method, jteData.useJniHelper)});
ret.m_jniObject = QJniObject(qt_FULL_CLASS_NAME, "${jteData.handyHelper.getBinaryMethodSignature(jteData.method!!.method)}"${jteData.handyHelper.getJniMethodParamVal(jteData.clazz!!, jteData.method!!.method, jteData.useJniHelper)});
return ret;
}

View File

@ -0,0 +1,17 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -0,0 +1,17 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.MethodIdDeclaration
@param methodIdDeclaration:MethodIdDeclaration

View File

@ -0,0 +1,69 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@import javax.lang.model.type.ArrayType
@import javax.lang.model.element.TypeElement
@import javax.lang.model.type.TypeKind
@import javax.lang.model.type.TypeMirror
@import javax.lang.model.type.DeclaredType
@param jteData: JteData
!{
fun getJniSignature(type: TypeMirror): String {
return when (type.kind) {
TypeKind.BOOLEAN -> "Z"
TypeKind.BYTE -> "B"
TypeKind.SHORT -> "S"
TypeKind.INT -> "I"
TypeKind.LONG -> "J"
TypeKind.CHAR -> "C"
TypeKind.FLOAT -> "F"
TypeKind.DOUBLE -> "D"
TypeKind.VOID -> "V"
TypeKind.ARRAY -> {
val arrayType = type as ArrayType
"[" + getJniSignature(arrayType.componentType)
}
TypeKind.DECLARED -> {
val declaredType = type as DeclaredType
val element = declaredType.asElement()
if (element is TypeElement) {
"L" + element.qualifiedName.toString().replace('.', '/') + ";"
} else {
// Fallback using type.toString()
"L" + type.toString().replace('.', '/') + ";"
}
}
else -> throw IllegalArgumentException("Unsupported type: $type")
}
}
}
!{val classParam = if (jteData.rawStaticMod != "") "qt_FULL_CLASS_NAME," else ""}
!{val thizParam = if (jteData.rawStaticMod != "") "QJniObject::" else "m_jniObject."}
${jteData.fieldComment}
${jteData.rawStaticMod}auto get${jteData.fieldCamelCaseName}(${jteData.param}) ${jteData.constMod}{
@if (jteData.jniReturnType == "jobject")
return ${thizParam}get${jteData.static}ObjectField(${classParam}"${
jteData.field!!.simpleName.toString()}", "${getJniSignature(jteData.field!!.asType())}");
@else
return ${thizParam}get${jteData.static}Field<${jteData.jniReturnType}>(${classParam}"${
jteData.field!!.simpleName.toString()}");
@endif
}

View File

@ -0,0 +1,30 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@import org.qtproject.qt.qtjenny.HandyHelper
@import org.qtproject.qt.qtjenny.MethodOverloadResolver.MethodRecord
@import javax.lang.model.type.TypeKind
@param jteData: JteData
!{val classParam = if (jteData.rawStaticMod != "") "qt_FULL_CLASS_NAME," else ""}
!{val thizParam = if (jteData.rawStaticMod != "") "QJniObject::" else "m_jniObject."}
${jteData.fieldComment}
${jteData.rawStaticMod}void set${jteData.fieldCamelCaseName}(${jteData.param}) ${jteData.constMod}{
${thizParam}set${jteData.static}Field(${classParam}"${
jteData.field!!.simpleName.toString()}", ${jteData.fieldSetterParam});
}

View File

@ -0,0 +1,17 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.FieldIdDeclaration
@param fieldIdDeclaration:FieldIdDeclaration

View File

@ -0,0 +1,17 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.FieldIdDeclaration
@param fieldIdDeclaration:FieldIdDeclaration

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -19,4 +18,3 @@
@param jteData: JteData
@if (jteData.environment.configurations.headerOnlyProxy)#endif // ${jteData.namespaceHelper.fileNamePrefix}${jteData.className}_H@endif

View File

@ -0,0 +1,17 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -0,0 +1,18 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData

View File

@ -0,0 +1,20 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
};
${jteData.namespaceHelper.endNamespace()}

View File

@ -0,0 +1,53 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@import org.qtproject.qt.qtjenny.Constants
@param jteData: JteData
${Constants.AUTO_GENERATE_NOTICE}
#ifndef ${jteData.namespaceHelper.fileNamePrefix}${jteData.className}_H
#define ${jteData.namespaceHelper.fileNamePrefix}${jteData.className}_H
#include <QJniObject>
#include <cmath>
${jteData.namespaceHelper.beginNamespace()}
class ${jteData.className} {
private:
QJniObject m_jniObject;
static constexpr double NaN = NAN;
public:
static constexpr auto qt_FULL_CLASS_NAME = "${jteData.slashClassName}";
QJniObject qt_getJniObject() const {return m_jniObject;}
const QJniObject* operator->() const {return &m_jniObject;}
template <class T> operator T() {return m_jniObject.object<T>();}
${jteData.className}() {}
${jteData.className}(const QJniObject& jniObject) {
m_jniObject = jniObject;
}
${jteData.className}(jobject globalRef) {
m_jniObject = QJniObject(globalRef);
}
static ${jteData.className} qt_fromLocalRef(jobject localRef) {
${jteData.className} res;
res.m_jniObject = QJniObject::fromLocalRef(localRef);
return res;
}
jobject qt_asGlobalRef() {
QJniEnvironment env;
return env->NewGlobalRef(m_jniObject.object());
}

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -31,8 +30,8 @@ private:
QJniObject m_jniObject;
static constexpr double NaN = NAN;
public:
static constexpr auto FULL_CLASS_NAME = "${jteData.slashClassName}";
QJniObject getJniObject() const {return m_jniObject;}
static constexpr auto qt_FULL_CLASS_NAME = "${jteData.slashClassName}";
QJniObject qt_getJniObject() const {return m_jniObject;}
const QJniObject* operator->() const {return &m_jniObject;}
template <class T> operator T() {return m_jniObject.object<T>();}
${jteData.className}() {}
@ -42,9 +41,8 @@ public:
${jteData.className}(jobject globalRef) {
m_jniObject = QJniObject(globalRef);
}
static ${jteData.className} fromLocalRef(jobject localRef) {
static ${jteData.className} qt_fromLocalRef(jobject localRef) {
${jteData.className} res;
res.m_jniObject = QJniObject::fromLocalRef(localRef);
return res;
res.m_jniObject = QJniObject::fromLocalRef(localRef);
return res;
}

View File

@ -0,0 +1,17 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -0,0 +1,17 @@
<%--
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
See the License for the specific language governing permissions and
limitations under the License.
--%>
@import org.qtproject.qt.qtjenny.JteData
@param jteData: JteData

View File

@ -1,6 +1,5 @@
<%--
Copyright (C) 2025 The Qt Company Ltd.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2024 The Qt Company Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -20,7 +19,7 @@
@import javax.lang.model.type.TypeKind
@param jteData: JteData
!{val classParam = if (jteData.rawStaticMod != "") "FULL_CLASS_NAME," else ""}
!{val classParam = if (jteData.rawStaticMod != "") "qt_FULL_CLASS_NAME," else ""}
!{val thizParam = if (jteData.rawStaticMod != "") "QJniObject::" else "m_jniObject."}
// method: ${jteData.handyHelper.getModifiers(jteData.method!!.method)} ${jteData.method!!.method.returnType.toString()} ${jteData.method!!.method.simpleName.toString()}(${

Some files were not shown because too many files have changed in this diff Show More