hi I need to add some custom java to my game.
what is the best way to do this?
hi I need to add some custom java to my game.
what is the best way to do this?
Hi Bas!
The general process of mixing Java code with QML/Felgo goes over C++, which can communicate with custom Java classes over JNI.
In *.pro file configuration you can add a setting that includes custom java-sources in the compilation process, for example:
android {
QT += androidextras # for QAndroidJNIObject
HEADERS += \
cpp/myandroidutils.h # c++ class that will be accessible through QML
SOURCES += \
cpp/myandroidutils.cpp # implementation of c++ class, uses JNI to access JAVA sources
# java class that will be called via JNI
JAVASOURCES += \
$$PWD/android/src/net/myproject/helper/MyAndroidUtils.java
}
The androidextras Qt module holds C++ classes for communicating with Java/Android over JNI.
The HEADERS and SOURCES configuration in the above example add a C++ class called “myandroidutils”, which we can make accessible from QML later.
The JAVASOURCES setting let’s you define the java classes you want to include in the build.
As everything is put inside the android { } block, you can later add a different implementation of “myandroidutils” for other platforms like desktop or iOS, and e.g. print a warning that this feature is only available on Android when the myandroidutils component is used.
In the main.cpp, you can then e.g. create an instance of the new class and add it as a context property:
// ...
#include <QQmlContext>
#include "cpp/mynativeutils.h"
int main(int argc, char *argv[])
{
// ...
engine.load(QUrl(vplay.mainQmlFileName()));
// add myAndroidUtils as context property
MyAndroidUtils *utils = new MyAndroidUtils(engine);
engine.rootContext()->setContextProperty("myAndroidUtils", utils);
return app.exec();
}
You can then use myAndroidUtils.myFunction(…) anywhere in your QML code, to access the MyAndroidUtils instance.
This is also the way we integrate native Android features within the Felgo lib.
Within your java class, you can access the root activity with QtNative.activity() and import org.qtproject.qt5.android.QtNative.
Everything else comes down to implementing the Java parts and the C++ class for the communication C++ <-> JNI <-> JAVA.
Best,
Günther
Hi Bas,
You can find some useful tutorial on KDAB blogs page.
https://www.kdab.com/category/blogs/
They have dedicated set of tutorials to understand better how this works.
https://www.kdab.com/qt-android-episode-7/ , this is part 7 but it’s easy to access older posts.
hi Günther & Marcin,
thanx both for the info.
I need screen pinning on android, it is not that hard to implement, but i need to understand how to implement the bridge C++ <-> JNI <-> JAVA.
Greetz,
Bas
hmm i cannot get it to work.
# allows to add DEPLOYMENTFOLDERS and links to the Felgo library and QtCreator auto-completion
CONFIG += v-play
qmlFolder.source = qml
DEPLOYMENTFOLDERS += qmlFolder # comment for publishing
assetsFolder.source = assets
DEPLOYMENTFOLDERS += assetsFolder
# Add more folders to ship with the application here
RESOURCES += # resources.qrc # uncomment for publishing
# NOTE: for PUBLISHING, perform the following steps:
# 1. comment the DEPLOYMENTFOLDERS += qmlFolder line above, to avoid shipping your qml files with the application (instead they get compiled to the app binary)
# 2. uncomment the resources.qrc file inclusion and add any qml subfolders to the .qrc file; this compiles your qml files and js files to the app binary and protects your source code
# 3. change the setMainQmlFile() call in main.cpp to the one starting with "qrc:/" - this loads the qml files from the resources
# for more details see the "Deployment Guides" in the Felgo Documentation
# during development, use the qmlFolder deployment because you then get shorter compilation times (the qml files do not need to be compiled to the binary but are just copied)
# also, for quickest deployment on Desktop disable the "Shadow Build" option in Projects/Builds - you can then select "Run Without Deployment" from the Build menu in Qt Creator if you only changed QML files; this speeds up application start, because your app is not copied & re-compiled but just re-interpreted
# The .cpp file which was generated for your project. Feel free to hack it.
SOURCES += main.cpp
android {
QT += androidextras # for QAndroidJNIObject
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
OTHER_FILES += android/AndroidManifest.xml
HEADERS += \
basandroidutils.h # c++ class that will be accessible through QML
SOURCES += \
basandroidutils.cpp # implementation of c++ class, uses JNI to access JAVA sources
# java class that will be called via JNI
# SOURCES += \
# android/com/lentfert/MyAndroidUtils.java
# OTHER_FILES += android/com/lentfert/MyAndroidUtils.java
JAVASOURCES += \
$$PWD/android/com/lentfert/MyAndroidUtils2.java
}
ios {
QMAKE_INFO_PLIST = ios/Project-Info.plist
OTHER_FILES += $$QMAKE_INFO_PLIST
}
# set application icons for win and macx
win32 {
RC_FILE += win/app_icon.rc
}
macx {
ICON = macx/app_icon.icns
}
#ifndef BASANDROIDUTILS_H
#define BASANDROIDUTILS_H
#include <QObject>
#include <QAndroidJniObject>
#include <QQmlApplicationEngine>
#include <QAndroidJniEnvironment>
class basandroidutils : public QObject
{
Q_OBJECT
public:
explicit basandroidutils(QQmlApplicationEngine *parent = 0);
public slots:
int fibonacci(int n);
int add1(int n);
};
#endif // BASANDROIDUTILS_H
#include "basandroidutils.h"
basandroidutils::basandroidutils(QQmlApplicationEngine *parent) : QObject(parent)
{
}
int basandroidutils::fibonacci(int n)
{
qDebug("------------------------");
qDebug("test call java static method -> fibonacci");
jint result = -1;
result = QAndroidJniObject::callStaticMethod<jint>
("com/lentfert/MyAndroidUtils" // class name
, "fibonacci" // method name
, "(I)I" // signature
, n);
QAndroidJniEnvironment env;
if (env->ExceptionCheck())
{
// Handle exception here.
qDebug("*** JNI exception ***");
env->ExceptionDescribe();
env->ExceptionClear();
env->ExceptionClear();
}
else
{
qDebug("result = %i", result);
}
return result;
}
int basandroidutils::add1(int n)
{
qDebug("------------------------");
qDebug("test call java static method -> add1");
jint result = -1;
result = QAndroidJniObject::callStaticMethod<jint>
("com/lentfert/MyAndroidUtils" // class name
, "add1" // method name
, "(I)I" // signature
, n);
QAndroidJniEnvironment env;
if (env->ExceptionCheck())
{
// Handle exception here.
qDebug("*** JNI exception ***");
env->ExceptionDescribe();
env->ExceptionClear();
env->ExceptionClear();
}
else
{
qDebug("result = %i", result);
}
return result;
}
#include <QApplication>
#include <VPApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "basandroidutils.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
VPApplication vplay;
// qmlRegisterType<basandroidutils>("com.lentfert.basandroidutils", 1, 0, "BasAndroidUtils");
// QQmlApplicationEngine is the preferred way to start qml projects since Qt 5.2
// if you have older projects using Qt App wizards from previous QtCreator versions than 3.1, please change them to QQmlApplicationEngine
QQmlApplicationEngine engine;
vplay.initialize(&engine);
// use this during development
// for PUBLISHING, use the entry point below
vplay.setMainQmlFileName(QStringLiteral("qml/Main.qml"));
// basandroidutils *utils = new basandroidutils(engine);
basandroidutils *utils = new basandroidutils();
// QQmlContext *qqc = engine.rootContext();
// qqc->setContextProperty("myAndroidUtils", utils);
engine.rootContext()->setContextProperty("myAndroidUtils", utils);
// use this instead of the above call to avoid deployment of the qml files and compile them into the binary with qt's resource system qrc
// this is the preferred deployment option for publishing games to the app stores, because then your qml files and js files are protected
// to avoid deployment of your qml files and images, also comment the DEPLOYMENTFOLDERS command in the .pro file
// also see the .pro file for more details
// vplay.setMainQmlFileName(QStringLiteral("qrc:/qml/Main.qml"));
engine.load(QUrl(vplay.mainQmlFileName()));
return app.exec();
}
import Felgo 3.0
import QtQuick 2.0
//import com.lentfert.basandroidutils 1.0
GameWindow {
id: gameWindow
// You get free licenseKeys from http://felgo.com/licenseKey
// With a licenseKey you can:
// * Publish your games & apps for the app stores
// * Remove the Felgo Splash Screen or set a custom one (available with the Pro Licenses)
// * Add plugins to monetize, analyze & improve your apps (available with the Pro Licenses)
//licenseKey: "<generate one from http://felgo.com/licenseKey>"
activeScene: scene
// the size of the Window can be changed at runtime by pressing Ctrl (or Cmd on Mac) + the number keys 1-8
// the content of the logical scene size (480x320 for landscape mode by default) gets scaled to the window size based on the scaleMode
// you can set this size to any resolution you would like your project to start with, most of the times the one of your main target device
// this resolution is for iPhone 4 & iPhone 4S
screenWidth: 960
screenHeight: 640
Scene {
id: scene
// the "logical size" - the scene content is auto-scaled to match the GameWindow size
width: 480
height: 320
// BasAndroidUtils { id:butils }
Component.onCompleted: {
var fib = myAndroidUtils.fibonacci(3)
console.log("myAndroidUtils.fibonacci(3)")
console.log(fib)
var a1 = myAndroidUtils.add1(2);
console.log("myAndroidUtils.add1(2)")
console.log(a1)
}
}
}
D AndroidJniTest: ../AndroidJniTest/basandroidutils.cpp:10 (int basandroidutils::fibonacci(int)): ————————
D AndroidJniTest: ../AndroidJniTest/basandroidutils.cpp:11 (int basandroidutils::fibonacci(int)): test call java static method -> fibonacci
D AndroidJniTest: ../AndroidJniTest/basandroidutils.cpp:32 (int basandroidutils::fibonacci(int)): result = 0
D AndroidJniTest: assets:/qml/Main.qml:38 (onCompleted): qml: myAndroidUtils.fibonacci(3)
D AndroidJniTest: assets:/qml/Main.qml:39 (onCompleted): qml: 0
D AndroidJniTest: ../AndroidJniTest/basandroidutils.cpp:38 (int basandroidutils::add1(int)): ————————
D AndroidJniTest: ../AndroidJniTest/basandroidutils.cpp:39 (int basandroidutils::add1(int)): test call java static method add1
D AndroidJniTest: ../AndroidJniTest/basandroidutils.cpp:60 (int basandroidutils::add1(int)): result = 0
D AndroidJniTest: assets:/qml/Main.qml:41 (onCompleted): qml: myAndroidUtils.add1(2)
D AndroidJniTest: assets:/qml/Main.qml:42 (onCompleted): qml: 0
package com.lentfert;
import android.util.Log;
import org.qtproject.qt5.android.QtNative
class MyJavaNatives
{
// declare the native method
public static native void sendFibonaciResult(int n);
}
public class MyAndroidUtils
{
private static final String TAG = "MyAndroidUtils";
// this method will be called from C/C++
public static int add1(int n)
{
LOG.d(TAG, "add1 = " + n);
return n + 1;
}
public static int fibonacci(int n)
{
if (n < 2)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
// the second method that will be called from C/C++
public static void compute_fibonacci(int n)
{
// callback the native method with the computed result.
MyJavaNatives.sendFibonaciResult(fibonacci(n));
}
}
and a simple java class, i forgot
ok I had the java file not in src directory:)
now its working.
You must be logged in to reply to this topic.
As part of the free Business evaluation, we offer a free welcome call for companies, to talk about your requirements, and how the Felgo SDK & Services can help you. Just sign up and schedule your call.
Sign up now to start your free Business evaluation: