Commit b3f720c4 authored by Volker Krause's avatar Volker Krause
Browse files

Handle methods returning arrays

We also create a wrapper type for those, which can be converted to a
JNI handle or an unpacked container. The latter is effectively limited
to containers of QStrings and containers or JNI handles for now as our
array code doesn't support more types yet.
parent e41b6d39
......@@ -5,6 +5,7 @@
*/
#include <KAndroidExtras/JniArray>
#include <KAndroidExtras/JniTypeTraits>
#include <QtTest/qtest.h>
......@@ -14,6 +15,12 @@ class JniArrayTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testTypeTrait()
{
static_assert(!Jni::is_array<int>::value);
static_assert(Jni::is_array<Jni::Array<int>>::value);
}
void testFromArray()
{
#ifndef Q_OS_ANDROID
......
......@@ -24,6 +24,7 @@ public:
JNI_METHOD(bool, setCoordinates, jfloat, jfloat)
JNI_METHOD(void, startIntent, android::content::Intent)
JNI_METHOD(android::content::Intent, getIntent)
JNI_METHOD(Jni::Array<java::lang::String>, getStringList)
JNI_PROPERTY(java::lang::String, name)
......@@ -91,7 +92,12 @@ private Q_SLOTS:
// call chaining with JNI handle pass-through
obj.setName(obj.getName());
QCOMPARE(obj.handle().protocol().size(), 17);
// conversion of array return types
j = obj.getStringList();
QStringList l = obj.getStringList();
std::vector<QString> l2 = obj.getStringList();
QCOMPARE(obj.handle().protocol().size(), 20);
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)"));
......@@ -110,10 +116,14 @@ private Q_SLOTS:
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; ()"));
#if 0
// stuff that must not compile
obj.setName(42);
obj.setFlags(QStringLiteral("42"));
s = obj.getStringList();
#endif
#endif
}
......
......@@ -61,6 +61,7 @@ ecm_generate_headers(KAndroidExtras_java_FORWARDING_HEADERS
ecm_generate_headers(KAndroidExtras_jni_FORWARDING_HEADERS
HEADER_NAMES
JniArray
JniCommon
JniMethod
JniProperty
JniSignature
......
......@@ -7,6 +7,7 @@
// list all headers here that have no .cpp file
// this only serves as a guarantee that these headers actually compile
#include "jniarray.h"
#include "jnicommon.h"
#include "jnimethod.h"
#include "jniproperty.h"
#include "jnisignature.h"
......
/*
SPDX-FileCopyrightText: 2019-2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNICOMMON_H
#define KANDROIDEXTRAS_JNICOMMON_H
namespace KAndroidExtras {
namespace Jni {
/** Wrapper type for array return values (which we cannot specify using [] syntax). */
template <typename T> struct Array {};
}
}
#endif
......@@ -6,6 +6,7 @@
#ifndef KANDROIDEXTRAS_JNIMETHOD_H
#define KANDROIDEXTRAS_JNIMETHOD_H
#include "jniarray.h"
#include "jnitypetraits.h"
#include <QAndroidJniObject>
......@@ -95,11 +96,27 @@ namespace Internal {
QAndroidJniObject value;
};
template <typename RetT>
class ReturnValue<Jni::Array<RetT>> {
public:
explicit inline ReturnValue(const QAndroidJniObject &v) : value(v) {}
inline operator QAndroidJniObject() const {
return value;
}
template <typename Container>
inline operator Container() const {
return Jni::fromArray<Container>(value);
}
private:
QAndroidJniObject value;
};
// return type conversion
template <typename RetT>
struct call_return {
static inline constexpr bool is_basic = Jni::is_basic_type<RetT>::value;
static inline constexpr bool is_convertible = !std::is_same_v<typename Jni::converter<RetT>::type, void>;
static inline constexpr bool is_convertible = !std::is_same_v<typename Jni::converter<RetT>::type, void> || Jni::is_array<RetT>::value;
typedef std::conditional_t<is_basic, RetT, QAndroidJniObject> JniReturnT;
typedef std::conditional_t<is_basic || !is_convertible, JniReturnT, ReturnValue<RetT>> CallReturnT;
......@@ -195,8 +212,11 @@ namespace Internal {
* be implicitly converted either to the destination type of the conversion, or a @c QAndroidJniObject.
* This allows to avoid type conversion when chaining calls for example, it however needs additional
* care when used in combination with automatic type deduction.
* - 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 with a @c JNI_OBJECT.
* Thie macro can only be placed in classes having a @c handle() method returning the corresponding
* QAndroidJniObject instance.
*
* @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.
......
......@@ -7,18 +7,14 @@
#ifndef KANDROIDEXTRAS_JNISIGNATURE_H
#define KANDROIDEXTRAS_JNISIGNATURE_H
#include "jnicommon.h"
#include <cstdint>
#include <utility>
namespace KAndroidExtras {
namespace Jni {
/** Wrapper type for array return values (which we cannot specify using [] syntax). */
template <typename T> struct Array {};
}
/** @cond internal */
namespace Internal {
/** Compile-time concat-able string. */
......@@ -110,6 +106,7 @@ struct JniSignature<RetT(Args...)>
}
};
}
/** @endcond */
/** Helper methods to deal with JNI. */
namespace Jni
......
......@@ -7,6 +7,7 @@
#ifndef KANDROIDEXTRAS_JNITYPETRAITS_H
#define KANDROIDEXTRAS_JNITYPETRAITS_H
#include "jnicommon.h"
#include "jnitypes.h"
#include <QAndroidJniObject>
......@@ -40,6 +41,10 @@ template <typename T> struct reverse_converter {
typedef converter<typename converter<T>::type> type;
};
/** Type trait for checking whether @tparam T is a JNI array type. */
template <typename T> struct is_array : std::false_type {};
template <typename T> struct is_array<Array<T>> : std::true_type {};
}
/**
......
Markdown is supported
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