From c71559facef912ac00d844014399845938e12f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konsta=20Alaj=C3=A4rvi?= Date: Fri, 31 Oct 2025 16:56:10 +0200 Subject: [PATCH] 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 --- REUSE.toml | 32 +++- examples/demos/qtjennydemo/.gitattributes | 1 - examples/demos/qtjennydemo/.gitignore | 6 + examples/demos/qtjennydemo/CMakeLists.txt | 14 +- examples/demos/qtjennydemo/Main.qml | 89 ++++----- .../qtjennydemo/android/AndroidManifest.xml | 2 +- .../qtjennydemo/NativeInvocationHandler.java | 30 +++ examples/demos/qtjennydemo/backend.cpp | 161 +++++++++------- examples/demos/qtjennydemo/backend.h | 57 ++++-- .../demos/qtjennydemo/contentobserver.cpp | 38 ++++ examples/demos/qtjennydemo/contentobserver.h | 24 +++ .../qtjennydemo/doc/src/qtjennydemo.qdoc | 4 +- examples/demos/qtjennydemo/main.cpp | 4 + examples/demos/qtjennydemo/nativedispatch.cpp | 39 ++++ examples/demos/qtjennydemo/nativedispatch.h | 14 ++ .../qtjenny_baseclass/build.gradle | 89 +++++++++ .../qtjenny_baseclass/gradle.properties | 1 + .../gradle/libs.versions.toml | 13 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../qtjennydemo/qtjenny_baseclass/gradlew | 177 ++++++++++++++++++ .../settings.gradle | 2 +- .../src/main/AndroidManifest.xml | 7 + .../qt/qtjenny_baseclass/GenerateCppCode.kt | 13 ++ .../templates/constructor_definition.kte | 44 +++++ .../constructors_ids_declarations.kte | 4 +- .../constructors_ids_initialisations.kte | 4 +- .../templates/field_getter.kte | 3 +- .../templates/field_setter.kte | 3 +- .../templates/fields_ids_declarations.kte | 4 +- .../templates/fields_ids_initialisations.kte | 4 +- .../templates/header_final_postamble.kte | 21 +++ .../templates/header_initfunctions.kte | 4 +- .../templates/header_initvars.kte | 4 +- .../templates/header_postamble.kte | 22 +++ .../templates/header_preamble.kte | 29 +++ .../templates/initclass_postamble.kte | 4 +- .../templates/initclass_preamble.kte | 4 +- .../templates/method_definition.kte | 105 +++++++++++ .../templates/method_prologue.kte | 3 +- .../templates/methods_ids_declarations.kte | 4 +- .../templates/methods_ids_initialisations.kte | 4 +- .../templates/param.kte | 3 +- .../qtjennydemo/qtjenny_callback/build.gradle | 73 ++++++++ .../qtjenny_callback/gradle.properties | 5 + .../gradle/libs.versions.toml | 13 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../qtjennydemo/qtjenny_callback/gradlew | 177 ++++++++++++++++++ .../qtjenny_callback/settings.gradle | 23 +++ .../src/main/AndroidManifest.xml | 7 + .../qt/qtjenny_callback/GenerateCppCode.kt | 15 ++ .../templates/constructor_definition.kte | 21 +++ .../constructors_ids_declarations.kte | 18 ++ .../constructors_ids_initialisations.kte | 18 ++ .../templates/field_getter.kte | 30 +++ .../templates/field_setter.kte | 30 +++ .../templates/fields_ids_declarations.kte | 18 ++ .../templates/fields_ids_initialisations.kte | 18 ++ .../templates/header_final_postamble.kte | 21 +++ .../templates/header_initfunctions.kte | 18 ++ .../templates/header_initvars.kte | 19 ++ .../templates/header_postamble.kte | 4 +- .../templates/header_preamble.kte | 76 ++++++++ .../templates/initclass_postamble.kte | 18 ++ .../templates/initclass_preamble.kte | 18 ++ .../templates/method_definition.kte | 75 ++++++++ .../templates/method_prologue.kte | 31 +++ .../templates/methods_ids_declarations.kte | 18 ++ .../templates/methods_ids_initialisations.kte | 18 ++ .../qtjenny_callback/templates/param.kte | 25 +++ .../app/build.gradle | 9 +- .../app/src/main/AndroidManifest.xml | 0 .../qt/qtjenny_general}/GenerateCppCode.kt | 17 +- .../qt/qtjenny_general}/MainActivity.kt | 2 +- .../app/src/main/res/values/strings.xml | 3 + .../build.gradle | 0 .../gradle.properties | 0 .../gradle/libs.versions.toml | 2 +- .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradlew | 0 .../gradlew.bat | 0 .../qtjenny_general/settings.gradle | 23 +++ .../templates/constructor_definition.kte | 5 +- .../constructors_ids_declarations.kte | 17 ++ .../constructors_ids_initialisations.kte | 17 ++ .../templates/field_getter.kte | 69 +++++++ .../templates/field_setter.kte | 30 +++ .../templates/fields_ids_declarations.kte | 17 ++ .../templates/fields_ids_initialisations.kte | 17 ++ .../templates/header_final_postamble.kte | 4 +- .../templates/header_initfunctions.kte | 17 ++ .../templates/header_initvars.kte | 18 ++ .../templates/header_postamble.kte | 20 ++ .../templates/header_preamble.kte | 53 ++++++ .../templates/header_preamble.kte~} | 14 +- .../templates/initclass_postamble.kte | 17 ++ .../templates/initclass_preamble.kte | 17 ++ .../templates/method_definition.kte | 5 +- .../templates/method_prologue.kte | 30 +++ .../templates/methods_ids_declarations.kte | 17 ++ .../templates/methods_ids_initialisations.kte | 17 ++ .../qtjenny_general/templates/param.kte | 24 +++ .../app/src/main/res/values/strings.xml | 3 - .../volumeandbrightnessobserver.cpp | 60 ++++++ .../qtjennydemo/volumeandbrightnessobserver.h | 41 ++++ licenseRule.json | 40 +++- 108 files changed, 2324 insertions(+), 212 deletions(-) delete mode 100644 examples/demos/qtjennydemo/.gitattributes create mode 100644 examples/demos/qtjennydemo/android/src/org/qtproject/qtjennydemo/NativeInvocationHandler.java create mode 100644 examples/demos/qtjennydemo/contentobserver.cpp create mode 100644 examples/demos/qtjennydemo/contentobserver.h create mode 100644 examples/demos/qtjennydemo/nativedispatch.cpp create mode 100644 examples/demos/qtjennydemo/nativedispatch.h create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/build.gradle create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/gradle.properties create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/gradle/libs.versions.toml create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/demos/qtjennydemo/qtjenny_baseclass/gradlew rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/settings.gradle (92%) create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/src/main/AndroidManifest.xml create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/src/main/java/org/qtproject/qt/qtjenny_baseclass/GenerateCppCode.kt create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructor_definition.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/constructors_ids_declarations.kte (88%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/constructors_ids_initialisations.kte (88%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/field_getter.kte (94%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/field_setter.kte (93%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/fields_ids_declarations.kte (88%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/fields_ids_initialisations.kte (88%) create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_final_postamble.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/header_initfunctions.kte (88%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/header_initvars.kte (89%) create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_postamble.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_preamble.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/initclass_postamble.kte (88%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/initclass_preamble.kte (88%) create mode 100644 examples/demos/qtjennydemo/qtjenny_baseclass/templates/method_definition.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/method_prologue.kte (92%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/methods_ids_declarations.kte (88%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/methods_ids_initialisations.kte (88%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_baseclass}/templates/param.kte (90%) create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/build.gradle create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/gradle.properties create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/gradle/libs.versions.toml create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/demos/qtjennydemo/qtjenny_callback/gradlew create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/settings.gradle create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/src/main/AndroidManifest.xml create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/src/main/java/org/qtproject/qt/qtjenny_callback/GenerateCppCode.kt create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/constructor_definition.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_declarations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_initialisations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/field_getter.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/field_setter.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_declarations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_initialisations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/header_final_postamble.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/header_initfunctions.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/header_initvars.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_callback}/templates/header_postamble.kte (89%) create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/header_preamble.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_postamble.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_preamble.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/method_definition.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/method_prologue.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_declarations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_initialisations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_callback/templates/param.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/app/build.gradle (91%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/app/src/main/AndroidManifest.xml (100%) rename examples/demos/qtjennydemo/{qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator => qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general}/GenerateCppCode.kt (69%) rename examples/demos/qtjennydemo/{qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator => qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general}/MainActivity.kt (67%) create mode 100644 examples/demos/qtjennydemo/qtjenny_general/app/src/main/res/values/strings.xml rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/build.gradle (100%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/gradle.properties (100%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/gradle/libs.versions.toml (97%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/gradle/wrapper/gradle-wrapper.properties (92%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/gradlew (100%) rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/gradlew.bat (100%) create mode 100644 examples/demos/qtjennydemo/qtjenny_general/settings.gradle rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/templates/constructor_definition.kte (77%) create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_declarations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_initialisations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/field_getter.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/field_setter.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_declarations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_initialisations.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/templates/header_final_postamble.kte (90%) create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/header_initfunctions.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/header_initvars.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/header_postamble.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/header_preamble.kte rename examples/demos/qtjennydemo/{qtjenny_generator/templates/header_preamble.kte => qtjenny_general/templates/header_preamble.kte~} (80%) create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/initclass_postamble.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/initclass_preamble.kte rename examples/demos/qtjennydemo/{qtjenny_generator => qtjenny_general}/templates/method_definition.kte (91%) create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/method_prologue.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_declarations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_initialisations.kte create mode 100644 examples/demos/qtjennydemo/qtjenny_general/templates/param.kte delete mode 100644 examples/demos/qtjennydemo/qtjenny_generator/app/src/main/res/values/strings.xml create mode 100644 examples/demos/qtjennydemo/volumeandbrightnessobserver.cpp create mode 100644 examples/demos/qtjennydemo/volumeandbrightnessobserver.h diff --git a/REUSE.toml b/REUSE.toml index 9319fe243..5da05d2a1 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -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." diff --git a/examples/demos/qtjennydemo/.gitattributes b/examples/demos/qtjennydemo/.gitattributes deleted file mode 100644 index cc0ba0481..000000000 --- a/examples/demos/qtjennydemo/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -qtjenny_generator/gradlew text eol=lf diff --git a/examples/demos/qtjennydemo/.gitignore b/examples/demos/qtjennydemo/.gitignore index f1f376447..ff1c88275 100644 --- a/examples/demos/qtjennydemo/.gitignore +++ b/examples/demos/qtjennydemo/.gitignore @@ -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 diff --git a/examples/demos/qtjennydemo/CMakeLists.txt b/examples/demos/qtjennydemo/CMakeLists.txt index dc2abb881..166e6c7b3 100644 --- a/examples/demos/qtjennydemo/CMakeLists.txt +++ b/examples/demos/qtjennydemo/CMakeLists.txt @@ -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 diff --git a/examples/demos/qtjennydemo/Main.qml b/examples/demos/qtjennydemo/Main.qml index e15f42fb3..53910ba8c 100644 --- a/examples/demos/qtjennydemo/Main.qml +++ b/examples/demos/qtjennydemo/Main.qml @@ -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 } } } diff --git a/examples/demos/qtjennydemo/android/AndroidManifest.xml b/examples/demos/qtjennydemo/android/AndroidManifest.xml index f9b116c30..16123774d 100644 --- a/examples/demos/qtjennydemo/android/AndroidManifest.xml +++ b/examples/demos/qtjennydemo/android/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/examples/demos/qtjennydemo/android/src/org/qtproject/qtjennydemo/NativeInvocationHandler.java b/examples/demos/qtjennydemo/android/src/org/qtproject/qtjennydemo/NativeInvocationHandler.java new file mode 100644 index 000000000..34fedc27b --- /dev/null +++ b/examples/demos/qtjennydemo/android/src/org/qtproject/qtjennydemo/NativeInvocationHandler.java @@ -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); + } +} diff --git a/examples/demos/qtjennydemo/backend.cpp b/examples/demos/qtjennydemo/backend.cpp index 1710c8f59..3ed9f9eb4 100644 --- a/examples/demos/qtjennydemo/backend.cpp +++ b/examples/demos/qtjennydemo/backend.cpp @@ -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()); + m_brightness = m_system.getInt(m_context.getContentResolver().object(), + QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object()); + 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(), - QJniObject::fromString("zen_mode").object()) != 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()); - - // 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(), - QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object()); - - 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(), - QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object(), - 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(), + QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object(), + 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()); + m_context.startActivity(m_intent); +} diff --git a/examples/demos/qtjennydemo/backend.h b/examples/demos/qtjennydemo/backend.h index 0934c3044..82f199634 100644 --- a/examples/demos/qtjennydemo/backend.h +++ b/examples/demos/qtjennydemo/backend.h @@ -11,7 +11,6 @@ #include #include #include - #include #include #include @@ -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; }; diff --git a/examples/demos/qtjennydemo/contentobserver.cpp b/examples/demos/qtjennydemo/contentobserver.cpp new file mode 100644 index 000000000..1fc0c1af0 --- /dev/null +++ b/examples/demos/qtjennydemo/contentobserver.cpp @@ -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 + +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()); +} + +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); +} + diff --git a/examples/demos/qtjennydemo/contentobserver.h b/examples/demos/qtjennydemo/contentobserver.h new file mode 100644 index 000000000..e63b2fdf8 --- /dev/null +++ b/examples/demos/qtjennydemo/contentobserver.h @@ -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 +#include + +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 diff --git a/examples/demos/qtjennydemo/doc/src/qtjennydemo.qdoc b/examples/demos/qtjennydemo/doc/src/qtjennydemo.qdoc index 6adc19687..bf0027bd1 100644 --- a/examples/demos/qtjennydemo/doc/src/qtjennydemo.qdoc +++ b/examples/demos/qtjennydemo/doc/src/qtjennydemo.qdoc @@ -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 diff --git a/examples/demos/qtjennydemo/main.cpp b/examples/demos/qtjennydemo/main.cpp index 8c3e01063..b1578a751 100644 --- a/examples/demos/qtjennydemo/main.cpp +++ b/examples/demos/qtjennydemo/main.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#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, diff --git a/examples/demos/qtjennydemo/nativedispatch.cpp b/examples/demos/qtjennydemo/nativedispatch.cpp new file mode 100644 index 000000000..06b798c7c --- /dev/null +++ b/examples/demos/qtjennydemo/nativedispatch.cpp @@ -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 +#include + +jobject JNICALL + +invokeNativeInvocationHandler(JNIEnv* env, jobject thiz, + jlong nativeHandle, jstring method, jobjectArray args) +{ + NativeInterface* context = reinterpret_cast(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(invokeNativeInvocationHandler) // Pointer to native function + }; + + if (!env.registerNativeMethods(clazz, {method})) { + qWarning("Failed to register native methods"); + return; + } +} diff --git a/examples/demos/qtjennydemo/nativedispatch.h b/examples/demos/qtjennydemo/nativedispatch.h new file mode 100644 index 000000000..d47d79233 --- /dev/null +++ b/examples/demos/qtjennydemo/nativedispatch.h @@ -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 + +struct NativeInterface { + virtual jobject qt_invoke(jstring method, jobjectArray args) = 0; +}; + +void registerNativeInvocationHandler(); + +#endif // NATIVEDISPATCH_H diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/build.gradle b/examples/demos/qtjennydemo/qtjenny_baseclass/build.gradle new file mode 100644 index 000000000..cee913db5 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/build.gradle @@ -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 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 +} diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/gradle.properties b/examples/demos/qtjennydemo/qtjenny_baseclass/gradle.properties new file mode 100644 index 000000000..5bac8ac50 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX=true diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/libs.versions.toml b/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/libs.versions.toml new file mode 100644 index 000000000..f1210049a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/libs.versions.toml @@ -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" } diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/wrapper/gradle-wrapper.jar b/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

<5G2M1WZP5 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 F{|7XC?P&l2 literal 0 HcmV?d00001 diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/wrapper/gradle-wrapper.properties b/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..cb15e3270 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/gradlew b/examples/demos/qtjennydemo/qtjenny_baseclass/gradlew new file mode 100755 index 000000000..b9708336a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/gradlew @@ -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 "$@" diff --git a/examples/demos/qtjennydemo/qtjenny_generator/settings.gradle b/examples/demos/qtjennydemo/qtjenny_baseclass/settings.gradle similarity index 92% rename from examples/demos/qtjennydemo/qtjenny_generator/settings.gradle rename to examples/demos/qtjennydemo/qtjenny_baseclass/settings.gradle index a84b35b45..f276f8a38 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/settings.gradle +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/settings.gradle @@ -19,5 +19,5 @@ dependencyResolutionManagement { } } -rootProject.name = "qtjenny_generator" +rootProject.name = "qtjenny_baseclass" include ':app' diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/src/main/AndroidManifest.xml b/examples/demos/qtjennydemo/qtjenny_baseclass/src/main/AndroidManifest.xml new file mode 100644 index 000000000..e40768108 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/src/main/java/org/qtproject/qt/qtjenny_baseclass/GenerateCppCode.kt b/examples/demos/qtjennydemo/qtjenny_baseclass/src/main/java/org/qtproject/qt/qtjenny_baseclass/GenerateCppCode.kt new file mode 100644 index 000000000..9230de545 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/src/main/java/org/qtproject/qt/qtjenny_baseclass/GenerateCppCode.kt @@ -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 { +} diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructor_definition.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructor_definition.kte new file mode 100644 index 000000000..b87962ae5 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructor_definition.kte @@ -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; + } diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/constructors_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructors_ids_declarations.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/constructors_ids_declarations.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructors_ids_declarations.kte index 45e4a2c66..c9aaa401f 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/constructors_ids_declarations.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructors_ids_declarations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/constructors_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructors_ids_initialisations.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/constructors_ids_initialisations.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructors_ids_initialisations.kte index 45e4a2c66..c9aaa401f 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/constructors_ids_initialisations.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/constructors_ids_initialisations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/field_getter.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/field_getter.kte similarity index 94% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/field_getter.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/field_getter.kte index 2cad7c6a8..f12e20dad 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/field_getter.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/field_getter.kte @@ -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. diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/field_setter.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/field_setter.kte similarity index 93% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/field_setter.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/field_setter.kte index f13214a16..2e1a37b81 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/field_setter.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/field_setter.kte @@ -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. diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/fields_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/fields_ids_declarations.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/fields_ids_declarations.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/fields_ids_declarations.kte index 1ac3f2617..38f8fa01d 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/fields_ids_declarations.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/fields_ids_declarations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/fields_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/fields_ids_initialisations.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/fields_ids_initialisations.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/fields_ids_initialisations.kte index 1ac3f2617..38f8fa01d 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/fields_ids_initialisations.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/fields_ids_initialisations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_final_postamble.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_final_postamble.kte new file mode 100644 index 000000000..c84902309 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_final_postamble.kte @@ -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 + +*/ + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_initfunctions.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_initfunctions.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/header_initfunctions.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_initfunctions.kte index b56fe996f..44d8dfe5f 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_initfunctions.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_initfunctions.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_initvars.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_initvars.kte similarity index 89% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/header_initvars.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_initvars.kte index 0e390414b..cb188dfd7 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_initvars.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_initvars.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_postamble.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_postamble.kte new file mode 100644 index 000000000..826cb6d5e --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_postamble.kte @@ -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 +} + +/* + diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_preamble.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_preamble.kte new file mode 100644 index 000000000..05f09a0e9 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/header_preamble.kte @@ -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; + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/initclass_postamble.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/initclass_postamble.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/initclass_postamble.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/initclass_postamble.kte index b56fe996f..44d8dfe5f 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/initclass_postamble.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/initclass_postamble.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/initclass_preamble.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/initclass_preamble.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/initclass_preamble.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/initclass_preamble.kte index b56fe996f..44d8dfe5f 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/initclass_preamble.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/initclass_preamble.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_baseclass/templates/method_definition.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/method_definition.kte new file mode 100644 index 000000000..05a4d8074 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/method_definition.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/method_prologue.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/method_prologue.kte similarity index 92% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/method_prologue.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/method_prologue.kte index e0fe9490d..1d4c2b551 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/method_prologue.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/method_prologue.kte @@ -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. diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/methods_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/methods_ids_declarations.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/methods_ids_declarations.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/methods_ids_declarations.kte index 45e4a2c66..c9aaa401f 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/methods_ids_declarations.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/methods_ids_declarations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/methods_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/methods_ids_initialisations.kte similarity index 88% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/methods_ids_initialisations.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/methods_ids_initialisations.kte index 45e4a2c66..c9aaa401f 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/methods_ids_initialisations.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/methods_ids_initialisations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/param.kte b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/param.kte similarity index 90% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/param.kte rename to examples/demos/qtjennydemo/qtjenny_baseclass/templates/param.kte index f16f7d6f5..728c1330d 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/param.kte +++ b/examples/demos/qtjennydemo/qtjenny_baseclass/templates/param.kte @@ -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. diff --git a/examples/demos/qtjennydemo/qtjenny_callback/build.gradle b/examples/demos/qtjennydemo/qtjenny_callback/build.gradle new file mode 100644 index 000000000..70d5b533c --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/build.gradle @@ -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 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 +} + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/gradle.properties b/examples/demos/qtjennydemo/qtjenny_callback/gradle.properties new file mode 100644 index 000000000..9e38f5f90 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/gradle.properties @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_callback/gradle/libs.versions.toml b/examples/demos/qtjennydemo/qtjenny_callback/gradle/libs.versions.toml new file mode 100644 index 000000000..f1210049a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/gradle/libs.versions.toml @@ -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" } diff --git a/examples/demos/qtjennydemo/qtjenny_callback/gradle/wrapper/gradle-wrapper.jar b/examples/demos/qtjennydemo/qtjenny_callback/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

<5G2M1WZP5 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 F{|7XC?P&l2 literal 0 HcmV?d00001 diff --git a/examples/demos/qtjennydemo/qtjenny_callback/gradle/wrapper/gradle-wrapper.properties b/examples/demos/qtjennydemo/qtjenny_callback/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..cb15e3270 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_callback/gradlew b/examples/demos/qtjennydemo/qtjenny_callback/gradlew new file mode 100755 index 000000000..b9708336a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/gradlew @@ -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 "$@" diff --git a/examples/demos/qtjennydemo/qtjenny_callback/settings.gradle b/examples/demos/qtjennydemo/qtjenny_callback/settings.gradle new file mode 100644 index 000000000..7cf335802 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/settings.gradle @@ -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' diff --git a/examples/demos/qtjennydemo/qtjenny_callback/src/main/AndroidManifest.xml b/examples/demos/qtjennydemo/qtjenny_callback/src/main/AndroidManifest.xml new file mode 100644 index 000000000..e40768108 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/src/main/java/org/qtproject/qt/qtjenny_callback/GenerateCppCode.kt b/examples/demos/qtjennydemo/qtjenny_callback/src/main/java/org/qtproject/qt/qtjenny_callback/GenerateCppCode.kt new file mode 100644 index 000000000..4e981b02b --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/src/main/java/org/qtproject/qt/qtjenny_callback/GenerateCppCode.kt @@ -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 { +} diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/constructor_definition.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/constructor_definition.kte new file mode 100644 index 000000000..b7636e8fc --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/constructor_definition.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_declarations.kte new file mode 100644 index 000000000..c9aaa401f --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_declarations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_initialisations.kte new file mode 100644 index 000000000..c9aaa401f --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/constructors_ids_initialisations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/field_getter.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/field_getter.kte new file mode 100644 index 000000000..f12e20dad --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/field_getter.kte @@ -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()}"); + + } + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/field_setter.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/field_setter.kte new file mode 100644 index 000000000..2e1a37b81 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/field_setter.kte @@ -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}); + } + + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_declarations.kte new file mode 100644 index 000000000..38f8fa01d --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_declarations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_initialisations.kte new file mode 100644 index 000000000..38f8fa01d --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/fields_ids_initialisations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/header_final_postamble.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_final_postamble.kte new file mode 100644 index 000000000..52db37a46 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_final_postamble.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/header_initfunctions.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_initfunctions.kte new file mode 100644 index 000000000..44d8dfe5f --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_initfunctions.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/header_initvars.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_initvars.kte new file mode 100644 index 000000000..cb188dfd7 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_initvars.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_postamble.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_postamble.kte similarity index 89% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/header_postamble.kte rename to examples/demos/qtjennydemo/qtjenny_callback/templates/header_postamble.kte index d22872dce..0e0b7ceb3 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_postamble.kte +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_postamble.kte @@ -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()} + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/header_preamble.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_preamble.kte new file mode 100644 index 000000000..1dd93f4a0 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/header_preamble.kte @@ -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 +#include +#include +#include +#include +#include +#include + +${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> 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 operator T() {return m_jniObject.object();} + + QJniObject qt_getProxy(const char* handlerClassName) { + QJniEnvironment env; + jlong nativeHandle = reinterpret_cast(static_cast(this)); + + QJniObject handler(handlerClassName, nativeHandle); + + jclass interfaceClass = env.findClass(FULL_CLASS_NAME); + QJniArray ifaceArray(1); + ifaceArray.setValue(0, interfaceClass); + QJniObject handlerClass = handler.callObjectMethod("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(), + ifaceArray.arrayObject(), + handler.object() + ); + 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; + } + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_postamble.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_postamble.kte new file mode 100644 index 000000000..44d8dfe5f --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_postamble.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_preamble.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_preamble.kte new file mode 100644 index 000000000..44d8dfe5f --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/initclass_preamble.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/method_definition.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/method_definition.kte new file mode 100644 index 000000000..da6a11d87 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/method_definition.kte @@ -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(\"intValue\")" + "boolean" -> "QJniObject(env->GetObjectArrayElement(args, $index)).callMethod(\"booleanValue\")" + "double" -> "QJniOBject(env->GetObjectArrayElement(args, $index)).callMethod(\"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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/method_prologue.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/method_prologue.kte new file mode 100644 index 000000000..1d4c2b551 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/method_prologue.kte @@ -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 jennyLocalRef = getThis(false); jobject thiz = jennyLocalRef.get(); + @endif +@else +assertInited(env); +@endif + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_declarations.kte new file mode 100644 index 000000000..c9aaa401f --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_declarations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_initialisations.kte new file mode 100644 index 000000000..c9aaa401f --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/methods_ids_initialisations.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_callback/templates/param.kte b/examples/demos/qtjennydemo/qtjenny_callback/templates/param.kte new file mode 100644 index 000000000..728c1330d --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_callback/templates/param.kte @@ -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 + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/app/build.gradle b/examples/demos/qtjennydemo/qtjenny_general/app/build.gradle similarity index 91% rename from examples/demos/qtjennydemo/qtjenny_generator/app/build.gradle rename to examples/demos/qtjennydemo/qtjenny_general/app/build.gradle index 24dc382aa..3f86bc21a 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/app/build.gradle +++ b/examples/demos/qtjennydemo/qtjenny_general/app/build.gradle @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/AndroidManifest.xml b/examples/demos/qtjennydemo/qtjenny_general/app/src/main/AndroidManifest.xml similarity index 100% rename from examples/demos/qtjennydemo/qtjenny_generator/app/src/main/AndroidManifest.xml rename to examples/demos/qtjennydemo/qtjenny_general/app/src/main/AndroidManifest.xml diff --git a/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator/GenerateCppCode.kt b/examples/demos/qtjennydemo/qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/GenerateCppCode.kt similarity index 69% rename from examples/demos/qtjennydemo/qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator/GenerateCppCode.kt rename to examples/demos/qtjennydemo/qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/GenerateCppCode.kt index a7d10af32..3f8426829 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator/GenerateCppCode.kt +++ b/examples/demos/qtjennydemo/qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/GenerateCppCode.kt @@ -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 { } diff --git a/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator/MainActivity.kt b/examples/demos/qtjennydemo/qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/MainActivity.kt similarity index 67% rename from examples/demos/qtjennydemo/qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator/MainActivity.kt rename to examples/demos/qtjennydemo/qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/MainActivity.kt index 63c183465..d39461f9a 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/java/org/qtproject/qt/qtjenny_generator/MainActivity.kt +++ b/examples/demos/qtjennydemo/qtjenny_general/app/src/main/java/org/qtproject/qt/qtjenny_general/MainActivity.kt @@ -1,4 +1,4 @@ -package org.qtproject.qt.qtjenny_generator +package org.qtproject.qt.qtjenny_general import androidx.activity.ComponentActivity diff --git a/examples/demos/qtjennydemo/qtjenny_general/app/src/main/res/values/strings.xml b/examples/demos/qtjennydemo/qtjenny_general/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..e171a2fc1 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + qtjenny_general + diff --git a/examples/demos/qtjennydemo/qtjenny_generator/build.gradle b/examples/demos/qtjennydemo/qtjenny_general/build.gradle similarity index 100% rename from examples/demos/qtjennydemo/qtjenny_generator/build.gradle rename to examples/demos/qtjennydemo/qtjenny_general/build.gradle diff --git a/examples/demos/qtjennydemo/qtjenny_generator/gradle.properties b/examples/demos/qtjennydemo/qtjenny_general/gradle.properties similarity index 100% rename from examples/demos/qtjennydemo/qtjenny_generator/gradle.properties rename to examples/demos/qtjennydemo/qtjenny_general/gradle.properties diff --git a/examples/demos/qtjennydemo/qtjenny_generator/gradle/libs.versions.toml b/examples/demos/qtjennydemo/qtjenny_general/gradle/libs.versions.toml similarity index 97% rename from examples/demos/qtjennydemo/qtjenny_generator/gradle/libs.versions.toml rename to examples/demos/qtjennydemo/qtjenny_general/gradle/libs.versions.toml index 22d22bbba..5f356269a 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/gradle/libs.versions.toml +++ b/examples/demos/qtjennydemo/qtjenny_general/gradle/libs.versions.toml @@ -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" diff --git a/examples/demos/qtjennydemo/qtjenny_generator/gradle/wrapper/gradle-wrapper.jar b/examples/demos/qtjennydemo/qtjenny_general/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/demos/qtjennydemo/qtjenny_generator/gradle/wrapper/gradle-wrapper.jar rename to examples/demos/qtjennydemo/qtjenny_general/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/demos/qtjennydemo/qtjenny_generator/gradle/wrapper/gradle-wrapper.properties b/examples/demos/qtjennydemo/qtjenny_general/gradle/wrapper/gradle-wrapper.properties similarity index 92% rename from examples/demos/qtjennydemo/qtjenny_generator/gradle/wrapper/gradle-wrapper.properties rename to examples/demos/qtjennydemo/qtjenny_general/gradle/wrapper/gradle-wrapper.properties index 75f762b7e..52ec33cba 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/gradle/wrapper/gradle-wrapper.properties +++ b/examples/demos/qtjennydemo/qtjenny_general/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_generator/gradlew b/examples/demos/qtjennydemo/qtjenny_general/gradlew similarity index 100% rename from examples/demos/qtjennydemo/qtjenny_generator/gradlew rename to examples/demos/qtjennydemo/qtjenny_general/gradlew diff --git a/examples/demos/qtjennydemo/qtjenny_generator/gradlew.bat b/examples/demos/qtjennydemo/qtjenny_general/gradlew.bat similarity index 100% rename from examples/demos/qtjennydemo/qtjenny_generator/gradlew.bat rename to examples/demos/qtjennydemo/qtjenny_general/gradlew.bat diff --git a/examples/demos/qtjennydemo/qtjenny_general/settings.gradle b/examples/demos/qtjennydemo/qtjenny_general/settings.gradle new file mode 100644 index 000000000..31cad4d07 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/settings.gradle @@ -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' diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/constructor_definition.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/constructor_definition.kte similarity index 77% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/constructor_definition.kte rename to examples/demos/qtjennydemo/qtjenny_general/templates/constructor_definition.kte index 97051bca3..ce278191d 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/constructor_definition.kte +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/constructor_definition.kte @@ -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; } diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_declarations.kte new file mode 100644 index 000000000..f7d25ebf4 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_declarations.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_initialisations.kte new file mode 100644 index 000000000..f7d25ebf4 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/constructors_ids_initialisations.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/field_getter.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/field_getter.kte new file mode 100644 index 000000000..df650e915 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/field_getter.kte @@ -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 + } + diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/field_setter.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/field_setter.kte new file mode 100644 index 000000000..1ec09121d --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/field_setter.kte @@ -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}); + } + + diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_declarations.kte new file mode 100644 index 000000000..a0b7bd14e --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_declarations.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_initialisations.kte new file mode 100644 index 000000000..a0b7bd14e --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/fields_ids_initialisations.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_final_postamble.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/header_final_postamble.kte similarity index 90% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/header_final_postamble.kte rename to examples/demos/qtjennydemo/qtjenny_general/templates/header_final_postamble.kte index 99e4e8140..0178ba831 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_final_postamble.kte +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/header_final_postamble.kte @@ -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 - diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/header_initfunctions.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/header_initfunctions.kte new file mode 100644 index 000000000..268fc0c0a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/header_initfunctions.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/header_initvars.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/header_initvars.kte new file mode 100644 index 000000000..9c92488a0 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/header_initvars.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/header_postamble.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/header_postamble.kte new file mode 100644 index 000000000..4e610345a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/header_postamble.kte @@ -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()} diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/header_preamble.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/header_preamble.kte new file mode 100644 index 000000000..ec9b6d072 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/header_preamble.kte @@ -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 +#include + +${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 operator T() {return m_jniObject.object();} + ${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()); + } diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_preamble.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/header_preamble.kte~ similarity index 80% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/header_preamble.kte rename to examples/demos/qtjennydemo/qtjenny_general/templates/header_preamble.kte~ index ae5ff0e7c..8fa46a588 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/header_preamble.kte +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/header_preamble.kte~ @@ -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 operator T() {return m_jniObject.object();} ${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; } - diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/initclass_postamble.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/initclass_postamble.kte new file mode 100644 index 000000000..268fc0c0a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/initclass_postamble.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/initclass_preamble.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/initclass_preamble.kte new file mode 100644 index 000000000..268fc0c0a --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/initclass_preamble.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_generator/templates/method_definition.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/method_definition.kte similarity index 91% rename from examples/demos/qtjennydemo/qtjenny_generator/templates/method_definition.kte rename to examples/demos/qtjennydemo/qtjenny_general/templates/method_definition.kte index 002a8d442..694fabf5e 100644 --- a/examples/demos/qtjennydemo/qtjenny_generator/templates/method_definition.kte +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/method_definition.kte @@ -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()}(${ diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/method_prologue.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/method_prologue.kte new file mode 100644 index 000000000..a0ae0f066 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/method_prologue.kte @@ -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 + +@param jteData: JteData + +@if (jteData.useJniHelper) + @if (jteData.isStatic) +::jenny::Env env; assertInited(env.get()); + @else +::jenny::Env env; ::jenny::LocalRef jennyLocalRef = getThis(false); jobject thiz = jennyLocalRef.get(); + @endif +@else +assertInited(env); +@endif diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_declarations.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_declarations.kte new file mode 100644 index 000000000..f7d25ebf4 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_declarations.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_initialisations.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_initialisations.kte new file mode 100644 index 000000000..f7d25ebf4 --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/methods_ids_initialisations.kte @@ -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 diff --git a/examples/demos/qtjennydemo/qtjenny_general/templates/param.kte b/examples/demos/qtjennydemo/qtjenny_general/templates/param.kte new file mode 100644 index 000000000..a85d4cf0d --- /dev/null +++ b/examples/demos/qtjennydemo/qtjenny_general/templates/param.kte @@ -0,0 +1,24 @@ +<%-- + 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 diff --git a/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/res/values/strings.xml b/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/res/values/strings.xml deleted file mode 100644 index 6b0176494..000000000 --- a/examples/demos/qtjennydemo/qtjenny_generator/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - qtjenny_generator - diff --git a/examples/demos/qtjennydemo/volumeandbrightnessobserver.cpp b/examples/demos/qtjennydemo/volumeandbrightnessobserver.cpp new file mode 100644 index 000000000..93f31dc9e --- /dev/null +++ b/examples/demos/qtjennydemo/volumeandbrightnessobserver.cpp @@ -0,0 +1,60 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#include +#include +#include +#include "volumeandbrightnessobserver.h" +#include "qtjenny_output/jenny/proxy/android_provider_SystemProxy.h" +#include "qtjenny_output/jenny/proxy/android_os_LooperProxy.h" + +VolumeAndBrightnessObserver::VolumeAndBrightnessObserver(QObject *parent, BackEnd *backend) : QObject(parent), m_backend(backend) +{ + m_context = m_backend->m_context; + m_audioManager = m_backend->m_audioManager; + m_system = m_backend->m_system; + m_handler = android::os::HandlerProxy::newInstance( + android::os::LooperProxy::getMainLooper().object(), + qt_getProxy("org/qtproject/qtjennydemo/NativeInvocationHandler").object()); + m_resolver = m_context.getContentResolver(); + m_lastBrightness = m_backend->m_brightness; + m_lastVolume = m_backend->m_volume; +} + +void VolumeAndBrightnessObserver::start() +{ + m_contentObserver.setHandler(m_handler); + jlong nativeHandle = reinterpret_cast(static_cast(&m_contentObserver)); + QJniObject handler("org/qtproject/qtjennydemo/NativeInvocationHandler", nativeHandle); + + m_volumeObserver = QJniObject("org/qtproject/qtjennydemo/ContentObserverProxy", + "(Landroid/os/Handler;Lorg/qtproject/qtjennydemo/NativeInvocationHandler;)V", + m_handler->object(), + handler.object()); + m_resolver.registerContentObserver( + m_system.getCONTENT_URI().object(), true, m_volumeObserver.object()); +} + +jobject VolumeAndBrightnessObserver::handleMessage(jobject arg0) +{ + int currentMusicVolume = m_audioManager.getStreamVolume(m_audioManager.STREAM_MUSIC); + + if (currentMusicVolume != m_lastVolume) { + m_lastVolume = currentMusicVolume; + emit volumeChangeObserved(currentMusicVolume); + } + + int currentBrightness = m_system.getInt( + m_resolver->object(), + QJniObject::fromString(m_system.SCREEN_BRIGHTNESS).object(), + 0 // default value if not found + ); + + if (currentBrightness != m_lastBrightness) { + m_lastBrightness = currentBrightness; + emit brightnessChangeObserved(currentBrightness); + } + + QJniObject res("java/lang/Boolean", true); + QJniEnvironment env; + return env->NewGlobalRef(res.object()); +} diff --git a/examples/demos/qtjennydemo/volumeandbrightnessobserver.h b/examples/demos/qtjennydemo/volumeandbrightnessobserver.h new file mode 100644 index 000000000..61997f30b --- /dev/null +++ b/examples/demos/qtjennydemo/volumeandbrightnessobserver.h @@ -0,0 +1,41 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#ifndef VOLUMEANDBRIGHTNESSOBSERVER_H +#define VOLUMEANDBRIGHTNESSOBSERVER_H +#include +#include +#include +#include "qtjenny_output/jenny/proxy/android_os_ContextProxy.h" +#include "qtjenny_output/jenny/proxy/android_content_ContentResolverProxy.h" +#include +#include "contentobserver.h" +#include "backend.h" + +class VolumeAndBrightnessObserver : public QObject, android::os::CallbackInterface +{ + + Q_OBJECT + +private: + android::os::ContextProxy m_context; + android::media::AudioManagerProxy m_audioManager; + android::os::HandlerProxy m_handler; + android::content::ContentResolverProxy m_resolver; + android::provider::SystemProxy m_system; + QJniObject m_volumeObserver; + ContentObserver m_contentObserver; + BackEnd *m_backend; + int m_lastVolume = -1; + int m_lastBrightness = -1; + +public: + explicit VolumeAndBrightnessObserver(QObject *parent = nullptr, BackEnd *backend = nullptr); + jobject handleMessage(jobject arg0) override; + void start(); + +signals: + void brightnessChangeObserved(int brightness); + void volumeChangeObserved(int volume); +}; + +#endif // VOLUMEANDBRIGHTNESSOBSERVER_H diff --git a/licenseRule.json b/licenseRule.json index a79d4c819..c022939cf 100644 --- a/licenseRule.json +++ b/licenseRule.json @@ -21,7 +21,17 @@ "file type": "examples and snippets", "spdx": ["LicenseRef-Qt-Commercial OR BSD-3-Clause"] }, - "examples/demos/qtjennydemo/qtjenny_generator": { + "examples/demos/qtjennydemo/qtjenny_general": { + "comment": "Example takes precedence", + "file type": "examples and snippets", + "spdx": ["Apache-2.0"] + }, + "examples/demos/qtjennydemo/qtjenny_baseclass": { + "comment": "Example takes precedence", + "file type": "examples and snippets", + "spdx": ["Apache-2.0"] + }, + "examples/demos/qtjennydemo/qtjenny_callback": { "comment": "Example takes precedence", "file type": "examples and snippets", "spdx": ["Apache-2.0"] @@ -56,11 +66,21 @@ "file type": "examples and snippets", "spdx": ["LicenseRef-Qt-Commercial OR BSD-3-Clause"] }, - "examples/demos/qtjennydemo/qtjenny_generator/gradlew.bat": { + "examples/demos/qtjennydemo/qtjenny_general/gradlew.bat": { "comment": "Example takes precedence", "file type": "examples and snippets", "spdx": ["Apache-2.0"] - } + }, + "examples/demos/qtjennydemo/qtjenny_baseclass/gradlew.bat": { + "comment": "Example takes precedence", + "file type": "examples and snippets", + "spdx": ["Apache-2.0"] + }, + "examples/demos/qtjennydemo/qtjenny_callback/gradlew.bat": { + "comment": "Example takes precedence", + "file type": "examples and snippets", + "spdx": ["Apache-2.0"] + } } }, { @@ -191,11 +211,21 @@ "file type": "3rd party", "spdx": ["CC-BY-4.0"] }, - "examples/demos/qtjennydemo/qtjenny_generator/": { + "examples/demos/qtjennydemo/qtjenny_general/": { "comment": "", "file type": "3rd party", "spdx": ["Apache-2.0"] - } + }, + "examples/demos/qtjennydemo/qtjenny_baseclass/": { + "comment": "Example takes precedence", + "file type": "3rd party", + "spdx": ["Apache-2.0"] + }, + "examples/demos/qtjennydemo/qtjenny_callback/": { + "comment": "Example takes precedence", + "file type": "3rd party", + "spdx": ["Apache-2.0"] + } } } ]