Commit 3f039209 authored by Volker Krause's avatar Volker Krause
Browse files

Expand the JNI_OBJECT macro to also manage the JNI handle

This simplifies user code wrapping instances, for purely static classes
or special/singleton instances there is now JNI_UNMANAGED_OBJECT.
parent 60fcb55c
......@@ -17,13 +17,9 @@ using namespace KAndroidExtras;
class TestClass
{
JNI_OBJECT(TestClass, android::content::Context)
public:
TestClass(const QAndroidJniObject &o) : m_handle(o) {}
JNI_PROPERTY(bool, myBoolProp)
private:
inline QAndroidJniObject handle() const { return m_handle; }
QAndroidJniObject m_handle;
};
class JniArrayTest : public QObject
{
Q_OBJECT
......
......@@ -31,14 +31,12 @@ public:
JNI_PROPERTY(java::lang::String, name)
inline QAndroidJniObject handle() const { return m_handle; }
JNI_STATIC_METHOD(void, noRetNoArg)
JNI_STATIC_METHOD(jlong, retNoArg)
JNI_STATIC_METHOD(void, noRetArg, java::lang::String)
JNI_STATIC_METHOD(android::content::Intent, retArg, bool)
private:
QAndroidJniObject m_handle;
friend class JniMethodTest;
};
class JniMethodTest : public QObject
......@@ -109,35 +107,35 @@ private Q_SLOTS:
// nullptr arguments
obj.startIntent(nullptr);
QCOMPARE(obj.handle().protocol().size(), 22);
QCOMPARE(obj.handle().protocol()[0], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[1], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.handle().protocol()[2], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.handle().protocol()[3], QLatin1String("callMethod: getFlags ()I ()"));
QCOMPARE(obj.handle().protocol()[4], QLatin1String("callMethod: setFlags (I)V (I)"));
QCOMPARE(obj.handle().protocol()[5], QLatin1String("callMethod: start ()V ()"));
QCOMPARE(obj.handle().protocol()[6], QLatin1String("callMethod: setCoordinates (FF)Z (FF)"));
QCOMPARE(obj.handle().protocol()[7], QLatin1String("callMethod: setCoordinates (FF)Z (FF)"));
QCOMPARE(obj.handle().protocol()[8], QLatin1String("getObjectField: name Ljava/lang/String;"));
QCOMPARE(obj.handle().protocol()[9], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.handle().protocol()[10], QLatin1String("callMethod: startIntent (Landroid/content/Intent;)V (o)"));
QCOMPARE(obj.handle().protocol()[11], QLatin1String("callObjectMethod: getIntent ()Landroid/content/Intent; ()"));
QCOMPARE(obj.handle().protocol()[12], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.handle().protocol()[13], QLatin1String("callMethod: setFlags (I)V (I)"));
QCOMPARE(obj.handle().protocol()[14], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[15], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[16], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.handle().protocol()[17], QLatin1String("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[18], QLatin1String("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[19], QLatin1String("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[20], QLatin1String("callObjectMethod: getShortList ()[S ()"));
QCOMPARE(obj.handle().protocol()[21], QLatin1String("callMethod: startIntent (Landroid/content/Intent;)V (o)"));
QCOMPARE(obj.jniHandle().protocol().size(), 22);
QCOMPARE(obj.jniHandle().protocol()[0], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.jniHandle().protocol()[1], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.jniHandle().protocol()[2], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.jniHandle().protocol()[3], QLatin1String("callMethod: getFlags ()I ()"));
QCOMPARE(obj.jniHandle().protocol()[4], QLatin1String("callMethod: setFlags (I)V (I)"));
QCOMPARE(obj.jniHandle().protocol()[5], QLatin1String("callMethod: start ()V ()"));
QCOMPARE(obj.jniHandle().protocol()[6], QLatin1String("callMethod: setCoordinates (FF)Z (FF)"));
QCOMPARE(obj.jniHandle().protocol()[7], QLatin1String("callMethod: setCoordinates (FF)Z (FF)"));
QCOMPARE(obj.jniHandle().protocol()[8], QLatin1String("getObjectField: name Ljava/lang/String;"));
QCOMPARE(obj.jniHandle().protocol()[9], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.jniHandle().protocol()[10], QLatin1String("callMethod: startIntent (Landroid/content/Intent;)V (o)"));
QCOMPARE(obj.jniHandle().protocol()[11], QLatin1String("callObjectMethod: getIntent ()Landroid/content/Intent; ()"));
QCOMPARE(obj.jniHandle().protocol()[12], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.jniHandle().protocol()[13], QLatin1String("callMethod: setFlags (I)V (I)"));
QCOMPARE(obj.jniHandle().protocol()[14], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.jniHandle().protocol()[15], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.jniHandle().protocol()[16], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.jniHandle().protocol()[17], QLatin1String("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
QCOMPARE(obj.jniHandle().protocol()[18], QLatin1String("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
QCOMPARE(obj.jniHandle().protocol()[19], QLatin1String("callObjectMethod: getStringList ()[Ljava/lang/String; ()"));
QCOMPARE(obj.jniHandle().protocol()[20], QLatin1String("callObjectMethod: getShortList ()[S ()"));
QCOMPARE(obj.jniHandle().protocol()[21], QLatin1String("callMethod: startIntent (Landroid/content/Intent;)V (o)"));
// ctor call
obj = TestClass(intent);
QCOMPARE(obj.handle().protocol().size(), 1);
QCOMPARE(obj.handle().protocol()[0], QLatin1String("ctor: android/content/Intent (Landroid/content/Intent;)V"));
QCOMPARE(obj.jniHandle().protocol().size(), 1);
QCOMPARE(obj.jniHandle().protocol()[0], QLatin1String("ctor: android/content/Intent (Landroid/content/Intent;)V"));
#if 0
// stuff that must not compile
obj.setName(42);
......
......@@ -16,6 +16,7 @@ using namespace KAndroidExtras;
class TestClass
{
TestClass() = default;
JNI_OBJECT(TestClass, android::content::Intent)
JNI_CONSTANT(java::lang::String, ACTION_CREATE_DOCUMENT)
......@@ -26,11 +27,8 @@ class TestClass
JNI_PROPERTY(int, myIntField)
JNI_PROPERTY(android::net::Uri, myUriField)
JNI_PROPERTY(android::content::Intent, myIntentField)
inline QAndroidJniObject handle() const { return m_handle; }
private:
QAndroidJniObject m_handle;
public:
friend class JniPropertyTest;
};
static_assert(sizeof(TestClass) == sizeof(QAndroidJniObject));
......@@ -69,15 +67,15 @@ private Q_SLOTS:
const QAndroidJniObject bla = obj.myIntentField;
obj.myIntentField = bla;
QCOMPARE(obj.handle().protocol().size(), 8);
QCOMPARE(obj.handle().protocol().at(0), QLatin1String("getObjectField: myStringField Ljava/lang/String;"));
QCOMPARE(obj.handle().protocol().at(1), QLatin1String("setField: myStringField Ljava/lang/String;"));
QCOMPARE(obj.handle().protocol().at(2), QLatin1String("getField: myIntField I"));
QCOMPARE(obj.handle().protocol().at(3), QLatin1String("setField: myIntField I"));
QCOMPARE(obj.handle().protocol().at(4), QLatin1String("getObjectField: myUriField Landroid/net/Uri;"));
QCOMPARE(obj.handle().protocol().at(5), QLatin1String("setField: myUriField Landroid/net/Uri;"));
QCOMPARE(obj.handle().protocol().at(6), QLatin1String("getObjectField: myIntentField Landroid/content/Intent;"));
QCOMPARE(obj.handle().protocol().at(7), QLatin1String("setField: myIntentField Landroid/content/Intent;"));
QCOMPARE(obj.jniHandle().protocol().size(), 8);
QCOMPARE(obj.jniHandle().protocol().at(0), QLatin1String("getObjectField: myStringField Ljava/lang/String;"));
QCOMPARE(obj.jniHandle().protocol().at(1), QLatin1String("setField: myStringField Ljava/lang/String;"));
QCOMPARE(obj.jniHandle().protocol().at(2), QLatin1String("getField: myIntField I"));
QCOMPARE(obj.jniHandle().protocol().at(3), QLatin1String("setField: myIntField I"));
QCOMPARE(obj.jniHandle().protocol().at(4), QLatin1String("getObjectField: myUriField Landroid/net/Uri;"));
QCOMPARE(obj.jniHandle().protocol().at(5), QLatin1String("setField: myUriField Landroid/net/Uri;"));
QCOMPARE(obj.jniHandle().protocol().at(6), QLatin1String("getObjectField: myIntentField Landroid/content/Intent;"));
QCOMPARE(obj.jniHandle().protocol().at(7), QLatin1String("setField: myIntentField Landroid/content/Intent;"));
#endif
}
};
......
......@@ -19,13 +19,13 @@ JNI_TYPE(org, kde, itinerary, Activity)
/** Interface to the Java Activity class. */
class ItineraryActivity {
JNI_OBJECT(ItineraryActivity, org::kde::itinerary::Activity)
JNI_UNMANAGED_OBJECT(ItineraryActivity, org::kde::itinerary::Activity)
public:
JNI_METHOD(void, checkCalendar)
JNI_METHOD(KAndroidExtras::Jni::Array<KAndroidExtras::java::lang::String>, attachmentsForIntent, KAndroidExtras::android::content::Intent)
JNI_METHOD(KAndroidExtras::android::net::Uri, openDocument, java::lang::String)
private:
inline QAndroidJniObject handle() const { return QtAndroid::androidActivity(); }
inline QAndroidJniObject jniHandle() const { return QtAndroid::androidActivity(); }
};
#endif
......@@ -17,30 +17,20 @@ using namespace KAndroidExtras;
Intent::Intent()
{
m_intent = QAndroidJniObject(Jni::typeName<android::content::Intent>());
}
Intent::Intent(const QAndroidJniObject &intent)
: m_intent(intent)
{
setJniHandle(QAndroidJniObject(Jni::typeName<android::content::Intent>()));
}
Intent::~Intent() = default;
Intent::operator QAndroidJniObject() const
{
return m_intent;
}
QAndroidJniObject Intent::handle() const
{
return m_intent;
return jniHandle();
}
template <typename T>
QAndroidJniObject Intent::getObjectExtra(const char *methodName, const QAndroidJniObject &name) const
{
return m_intent.callObjectMethod(methodName, Jni::signature<T(java::lang::String)>(), name.object());
return jniHandle().callObjectMethod(methodName, Jni::signature<T(java::lang::String)>(), name.object());
}
QString Intent::getStringExtra(const QAndroidJniObject &name) const
......
......@@ -31,8 +31,6 @@ class KANDROIDEXTRAS_EXPORT Intent
public:
/** Creates a new empty intent. */
Intent();
/** Adopts an existing intent available as a JNI object. */
explicit Intent(const QAndroidJniObject &intent);
~Intent();
/** Add a category to the intent. */
......@@ -60,12 +58,11 @@ public:
template <typename T>
inline void putExtra(const QAndroidJniObject &name, const QAndroidJniObject &value)
{
m_intent.callObjectMethod("putExtra", Jni::signature<android::content::Intent(java::lang::String, T)>(), name.object(), value.object());
jniHandle().callObjectMethod("putExtra", Jni::signature<android::content::Intent(java::lang::String, T)>(), name.object(), value.object());
}
/** Implicit conversion to an QAndroidJniObject. */
operator QAndroidJniObject() const;
QAndroidJniObject handle() const;
/** Action constant for create document intents. */
JNI_CONSTANT(java::lang::String, ACTION_CREATE_DOCUMENT)
......@@ -96,7 +93,6 @@ public:
private:
template <typename T>
QAndroidJniObject getObjectExtra(const char* methodName, const QAndroidJniObject &name) const;
QAndroidJniObject m_intent;
};
}
......
......@@ -20,7 +20,7 @@ namespace KAndroidExtras {
*/
class ManifestPermission
{
JNI_OBJECT(ManifestPermission, android::Manifest_permission)
JNI_UNMANAGED_OBJECT(ManifestPermission, android::Manifest_permission)
public:
JNI_CONSTANT(java::lang::String, READ_CALENDAR)
JNI_CONSTANT(java::lang::String, READ_EXTERNAL_STORAGE)
......
......@@ -20,7 +20,7 @@ namespace KAndroidExtras {
*/
class OpenableColumns
{
JNI_OBJECT(OpenableColumns, android::provider::OpenableColumns)
JNI_UNMANAGED_OBJECT(OpenableColumns, android::provider::OpenableColumns)
public:
JNI_CONSTANT(java::lang::String, DISPLAY_NAME)
JNI_CONSTANT(java::lang::String, SIZE)
......
......@@ -15,7 +15,7 @@ namespace KAndroidExtras {
/** Methods around android.provider.Settings. */
class Settings
{
JNI_OBJECT(Settings, android::provider::Settings)
JNI_UNMANAGED_OBJECT(Settings, android::provider::Settings)
JNI_CONSTANT(java::lang::String, ACTION_APP_NOTIFICATION_SETTINGS)
JNI_CONSTANT(java::lang::String, ACTION_CHANNEL_NOTIFICATION_SETTINGS)
JNI_CONSTANT(java::lang::String, EXTRA_APP_PACKAGE)
......
......@@ -194,8 +194,7 @@ namespace Internal {
* - array return types also result in a wrapper class that can be implicitly converted to a sequential
* container or a @p QAndroidJniObject representing the JNI array.
*
* Thie macro can only be placed in classes having a @c handle() method returning the corresponding
* QAndroidJniObject instance.
* Thie macro can only be placed in classes having the @c JNI_OBJECT macro.
*
* @param RetT The return type. Must either be a basic type or a type declared with @c JNI_TYPE
* @param Name The name of the method. Must match the JNI method to be called exactly.
......@@ -206,7 +205,7 @@ namespace Internal {
template <typename ...Args> \
inline auto Name(Args&&... args) const { \
using namespace KAndroidExtras; \
return Internal::invoker<RetT, ## __VA_ARGS__>::call(handle(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>(), std::forward<Args>(args)...); \
return Internal::invoker<RetT, ## __VA_ARGS__>::call(jniHandle(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>(), std::forward<Args>(args)...); \
}
/**
......@@ -227,7 +226,7 @@ inline auto Name(Args&&... args) const { \
* - array return types also result in a wrapper class that can be implicitly converted to a sequential
* container or a @p QAndroidJniObject representing the JNI array.
*
* Thie macro can only be placed in classes having @c JNI_OBJECT macro.
* Thie macro can only be placed in classes having the @c JNI_UNMANAGED_OBJECT or @c JNI_OBJECT macro.
*
* @param RetT The return type. Must either be a basic type or a type declared with @c JNI_TYPE
* @param Name The name of the method. Must match the JNI method to be called exactly.
......@@ -259,7 +258,7 @@ static inline auto Name(Args&&... args) { \
template <typename ...Args> \
inline Name(Args&&... args) { \
using namespace KAndroidExtras; \
m_handle = Internal::constructor<__VA_ARGS__>::call(Jni::typeName<_jni_ThisType>(), Jni::signature<void(__VA_ARGS__)>(), std::forward<Args>(args)...); \
setJniHandle(Internal::constructor<__VA_ARGS__>::call(Jni::typeName<_jni_ThisType>(), Jni::signature<void(__VA_ARGS__)>(), std::forward<Args>(args)...)); \
}
}
......
......@@ -44,21 +44,36 @@ private:
/** Annotates a class for holding JNI method or property wrappers.
*
* For using non-static methods or properties, this class also has to provide a
* @p handle() method returning a @c QAndroidJniObject representing the current
* instance.
* Use this if the class only has static methods or constants. For methods
* and properties, you either need to use @c JNI_OBJECT instead, or make
* sure there is a @p jniHandle() method returning a @c QAndroidJniObject
* representing the current instance.
*
* @param Class the name of the class this is added to.
* @param BaseType the Java type this class represents, defined by the
* @c JNI_TYPE or @c JNI_NESTED_TYPE macros.
*/
#define JNI_OBJECT(Class, BaseType) \
#define JNI_UNMANAGED_OBJECT(Class, BaseType) \
public: \
typedef Class _jni_ThisType; \
private: \
static inline constexpr const char *jniName() { return KAndroidExtras::Jni::typeName<BaseType>(); } \
friend constexpr const char* KAndroidExtras::Jni::typeName<Class>();
/** Annotates a class for holding JNI method or property wrappers.
*
* @param Class the name of the class this is added to.
* @param BaseType the Java type this class represents, defined by the
* @c JNI_TYPE or @c JNI_NESTED_TYPE macros.
*/
#define JNI_OBJECT(Class, BaseType) \
JNI_UNMANAGED_OBJECT(Class, BaseType) \
private: \
QAndroidJniObject _m_jni_handle; \
inline QAndroidJniObject jniHandle() const { return _m_jni_handle; } \
inline void setJniHandle(const QAndroidJniObject &h) { _m_jni_handle = h; } \
public: \
explicit Class(const QAndroidJniObject &h) : _m_jni_handle(h) {}
}
}
......
......@@ -56,7 +56,7 @@ class PropertyBase {
protected:
inline QAndroidJniObject handle() const {
const auto owner = reinterpret_cast<const ClassType*>(reinterpret_cast<const char*>(this) - OffsetHolder::offset());
return owner->handle();
return owner->jniHandle();
}
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment