Use Knotificationpermission

This commit is contained in:
James Graham
2024-10-02 21:07:58 +00:00
parent c28ca9087c
commit d22cc7f40a
49 changed files with 3 additions and 3339 deletions

View File

@@ -194,8 +194,6 @@ add_library(neochat STATIC
neochatroommember.h
models/threadmodel.cpp
models/threadmodel.h
permissionmanager.cpp
permissionmanager.h
)
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
@@ -306,10 +304,6 @@ add_subdirectory(devtools)
add_subdirectory(login)
add_subdirectory(chatbar)
if (ANDROID)
add_subdirectory(kandroidextras)
endif()
if(NOT ANDROID AND NOT WIN32)
qt_target_qml_sources(neochat QML_FILES
qml/ShareAction.qml
@@ -442,7 +436,7 @@ endif()
if(ANDROID)
target_sources(neochat PRIVATE notifyrc.qrc)
target_link_libraries(neochat PUBLIC KAndroidExtras PRIVATE Qt::Svg OpenSSL::SSL)
target_link_libraries(neochat PRIVATE Qt::Svg OpenSSL::SSL)
if(SQLite3_FOUND)
target_link_libraries(neochat-app PRIVATE SQLite::SQLite3)
endif()

View File

@@ -1,78 +0,0 @@
# SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
add_library(KAndroidExtras STATIC)
target_sources(KAndroidExtras PRIVATE
android/activity.cpp
android/android_headers.cpp
android/contentresolver.cpp
android/context.cpp
android/intent.cpp
android/uri.cpp
java/java_headers.cpp
java/javalocale.cpp
jni/jni_headers.cpp
)
if (NOT ANDROID)
target_sources(KAndroidExtras PRIVATE
fake/mock_impl.cpp
fake/mock_jniobject.cpp
)
endif()
generate_export_header(KAndroidExtras BASE_NAME KAndroidExtras)
target_compile_features(KAndroidExtras PUBLIC cxx_std_20) # for __VA_OPT__
target_link_libraries(KAndroidExtras PUBLIC Qt::Core)
if (ANDROID)
target_link_libraries(KAndroidExtras PRIVATE Qt::CorePrivate)
else()
target_include_directories(KAndroidExtras PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/fake ${CMAKE_CURRENT_SOURCE_DIR}/fake/qt6)
endif()
ecm_generate_headers(KAndroidExtras_android_FORWARDING_HEADERS
HEADER_NAMES
Activity
AndroidTypes
CalendarContract
ContentResolver
Context
Intent
ManifestPermission
OpenableColumns
Settings
Uri
PREFIX KAndroidExtras
REQUIRED_HEADERS KAndroidExtras_android_HEADERS
RELATIVE android
)
ecm_generate_headers(KAndroidExtras_java_FORWARDING_HEADERS
HEADER_NAMES
JavaLocale
JavaTypes
PREFIX KAndroidExtras
REQUIRED_HEADERS KAndroidExtras_java_HEADERS
RELATIVE java
)
ecm_generate_headers(KAndroidExtras_jni_FORWARDING_HEADERS
HEADER_NAMES
JniArgument
JniArray
JniObject
JniMethod
JniReturnValue
JniPp
JniProperty
JniSignature
JniTypes
JniTypeTraits
PREFIX KAndroidExtras
REQUIRED_HEADERS KAndroidExtras_jni_HEADERS
RELATIVE jni
)

View File

@@ -1,25 +0,0 @@
# KAndroidExtras
Utilities for using Java Native Interface (JNI) to interface with Android platform API.
## Java Native Interface (JNI) wrapper
C++ header-only code for defining compile-time checked JNI wrappers.
Supported:
- typed `jobject` wrappers (`Jni::Object`)
- wrappers for Java arrays holding primitive or non-primitive content (`Jni::Array`)
- reading static final properties (`JNI_CONSTANT`)
- reading and writing non-static properties (`JNI_PROPERTY`)
- static and non-static method calls, constructors (`JNI_METHOD`, `JNI_STATIC_METHOD`, `JNI_CONSTRUCTOR`)
Not yet supported:
- registering native methods for Java -> C++ calls
## JNI mock implementation
This is useful for automated testing of JNI code on other platforms than Android.
## Wrappers for Java and Android types
Predefined wrappers for common platform types needed in multiple places.

View File

@@ -1,2 +0,0 @@
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: CC0-1.0

View File

@@ -1,38 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "activity.h"
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/Intent>
#include <KAndroidExtras/JniSignature>
#include "private/qandroidextras_p.h"
#include <QCoreApplication>
#include <QJniEnvironment>
using namespace KAndroidExtras;
Intent Activity::getIntent()
{
const QJniObject activity = QNativeInterface::QAndroidApplication::context();
if (!activity.isValid())
return {};
const auto intent = activity.callObjectMethod("getIntent", Jni::signature<android::content::Intent()>());
return Intent(Jni::fromHandle<Intent>(intent));
}
bool Activity::startActivity(const Intent &intent, int receiverRequestCode)
{
QJniEnvironment jniEnv;
QtAndroidPrivate::startActivity(intent, receiverRequestCode);
if (jniEnv->ExceptionCheck()) {
jniEnv->ExceptionClear();
return false;
}
return true;
}

View File

@@ -1,29 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_ACTIVITY_H
#define KANDROIDEXTRAS_ACTIVITY_H
#include "kandroidextras_export.h"
namespace KAndroidExtras
{
class Intent;
/** Methods around android.app.Activity. */
namespace Activity
{
/** Returns the Intent that started the activity. */
KANDROIDEXTRAS_EXPORT Intent getIntent();
/** Same as QtAndroid::startActivity(), but with exception handling. */
KANDROIDEXTRAS_EXPORT bool startActivity(const Intent &intent, int receiverRequestCode); // TODO add callback arg
}
}
#endif // KANDROIDEXTRAS_ACTIVITY_H

View File

@@ -1,13 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
// list all headers here that have no .cpp file
// this only serves as a guarantee that these headers actually compile
#include "androidtypes.h"
#include "calendarcontract.h"
#include "manifestpermission.h"
#include "openablecolumns.h"
#include "settings.h"

View File

@@ -1,30 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_ANDROIDTYPES_H
#define KANDROIDEXTRAS_ANDROIDTYPES_H
#include <KAndroidExtras/JniTypes>
namespace KAndroidExtras
{
JNI_TYPE(android, content, ContentResolver)
JNI_TYPE(android, content, Context)
JNI_TYPE(android, content, Intent)
JNI_TYPE(android, database, Cursor)
JNI_NESTED_TYPE(android, Manifest, permission)
JNI_TYPE(android, net, Uri)
JNI_NESTED_TYPE(android, provider, CalendarContract, AttendeesColumns)
JNI_NESTED_TYPE(android, provider, CalendarContract, CalendarColumns)
JNI_NESTED_TYPE(android, provider, CalendarContract, EventsColumns)
JNI_NESTED_TYPE(android, provider, CalendarContract, RemindersColumns)
JNI_TYPE(android, provider, OpenableColumns)
JNI_TYPE(android, provider, Settings)
}
#endif // KANDROIDEXTRAS_ANDROIDTYPES_H

View File

@@ -1,85 +0,0 @@
/*
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_CALENDARCONTRACT_H
#define KANDROIDEXTRAS_CALENDARCONTRACT_H
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/JavaTypes>
#include <KAndroidExtras/JniObject>
#include <KAndroidExtras/JniProperty>
namespace KAndroidExtras
{
/** CalendarContracts.EventColumns wrapper. */
class CalendarColumns
{
JNI_UNMANAGED_OBJECT(CalendarColumns, android::provider::CalendarContract_CalendarColumns)
JNI_CONSTANT(jint, CAL_ACCESS_CONTRIBUTOR)
JNI_CONSTANT(jint, CAL_ACCESS_EDITOR)
JNI_CONSTANT(jint, CAL_ACCESS_FREEBUSY)
JNI_CONSTANT(jint, CAL_ACCESS_NONE)
JNI_CONSTANT(jint, CAL_ACCESS_OVERRIDE)
JNI_CONSTANT(jint, CAL_ACCESS_OWNER)
JNI_CONSTANT(jint, CAL_ACCESS_READ)
JNI_CONSTANT(jint, CAL_ACCESS_RESPOND)
JNI_CONSTANT(jint, CAL_ACCESS_ROOT)
};
/** CalendarContracts.EventColumns wrapper. */
class EventsColumns
{
JNI_UNMANAGED_OBJECT(EventsColumns, android::provider::CalendarContract_EventsColumns)
JNI_CONSTANT(jint, ACCESS_CONFIDENTIAL)
JNI_CONSTANT(jint, ACCESS_DEFAULT)
JNI_CONSTANT(jint, ACCESS_PRIVATE)
JNI_CONSTANT(jint, ACCESS_PUBLIC)
JNI_CONSTANT(jint, AVAILABILITY_BUSY)
JNI_CONSTANT(jint, AVAILABILITY_FREE)
JNI_CONSTANT(jint, AVAILABILITY_TENTATIVE)
};
/** CalendarContracts.AttendeesColumns wrapper. */
class AttendeesColumns
{
JNI_UNMANAGED_OBJECT(AttendeesColumns, android::provider::CalendarContract_AttendeesColumns)
JNI_CONSTANT(jint, ATTENDEE_STATUS_ACCEPTED)
JNI_CONSTANT(jint, ATTENDEE_STATUS_DECLINED)
JNI_CONSTANT(jint, ATTENDEE_STATUS_INVITED)
JNI_CONSTANT(jint, ATTENDEE_STATUS_NONE)
JNI_CONSTANT(jint, ATTENDEE_STATUS_TENTATIVE)
JNI_CONSTANT(jint, RELATIONSHIP_ATTENDEE)
JNI_CONSTANT(jint, RELATIONSHIP_NONE)
JNI_CONSTANT(jint, RELATIONSHIP_ORGANIZER)
JNI_CONSTANT(jint, RELATIONSHIP_PERFORMER)
JNI_CONSTANT(jint, RELATIONSHIP_SPEAKER)
JNI_CONSTANT(jint, TYPE_NONE)
JNI_CONSTANT(jint, TYPE_OPTIONAL)
JNI_CONSTANT(jint, TYPE_REQUIRED)
JNI_CONSTANT(jint, TYPE_RESOURCE)
};
/** CalendarContract.RemindersColumns wrapper. */
class RemindersColumns
{
JNI_UNMANAGED_OBJECT(RemindersColumns, android::provider::CalendarContract_RemindersColumns)
JNI_CONSTANT(jint, METHOD_ALARM)
JNI_CONSTANT(jint, METHOD_ALERT)
JNI_CONSTANT(jint, METHOD_DEFAULT)
JNI_CONSTANT(jint, METHOD_EMAIL)
JNI_CONSTANT(jint, METHOD_SMS)
};
}
#endif // KANDROIDEXTRAS_OPENABLECOLUMNS_H

View File

@@ -1,53 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "contentresolver.h"
#include "openablecolumns.h"
#include "uri.h"
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/JniSignature>
#include <QCoreApplication>
#include <QString>
#include <QUrl>
using namespace KAndroidExtras;
QJniObject ContentResolver::get()
{
const QJniObject context = QNativeInterface::QAndroidApplication::context();
return context.callObjectMethod("getContentResolver", Jni::signature<android::content::ContentResolver()>());
}
QString ContentResolver::mimeType(const QUrl &url)
{
auto cs = ContentResolver::get();
const auto uri = Uri::fromUrl(url);
auto mt = cs.callObjectMethod("getType", Jni::signature<java::lang::String(android::net::Uri)>(), uri.object<jobject>());
return mt.toString();
}
QString ContentResolver::fileName(const QUrl &url)
{
auto cs = ContentResolver::get();
const auto uri = Uri::fromUrl(url);
// query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
auto cursor = cs.callObjectMethod(
"query",
Jni::signature<android::database::Cursor(android::net::Uri, java::lang::String[], java::lang::String, java::lang::String[], java::lang::String)>(),
uri.object<jobject>(),
0,
0,
0,
0);
const QJniObject DISPLAY_NAME = OpenableColumns::DISPLAY_NAME;
const auto nameIndex = cursor.callMethod<jint>("getColumnIndex", (const char *)Jni::signature<int(java::lang::String)>(), DISPLAY_NAME.object());
cursor.callMethod<jboolean>("moveToFirst", (const char *)Jni::signature<bool()>());
return cursor.callObjectMethod("getString", Jni::signature<java::lang::String(int)>(), nameIndex).toString();
}

View File

@@ -1,37 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_CONTENTRESOLVER_H
#define KANDROIDEXTRAS_CONTENTRESOLVER_H
#include "kandroidextras_export.h"
#include <qglobal.h>
class QJniObject;
class QString;
class QUrl;
namespace KAndroidExtras
{
/** Methods for working with Android's ContentResolver. */
namespace ContentResolver
{
/** Get the JNI content resolver. */
KANDROIDEXTRAS_EXPORT QJniObject get();
/** Returns the mime type of the given content: URL.
* @see Android ContentResolver.getType
*/
KANDROIDEXTRAS_EXPORT QString mimeType(const QUrl &url);
/** File name of a file provided by a content: URL. */
KANDROIDEXTRAS_EXPORT QString fileName(const QUrl &url);
}
}
#endif // KANDROIDEXTRAS_CONTENTRESOLVER_H

View File

@@ -1,20 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "context.h"
#include <KAndroidExtras/JavaTypes>
#include <KAndroidExtras/JniSignature>
#include <QCoreApplication>
using namespace KAndroidExtras;
QJniObject Context::getPackageName()
{
const QJniObject context = QNativeInterface::QAndroidApplication::context();
return context.callObjectMethod("getPackageName", Jni::signature<java::lang::String()>());
}

View File

@@ -1,25 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_CONTEXT_H
#define KANDROIDEXTRAS_CONTEXT_H
#include "kandroidextras_export.h"
class QJniObject;
namespace KAndroidExtras
{
/** Methods around android.content.Context. */
namespace Context
{
KANDROIDEXTRAS_EXPORT QJniObject getPackageName();
}
}
#endif // KANDROIDEXTRAS_CONTEXT_H

View File

@@ -1,39 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "intent.h"
#include "uri.h"
#include <KAndroidExtras/JniArray>
#include <KAndroidExtras/JniSignature>
#include <QUrl>
using namespace KAndroidExtras;
Intent::~Intent() = default;
Intent::operator QJniObject() const
{
return jniHandle();
}
template<typename T>
QJniObject Intent::getObjectExtra(const char *methodName, const QJniObject &name) const
{
return jniHandle().callObjectMethod(methodName, Jni::signature<T(java::lang::String)>(), name.object());
}
QString Intent::getStringExtra(const QJniObject &name) const
{
return getObjectExtra<java::lang::String>("getStringExtra", name).toString();
}
QStringList Intent::getStringArrayExtra(const QJniObject &name) const
{
const auto extra = getObjectExtra<Jni::Array<java::lang::String>>("getStringArrayExtra", name);
return Jni::fromArray<QStringList>(extra);
}

View File

@@ -1,99 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_INTENT_H
#define KANDROIDEXTRAS_INTENT_H
#include "kandroidextras_export.h"
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/JavaTypes>
#include <KAndroidExtras/JniMethod>
#include <KAndroidExtras/JniProperty>
#include <KAndroidExtras/Uri>
class QUrl;
namespace KAndroidExtras
{
/** Methods to interact with android.content.Intent objects.
* This does not only offer features beyond what QAndroidIntent, it also provides
* a putExtra() implementation that actually interoperates with system services.
*/
class KANDROIDEXTRAS_EXPORT Intent
{
JNI_OBJECT(Intent, android::content::Intent)
public:
/** Creates a new empty intent. */
JNI_CONSTRUCTOR(Intent)
~Intent();
/** Add a category to the intent. */
JNI_METHOD(android::content::Intent, addCategory, java::lang::String)
/** Add flags to this intent. */
JNI_METHOD(android::content::Intent, addFlags, jint)
/** Returns the data of this intent. */
JNI_METHOD(android::net::Uri, getData)
/** Get the intent action. */
JNI_METHOD(java::lang::String, getAction)
/** Sets the action of the intent. */
JNI_METHOD(android::content::Intent, setAction, java::lang::String)
/** Set the data URL of this intent. */
JNI_METHOD(android::content::Intent, setData, android::net::Uri)
/** Returns the mimetype of this intent. */
JNI_METHOD(java::lang::String, getType)
/** Set the mime type for this intent. */
JNI_METHOD(android::content::Intent, setType, java::lang::String)
/** Read extra intent data. */
QString getStringExtra(const QJniObject &name) const;
QStringList getStringArrayExtra(const QJniObject &name) const;
/** Add extra intent data of type @tparam T. */
template<typename T>
inline void putExtra(const QJniObject &name, const QJniObject &value)
{
jniHandle().callObjectMethod("putExtra", Jni::signature<android::content::Intent(java::lang::String, T)>(), name.object(), value.object());
}
/** Implicit conversion to an QJniObject. */
operator QJniObject() const;
/** Action constant for create document intents. */
JNI_CONSTANT(java::lang::String, ACTION_CREATE_DOCUMENT)
/** Main activity entry point. */
JNI_CONSTANT(java::lang::String, ACTION_MAIN)
/** Action constant for open document intents. */
JNI_CONSTANT(java::lang::String, ACTION_OPEN_DOCUMENT)
/** Action constant for viewing intents. */
JNI_CONSTANT(java::lang::String, ACTION_VIEW)
/** Share data. */
JNI_CONSTANT(java::lang::String, ACTION_SEND)
/** Share multiple data items. */
JNI_CONSTANT(java::lang::String, ACTION_SEND_MULTIPLE)
/** Category constant for openable content. */
JNI_CONSTANT(java::lang::String, CATEGORY_OPENABLE)
JNI_CONSTANT(java::lang::String, EXTRA_EMAIL)
JNI_CONSTANT(java::lang::String, EXTRA_STREAM)
JNI_CONSTANT(java::lang::String, EXTRA_SUBJECT)
JNI_CONSTANT(java::lang::String, EXTRA_TEXT)
/** Flag for granting read URI permissions on content providers. */
JNI_CONSTANT(jint, FLAG_GRANT_READ_URI_PERMISSION)
/** Flag for granting write URI permissions on content providers. */
JNI_CONSTANT(jint, FLAG_GRANT_WRITE_URI_PERMISSION)
private:
template<typename T>
QJniObject getObjectExtra(const char *methodName, const QJniObject &name) const;
};
}
#endif // KANDROIDEXTRAS_INTENT_H

View File

@@ -1,36 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_MANIFESTPERMISSIONM_H
#define KANDROIDEXTRAS_MANIFESTPERMISSIONM_H
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/JavaTypes>
#include <KAndroidExtras/JniObject>
#include <KAndroidExtras/JniProperty>
namespace KAndroidExtras
{
/**
* Access to manifest permissions.
* @see https://developer.android.com/reference/android/Manifest.permission
*/
class ManifestPermission
{
JNI_UNMANAGED_OBJECT(ManifestPermission, android::Manifest_permission)
public:
JNI_CONSTANT(java::lang::String, READ_CALENDAR)
JNI_CONSTANT(java::lang::String, WRITE_CALENDAR)
JNI_CONSTANT(java::lang::String, READ_EXTERNAL_STORAGE)
JNI_CONSTANT(java::lang::String, WRITE_EXTERNAL_STORAGE)
JNI_CONSTANT(java::lang::String, POST_NOTIFICATIONS)
JNI_CONSTANT(java::lang::String, CAMERA)
};
}
#endif // KANDROIDEXTRAS_MANIFESTPERMISSIONM_H

View File

@@ -1,32 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_OPENABLECOLUMNS_H
#define KANDROIDEXTRAS_OPENABLECOLUMNS_H
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/JavaTypes>
#include <KAndroidExtras/JniObject>
#include <KAndroidExtras/JniProperty>
namespace KAndroidExtras
{
/**
* Constants for ContentResolver queries.
* @see https://developer.android.com/reference/android/provider/OpenableColumns
*/
class OpenableColumns
{
JNI_UNMANAGED_OBJECT(OpenableColumns, android::provider::OpenableColumns)
public:
JNI_CONSTANT(java::lang::String, DISPLAY_NAME)
JNI_CONSTANT(java::lang::String, SIZE)
};
}
#endif // KANDROIDEXTRAS_OPENABLECOLUMNS_H

View File

@@ -1,28 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_SETTINGS_H
#define KANDROIDEXTRAS_SETTINGS_H
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/JniProperty>
namespace KAndroidExtras
{
/** Methods around android.provider.Settings. */
class 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)
JNI_CONSTANT(java::lang::String, EXTRA_CHANNEL_ID)
};
}
#endif // KANDROIDEXTRAS_SETTINGS_H

View File

@@ -1,28 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "uri.h"
#include <KAndroidExtras/JavaTypes>
#include <KAndroidExtras/JniSignature>
using namespace KAndroidExtras;
QJniObject Uri::fromUrl(const QUrl &url)
{
return QJniObject::callStaticObjectMethod(Jni::typeName<android::net::Uri>(),
"parse",
Jni::signature<android::net::Uri(java::lang::String)>(),
QJniObject::fromString(url.toString(QUrl::FullyEncoded)).object<jstring>());
}
QUrl Uri::toUrl(const QJniObject &uri)
{
if (!uri.isValid()) {
return QUrl();
}
return QUrl(uri.callObjectMethod("toString", Jni::signature<java::lang::String()>()).toString());
}

View File

@@ -1,34 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_URI_H
#define KANDROIDEXTRAS_URI_H
#include "kandroidextras_export.h"
#include <KAndroidExtras/AndroidTypes>
#include <KAndroidExtras/JniTypeTraits>
#include <QUrl>
namespace KAndroidExtras
{
/** Conversion methods for android.net.Uri. */
namespace Uri
{
/** Create an android.net.Uri from a QUrl. */
KANDROIDEXTRAS_EXPORT QJniObject fromUrl(const QUrl &url);
/** Convert a android.net.Uri to a QUrl. */
KANDROIDEXTRAS_EXPORT QUrl toUrl(const QJniObject &uri);
}
JNI_DECLARE_CONVERTER(android::net::Uri, QUrl, (Uri::toUrl(value)), (Uri::fromUrl(value)))
}
#endif // KANDROIDEXTRAS_URI_H

View File

@@ -1,208 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef FAKE_JNI_H
#define FAKE_JNI_H
#include <cstdint>
#include <numeric>
#ifdef Q_OS_ANDROID
#error This is a mock object for use on non-Android!
#endif
typedef uint8_t jboolean;
typedef int8_t jbyte;
typedef uint16_t jchar;
typedef int16_t jshort;
typedef int32_t jint;
typedef int64_t jlong;
typedef float jfloat;
typedef double jdouble;
typedef jint jsize;
typedef void *jobject;
typedef jobject jclass;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jobjectArray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
namespace detail
{
template<typename T>
inline T *getArrayElements(jsize size)
{
T *array = new T[size];
std::iota(array, array + size, T{});
return array;
}
}
struct JNIEnv {
inline bool ExceptionCheck()
{
return false;
}
inline void ExceptionClear()
{
}
inline int GetArrayLength(jobjectArray)
{
return m_arrayLength;
}
inline jobjectArray NewObjectArray(jsize, jclass, jobject)
{
return nullptr;
}
inline jobject GetObjectArrayElement(jobjectArray, int index)
{
return reinterpret_cast<jobject>(index);
}
inline void SetObjectArrayElement(jobjectArray, jsize, jobject)
{
}
inline jbooleanArray NewBooleanArray(jsize)
{
return nullptr;
}
inline jbyteArray NewByteArray(jsize)
{
return nullptr;
}
inline jcharArray NewCharArray(jsize)
{
return nullptr;
}
inline jshortArray NewShortArray(jsize)
{
return nullptr;
}
inline jintArray NewIntArray(jsize)
{
return nullptr;
}
inline jlongArray NewLongArray(jsize)
{
return nullptr;
}
inline jfloatArray NewFloatArray(jsize)
{
return nullptr;
}
inline jdoubleArray NewDoubleArray(jsize)
{
return nullptr;
}
inline jboolean *GetBooleanArrayElements(jbooleanArray, jboolean *)
{
return detail::getArrayElements<jboolean>(m_arrayLength);
}
inline jbyte *GetByteArrayElements(jbyteArray, jboolean *)
{
return detail::getArrayElements<jbyte>(m_arrayLength);
}
inline jchar *GetCharArrayElements(jcharArray, jboolean *)
{
return detail::getArrayElements<jchar>(m_arrayLength);
}
inline jshort *GetShortArrayElements(jshortArray, jboolean *)
{
return detail::getArrayElements<jshort>(m_arrayLength);
}
inline jint *GetIntArrayElements(jintArray, jboolean *)
{
return detail::getArrayElements<jint>(m_arrayLength);
}
inline jlong *GetLongArrayElements(jlongArray, jboolean *)
{
return detail::getArrayElements<jlong>(m_arrayLength);
}
inline jfloat *GetFloatArrayElements(jfloatArray, jboolean *)
{
return detail::getArrayElements<jfloat>(m_arrayLength);
}
inline jdouble *GetDoubleArrayElements(jdoubleArray, jboolean *)
{
return detail::getArrayElements<jdouble>(m_arrayLength);
}
inline void ReleaseBooleanArrayElements(jbooleanArray, jboolean *data, jint)
{
delete[] data;
}
inline void ReleaseByteArrayElements(jbyteArray, jbyte *data, jint)
{
delete[] data;
}
inline void ReleaseCharArrayElements(jcharArray, jchar *data, jint)
{
delete[] data;
}
inline void ReleaseShortArrayElements(jshortArray, jshort *data, jint)
{
delete[] data;
}
inline void ReleaseIntArrayElements(jintArray, jint *data, jint)
{
delete[] data;
}
inline void ReleaseLongArrayElements(jlongArray, jlong *data, jint)
{
delete[] data;
}
inline void ReleaseFloatArrayElements(jfloatArray, jfloat *data, jint)
{
delete[] data;
}
inline void ReleaseDoubleArrayElements(jdoubleArray, jdouble *data, jint)
{
delete[] data;
}
inline void SetBooleanArrayRegion(jbooleanArray, jsize, jsize, const jboolean *)
{
}
inline void SetByteArrayRegion(jbyteArray, jsize, jsize, const jbyte *)
{
}
inline void SetCharArrayRegion(jcharArray, jsize, jsize, const jchar *)
{
}
inline void SetShortArrayRegion(jshortArray, jsize, jsize, const jshort *)
{
}
inline void SetIntArrayRegion(jintArray, jsize, jsize, const jint *)
{
}
inline void SetLongArrayRegion(jlongArray, jsize, jsize, const jlong *)
{
}
inline void SetFloatArrayRegion(jfloatArray, jsize, jsize, const jfloat *)
{
}
inline void SetDoubleArrayRegion(jdoubleArray, jsize, jsize, const jdouble *)
{
}
static jsize m_arrayLength;
};
#define JNI_COMMIT 1
#define JNI_ABORT 2
#endif

View File

@@ -1,9 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "jni.h"
int JNIEnv::m_arrayLength = 0;

View File

@@ -1,30 +0,0 @@
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_MOCK_JNIENVIRONMENT_H
#define KANDROIDEXTRAS_MOCK_JNIENVIRONMENT_H
#include "jni.h"
namespace KAndroidExtras
{
class MockJniEnvironment
{
public:
inline jclass findClass(const char *)
{
return nullptr;
}
inline JNIEnv *operator->()
{
return &m_env;
}
protected:
mutable JNIEnv m_env;
};
}
#endif

View File

@@ -1,80 +0,0 @@
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "mock_jniobject.h"
using namespace KAndroidExtras::Internal;
QStringList MockJniObjectBase::m_staticProtocol;
class MockJniObjectBasePrivate : public QSharedData
{
public:
QStringList protocol;
QHash<QByteArray, QVariant> properties;
QVariant value;
};
MockJniObjectBase::MockJniObjectBase()
: d(new MockJniObjectBasePrivate)
{
}
MockJniObjectBase::MockJniObjectBase(const char *className)
: d(new MockJniObjectBasePrivate)
{
addToProtocol(QLatin1StringView("ctor: ") + QLatin1StringView(className));
}
MockJniObjectBase::MockJniObjectBase(jobject object)
: d(new MockJniObjectBasePrivate)
{
Q_UNUSED(object);
addToProtocol(QLatin1StringView("ctor: o"));
}
MockJniObjectBase::MockJniObjectBase(const MockJniObjectBase &) = default;
MockJniObjectBase &MockJniObjectBase::operator=(const MockJniObjectBase &) = default;
MockJniObjectBase::~MockJniObjectBase() = default;
QStringList MockJniObjectBase::protocol() const
{
return d->protocol;
}
void MockJniObjectBase::addToProtocol(const QString &line) const
{
d->protocol.push_back(line);
}
void MockJniObjectBase::setProtocol(const QStringList &protocol)
{
d->protocol = protocol;
}
QVariant MockJniObjectBase::property(const QByteArray &name) const
{
return d->properties.value(name);
}
void MockJniObjectBase::setProperty(const QByteArray &name, const QVariant &value)
{
d->properties.insert(name, value);
}
QVariant MockJniObjectBase::value() const
{
return d->value;
}
void MockJniObjectBase::setValue(const QVariant &value)
{
d->value = value;
}
void MockJniObjectBase::setData(jobject object)
{
d = reinterpret_cast<MockJniObjectBasePrivate *>(object);
}

View File

@@ -1,262 +0,0 @@
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_MOCK_JNIOBJECT_H
#define KANDROIDEXTRAS_MOCK_JNIOBJECT_H
#include "jni.h"
#include "kandroidextras_export.h"
#include "kandroidextras/jnisignature.h"
#include "kandroidextras/jnitypes.h"
#include <QExplicitlySharedDataPointer>
#include <QStringList>
#include <QVariant>
class MockJniObjectBasePrivate;
namespace KAndroidExtras
{
namespace Internal
{
template<typename T>
constexpr inline const char *argTypeToString()
{
return KAndroidExtras::Jni::signature<T>();
}
template<>
constexpr inline const char *argTypeToString<jobject>()
{
return "o";
}
class KANDROIDEXTRAS_EXPORT MockJniObjectBase
{
public:
MockJniObjectBase();
MockJniObjectBase(const MockJniObjectBase &);
~MockJniObjectBase();
MockJniObjectBase &operator=(const MockJniObjectBase &);
MockJniObjectBase(const char *className);
inline MockJniObjectBase(const char *className, const char *signature)
: MockJniObjectBase()
{
addToProtocol(QLatin1StringView("ctor: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(signature));
}
template<typename... Args>
inline MockJniObjectBase(const char *className, const char *signature, Args...)
: MockJniObjectBase()
{
addToProtocol(QLatin1StringView("ctor: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(signature) + QLatin1StringView(" (")
+ (... + QLatin1StringView(KAndroidExtras::Internal::argTypeToString<Args>())) + QLatin1Char(')'));
}
MockJniObjectBase(jobject object);
bool isValid() const
{
return true;
}
static QStringList m_staticProtocol;
QStringList protocol() const;
void addToProtocol(const QString &line) const;
protected:
QVariant property(const QByteArray &name) const;
void setProperty(const QByteArray &name, const QVariant &value);
QVariant value() const;
void setValue(const QVariant &value);
void setData(jobject object);
void setProtocol(const QStringList &protocol);
QExplicitlySharedDataPointer<MockJniObjectBasePrivate> d;
};
template<typename JniObjectT>
class MockJniObject : public MockJniObjectBase
{
public:
using MockJniObjectBase::MockJniObjectBase;
static inline JniObjectT fromString(const QString &s)
{
JniObjectT o;
o.setValue(s);
return o;
}
static inline JniObjectT fromLocalRef(jobject o)
{
JniObjectT obj;
obj.addToProtocol(QLatin1StringView("ctor: ") + QString::number((qulonglong)o));
return obj;
}
inline QString toString() const
{
return value().userType() == QMetaType::QString ? value().toString() : protocol().join(QLatin1Char('\n'));
}
inline jobject object() const
{
return d.data();
}
template<typename T>
inline T object() const
{
return {};
}
template<typename T, typename... Args>
inline T callMethod(const char *methodName, const char *signature, Args...) const
{
const QString s = QLatin1StringView("callMethod: ") + QLatin1StringView(methodName) + QLatin1Char(' ') + QLatin1StringView(signature)
+ QLatin1StringView(" (") + (... + QLatin1StringView(KAndroidExtras::Internal::argTypeToString<Args>())) + QLatin1Char(')');
addToProtocol(s);
if constexpr (!std::is_same_v<T, void>) {
return {};
}
}
template<typename T>
inline T callMethod(const char *methodName, const char *signature) const
{
const QString s =
QLatin1StringView("callMethod: ") + QLatin1StringView(methodName) + QLatin1Char(' ') + QLatin1StringView(signature) + QLatin1StringView(" ()");
addToProtocol(s);
if constexpr (!std::is_same_v<T, void>) {
return {};
}
}
template<typename... Args>
inline JniObjectT callObjectMethod(const char *methodName, const char *signature, Args...) const
{
const QString s = QLatin1StringView("callObjectMethod: ") + QLatin1StringView(methodName) + QLatin1Char(' ') + QLatin1StringView(signature)
+ QLatin1StringView(" (") + (... + QLatin1StringView(KAndroidExtras::Internal::argTypeToString<Args>())) + QLatin1Char(')');
addToProtocol(s);
JniObjectT obj;
obj.setProtocol(protocol());
return obj;
}
inline JniObjectT callObjectMethod(const char *methodName, const char *signature) const
{
addToProtocol(QLatin1StringView("callObjectMethod: ") + QLatin1StringView(methodName) + QLatin1Char(' ') + QLatin1StringView(signature)
+ QLatin1StringView(" ()"));
JniObjectT obj;
obj.setProtocol(protocol());
return obj;
}
template<typename T>
inline T getField(const char *fieldName) const
{
addToProtocol(QLatin1StringView("getField: ") + QLatin1StringView(fieldName) + QLatin1Char(' ')
+ QLatin1StringView(KAndroidExtras::Jni::signature<T>()));
return property(fieldName).template value<T>();
}
inline JniObjectT getObjectField(const char *fieldName, const char *signature) const
{
addToProtocol(QLatin1StringView("getObjectField: ") + QLatin1StringView(fieldName) + QLatin1Char(' ') + QLatin1StringView(signature));
return property(fieldName).template value<JniObjectT>();
}
template<typename T>
inline void setField(const char *fieldName, const char *signature, T value)
{
Q_UNUSED(value);
addToProtocol(QLatin1StringView("setField: ") + QLatin1StringView(fieldName) + QLatin1Char(' ') + QLatin1StringView(signature));
if constexpr (std::is_same_v<jobject, T>) {
setObjectProperty(fieldName, value);
} else {
setProperty(fieldName, QVariant::fromValue(value));
}
}
template<typename T>
inline void setField(const char *fieldName, T value)
{
setField(fieldName, KAndroidExtras::Jni::signature<T>(), value);
}
template<typename T, typename... Args>
static inline T callStaticMethod(const char *className, const char *methodName, const char *signature, Args...)
{
const QString s = QLatin1StringView("callStaticMethod: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(methodName)
+ QLatin1Char(' ') + QLatin1StringView(signature) + QLatin1StringView(" (")
+ (... + QLatin1StringView(KAndroidExtras::Internal::argTypeToString<Args>())) + QLatin1Char(')');
m_staticProtocol.push_back(s);
if constexpr (!std::is_same_v<T, void>) {
return {};
}
}
template<typename T>
static inline T callStaticMethod(const char *className, const char *methodName, const char *signature)
{
const QString s = QLatin1StringView("callStaticMethod: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(methodName)
+ QLatin1Char(' ') + QLatin1StringView(signature) + QLatin1StringView(" ()");
m_staticProtocol.push_back(s);
if constexpr (!std::is_same_v<T, void>) {
return {};
}
}
template<typename... Args>
static inline JniObjectT callStaticObjectMethod(const char *className, const char *methodName, const char *signature, Args...)
{
const QString s = QLatin1StringView("callStaticObjectMethod: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(methodName)
+ QLatin1Char(' ') + QLatin1StringView(signature) + QLatin1StringView(" (")
+ (... + QLatin1StringView(KAndroidExtras::Internal::argTypeToString<Args>())) + QLatin1Char(')');
JniObjectT obj;
obj.addToProtocol(s);
return obj;
}
static inline JniObjectT callStaticObjectMethod(const char *className, const char *methodName, const char *signature)
{
JniObjectT obj;
obj.addToProtocol(QLatin1StringView("callStaticObjectMethod: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(methodName)
+ QLatin1Char(' ') + QLatin1StringView(signature) + QLatin1StringView(" ()"));
return obj;
}
static inline JniObjectT getStaticObjectField(const char *className, const char *fieldName, const char *signature)
{
m_staticProtocol.push_back(QLatin1StringView("getStaticObjectField: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(fieldName)
+ QLatin1Char(' ') + QLatin1StringView(signature));
return {};
}
template<typename T>
static inline JniObjectT getStaticObjectField(const char *className, const char *fieldName)
{
m_staticProtocol.push_back(QLatin1StringView("getStaticObjectField<>: ") + QLatin1StringView(className) + QLatin1Char(' ')
+ QLatin1StringView(fieldName));
return {};
}
template<typename T>
static inline T getStaticField(const char *className, const char *fieldName)
{
m_staticProtocol.push_back(QLatin1StringView("getStaticField<>: ") + QLatin1StringView(className) + QLatin1Char(' ') + QLatin1StringView(fieldName)
+ QLatin1Char(' ') + QLatin1StringView(KAndroidExtras::Jni::signature<T>()));
return {};
}
inline void setObjectProperty(const QByteArray &name, jobject value)
{
JniObjectT o;
o.setData(value);
setProperty(name, QVariant::fromValue(o));
}
};
}
}
#endif

View File

@@ -1,24 +0,0 @@
/*
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_MOCK_QCOREAPPLICATION_H
#define KANDROIDEXTRAS_MOCK_QCOREAPPLICATION_H
#include_next <QCoreApplication>
#include <QJniObject>
namespace QNativeInterface {
namespace QAndroidApplication {
inline QJniObject context()
{
QJniObject obj;
obj.addToProtocol(QStringLiteral("global androidContext()"));
return obj;
}
}
}
#endif

View File

@@ -1,17 +0,0 @@
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_MOCK_QJNIENVIRONMENT_H
#define KANDROIDEXTRAS_MOCK_QJNIENVIRONMENT_H
#include "mock_jnienvironment.h"
class QJniEnvironment : public KAndroidExtras::MockJniEnvironment
{
public:
inline JNIEnv* jniEnv() const { return &m_env; }
};
#endif

View File

@@ -1,6 +0,0 @@
/*
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "qjniobject.h"

View File

@@ -1,19 +0,0 @@
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_MAKE_QTANDROIDEXTRAS_P_H
#define KANDROIDEXTRAS_MAKE_QTANDROIDEXTRAS_P_H
#include <QJniObject>
/** Mock object for QtAndroid namespace. */
namespace QtAndroidPrivate
{
inline void startActivity(const QJniObject &, int)
{
}
}
#endif

View File

@@ -1,20 +0,0 @@
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_MOCK_QJNIOBJECT_H
#define KANDROIDEXTRAS_MOCK_QJNIOBJECT_H
#include "mock_jniobject.h"
/** Mock object for QJniObject outside of Android, for automated testing. */
class KANDROIDEXTRAS_EXPORT QJniObject : public KAndroidExtras::Internal::MockJniObject<QJniObject>
{
public:
using MockJniObject<QJniObject>::MockJniObject;
};
Q_DECLARE_METATYPE(QJniObject)
#endif

View File

@@ -1,9 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
// list all headers here that have no .cpp file
// this only serves as a guarantee that these headers actually compile
#include "javatypes.h"

View File

@@ -1,32 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "javalocale.h"
#include <KAndroidExtras/JavaTypes>
#include <KAndroidExtras/JniSignature>
#include <QLocale>
using namespace KAndroidExtras;
QJniObject Locale::fromLocale(const QLocale &locale)
{
auto lang = QJniObject::fromString(QLocale::languageToString(locale.language()));
auto country = QJniObject::fromString(QLocale::countryToString(locale.country()));
auto script = QJniObject::fromString(QLocale::scriptToString(locale.script()));
return QJniObject(Jni::typeName<java::util::Locale>(),
(const char *)Jni::signature<void(java::lang::String, java::lang::String, java::lang::String)>(),
lang.object(),
country.object(),
script.object());
}
QJniObject Locale::current()
{
return fromLocale(QLocale());
}

View File

@@ -1,33 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_LOCALE_H
#define KANDROIDEXTRAS_LOCALE_H
#include "kandroidextras_export.h"
class QJniObject;
class QLocale;
namespace KAndroidExtras
{
/** Conversion methods between java.util.Locale and QLocale.
* @note Do not rename this file to locale.h, that clashes with POSIX locale.h when your
* include paths are unfortunately set up causing bizarre compilation issues.
*/
namespace Locale
{
/** Create an java.util.Locale object from a QLocale. */
KANDROIDEXTRAS_EXPORT QJniObject fromLocale(const QLocale &locale);
/** Create an java.util.Locale object for the current QLocale. */
KANDROIDEXTRAS_EXPORT QJniObject current();
}
}
#endif // KANDROIDEXTRAS_LOCALE_H

View File

@@ -1,24 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JAVATYPES_H
#define KANDROIDEXTRAS_JAVATYPES_H
#include <KAndroidExtras/JniTypeTraits>
#include <KAndroidExtras/JniTypes>
namespace KAndroidExtras
{
JNI_TYPE(java, io, File)
JNI_TYPE(java, lang, String)
JNI_TYPE(java, util, Locale)
JNI_DECLARE_CONVERTER(java::lang::String, QString, (value.toString()), (QJniObject::fromString(value)))
}
#endif // KANDROIDEXTRAS_JAVATYPES_H

View File

@@ -1,19 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
// list all headers here that have no .cpp file
// this only serves as a guarantee that these headers actually compile
#include "jniargument.h"
#include "jniarray.h"
#include "jnimethod.h"
#include "jniobject.h"
#include "jnipp.h"
#include "jniprimitivetypes.h"
#include "jniproperty.h"
#include "jnireturnvalue.h"
#include "jnisignature.h"
#include "jnitypes.h"
#include "jnitypetraits.h"

View File

@@ -1,57 +0,0 @@
/*
SPDX-FileCopyrightText: 2021-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIARGUMENTVALUE_H
#define KANDROIDEXTRAS_JNIARGUMENTVALUE_H
#include "jniobject.h"
#include "jnitypetraits.h"
namespace KAndroidExtras
{
namespace Jni
{
template<typename T>
class Array;
}
///@cond internal
namespace Internal
{
/** Call argument wrapper. */
template<typename T, typename = std::void_t<>>
struct argument {
static_assert(!is_invalid_primitive_type<T>::value, "Using an incompatible primitive type!");
typedef std::conditional_t<Jni::is_primitive_type<T>::value, T, const Jni::Object<T> &> type;
static inline constexpr auto toCallArgument(type value)
{
if constexpr (Jni::is_primitive_type<T>::value) {
return primitive_value<T>::toJni(value);
} else {
return value.jniHandle().object();
}
}
};
template<typename T>
struct argument<T, std::void_t<typename T::_jni_ThisType>> {
typedef const T &type;
static inline auto toCallArgument(const T &value)
{
return Jni::handle(value).object();
}
};
template<typename T>
struct argument<Jni::Array<T>> {
typedef const Jni::Array<T> &type;
static inline auto toCallArgument(const Jni::Array<T> &value)
{
return value.jniHandle().object();
}
};
}
///@endcond
}
#endif

View File

@@ -1,395 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIARRAY_H
#define KANDROIDEXTRAS_JNIARRAY_H
#include "jniargument.h"
#include "jnireturnvalue.h"
#include "jnitypetraits.h"
#include <QJniEnvironment>
namespace KAndroidExtras
{
///@cond internal
namespace Internal
{
/** Primitive type array type traits. */
template<typename T>
struct array_trait {
typedef jobjectArray type;
};
#define MAKE_ARRAY_TRAIT(base_type, type_name) \
template<> \
struct array_trait<base_type> { \
typedef base_type##Array type; \
static inline type newArray(QJniEnvironment &env, jsize size) \
{ \
return env->New##type_name##Array(size); \
} \
static inline base_type *getArrayElements(QJniEnvironment &env, type array, jboolean *isCopy) \
{ \
return env->Get##type_name##ArrayElements(array, isCopy); \
} \
static inline void releaseArrayElements(QJniEnvironment &env, type array, base_type *data, jint mode) \
{ \
return env->Release##type_name##ArrayElements(array, data, mode); \
} \
static inline void setArrayRegion(QJniEnvironment &env, type array, jsize start, jsize length, const base_type *data) \
{ \
env->Set##type_name##ArrayRegion(array, start, length, data); \
} \
};
MAKE_ARRAY_TRAIT(jboolean, Boolean)
MAKE_ARRAY_TRAIT(jbyte, Byte)
MAKE_ARRAY_TRAIT(jchar, Char)
MAKE_ARRAY_TRAIT(jshort, Short)
MAKE_ARRAY_TRAIT(jint, Int)
MAKE_ARRAY_TRAIT(jlong, Long)
MAKE_ARRAY_TRAIT(jfloat, Float)
MAKE_ARRAY_TRAIT(jdouble, Double)
#undef MAKE_ARRAY_TRAIT
/** Meta function for retrieving a JNI array .*/
template<typename Container, typename Value, bool is_primitive>
struct FromArray {
};
template<typename Container>
struct FromArray<Container, QJniObject, false> {
inline auto operator()(const QJniObject &array) const
{
if (!array.isValid()) {
return Container{};
}
const auto a = static_cast<jobjectArray>(array.object());
QJniEnvironment env;
const auto size = env->GetArrayLength(a);
Container r;
r.reserve(size);
for (auto i = 0; i < size; ++i) {
r.push_back(QJniObject::fromLocalRef(env->GetObjectArrayElement(a, i)));
}
return r;
}
};
template<typename Container, typename Value>
struct FromArray<Container, Value, false> {
inline auto operator()(const QJniObject &array) const
{
if (!array.isValid()) {
return Container{};
}
const auto a = static_cast<jobjectArray>(array.object());
QJniEnvironment env;
const auto size = env->GetArrayLength(a);
Container r;
r.reserve(size);
for (auto i = 0; i < size; ++i) {
r.push_back(Jni::reverse_converter<Value>::type::convert(QJniObject::fromLocalRef(env->GetObjectArrayElement(a, i))));
}
return r;
}
};
// specializations for primitive types
template<typename Container, typename Value>
struct FromArray<Container, Value, true> {
typedef array_trait<Value> _t;
inline auto operator()(const QJniObject &array) const
{
if (!array.isValid()) {
return Container{};
}
const auto a = static_cast<typename _t::type>(array.object());
QJniEnvironment env;
const auto size = env->GetArrayLength(a);
Container r;
r.reserve(size);
auto data = _t::getArrayElements(env, a, nullptr);
std::copy(data, data + size, std::back_inserter(r));
_t::releaseArrayElements(env, a, data, JNI_ABORT);
return r;
}
};
// array wrapper, common base for primitive and non-primitive types
template<typename T>
class ArrayImplBase
{
public:
typedef T value_type;
typedef jsize size_type;
typedef jsize difference_type;
ArrayImplBase() = default;
inline ArrayImplBase(const QJniObject &array)
: m_array(array)
{
}
ArrayImplBase(const ArrayImplBase &) = default;
ArrayImplBase(ArrayImplBase &&) = default;
inline size_type size() const
{
if (!m_array.isValid()) {
return 0;
}
const auto a = static_cast<typename _t::type>(m_array.object());
QJniEnvironment env;
return env->GetArrayLength(a);
}
inline operator QJniObject() const
{
return m_array;
}
inline QJniObject jniHandle() const
{
return m_array;
}
protected:
typedef array_trait<T> _t;
typename _t::type handle() const
{
return static_cast<typename _t::type>(m_array.object());
}
QJniObject m_array;
};
template<typename T, bool is_primitive>
class ArrayImpl
{
};
// array wrapper for primitive types
template<typename T>
class ArrayImpl<T, true> : public ArrayImplBase<T>
{
static_assert(!Internal::is_invalid_primitive_type<T>::value, "Using an incompatible primitive type!");
public:
inline ArrayImpl(const QJniObject &array)
: ArrayImplBase<T>(array)
{
// ### do this on demand?
getArrayElements();
}
/** Create a new array with @p size elements. */
inline explicit ArrayImpl(jsize size)
{
QJniEnvironment env;
ArrayImplBase<T>::m_array = QJniObject::fromLocalRef(ArrayImplBase<T>::_t::newArray(env, size));
getArrayElements();
}
ArrayImpl() = default;
ArrayImpl(const ArrayImpl &) = delete; // ### ref count m_data and allow copying?
ArrayImpl(ArrayImpl &&) = default;
~ArrayImpl()
{
QJniEnvironment env;
ArrayImplBase<T>::_t::releaseArrayElements(env, this->handle(), m_data, JNI_ABORT);
}
T operator[](jsize index) const
{
return m_data[index];
}
T *begin() const
{
return m_data;
}
T *end() const
{
return m_data + ArrayImplBase<T>::size();
}
private:
inline void getArrayElements()
{
if (!ArrayImplBase<T>::m_array.isValid()) {
return;
}
QJniEnvironment env;
m_data = ArrayImplBase<T>::_t::getArrayElements(env, this->handle(), nullptr);
}
T *m_data = nullptr;
};
// array wrapper for non-primitive types
template<typename T>
class ArrayImpl<T, false> : public ArrayImplBase<T>
{
public:
using ArrayImplBase<T>::ArrayImplBase;
/** Create a new array with @p size elements initialized with @p value. */
explicit inline ArrayImpl(jsize size, typename Internal::argument<T>::type value)
{
QJniEnvironment env;
auto clazz = env.findClass(Jni::typeName<T>());
ArrayImplBase<T>::m_array = QJniObject::fromLocalRef(env->NewObjectArray(size, clazz, Internal::argument<T>::toCallArgument(value)));
}
/** Create a new array with @p size null elements. */
explicit inline ArrayImpl(jsize size, std::nullptr_t = nullptr)
{
QJniEnvironment env;
auto clazz = env.findClass(Jni::typeName<T>());
ArrayImplBase<T>::m_array = QJniObject::fromLocalRef(env->NewObjectArray(size, clazz, nullptr));
}
ArrayImpl() = default;
ArrayImpl(const ArrayImpl &) = default;
ArrayImpl(ArrayImpl &&) = default;
auto operator[](jsize index) const
{
QJniEnvironment env;
return Internal::return_wrapper<T>::toReturnValue(QJniObject::fromLocalRef(env->GetObjectArrayElement(this->handle(), index)));
}
class ref
{
public:
inline operator auto()
{
QJniEnvironment env;
return Internal::return_wrapper<T>::toReturnValue(QJniObject::fromLocalRef(env->GetObjectArrayElement(c.handle(), index)));
}
inline ref &operator=(typename Internal::argument<T>::type v)
{
QJniEnvironment env;
env->SetObjectArrayElement(c.handle(), index, Internal::argument<T>::toCallArgument(v));
return *this;
}
private:
ArrayImpl<T, false> &c;
jsize index;
friend class ArrayImpl<T, false>;
inline ref(jsize _i, ArrayImpl<T, false> &_c)
: c(_c)
, index(_i)
{
}
};
ref operator[](jsize index)
{
return ref(index, *this);
}
class const_iterator
{
const ArrayImpl<T, false> &c;
jsize i = 0;
public:
typedef jsize difference_type;
typedef T value_type;
typedef T &reference;
typedef std::random_access_iterator_tag iterator_category;
typedef T *pointer;
const_iterator(const ArrayImpl<T, false> &_c, jsize _i)
: c(_c)
, i(_i)
{
}
difference_type operator-(const_iterator other) const
{
return i - other.i;
}
const_iterator &operator++()
{
++i;
return *this;
}
const_iterator operator++(int)
{
return const_iterator(c, i++);
}
bool operator==(const_iterator other) const
{
return i == other.i;
}
bool operator!=(const_iterator other) const
{
return i != other.i;
}
auto operator*() const
{
return c[i];
}
};
const_iterator begin() const
{
return const_iterator(*this, 0);
}
const_iterator end() const
{
return const_iterator(*this, ArrayImplBase<T>::size());
}
};
}
///@endcond
namespace Jni
{
/** Convert a JNI array to a C++ container.
* Container value types can be any of
* - QJniObject
* - a primitive JNI type
* - a type with a conversion defined with @c JNI_DECLARE_CONVERTER
*/
template<typename Container>
constexpr
__attribute__((__unused__)) Internal::FromArray<Container, typename Container::value_type, Jni::is_primitive_type<typename Container::value_type>::value>
fromArray = {};
/** Container-like wrapper for JNI arrays. */
template<typename T>
class Array : public Internal::ArrayImpl<T, Jni::is_primitive_type<T>::value>
{
public:
using Internal::ArrayImpl<T, Jni::is_primitive_type<T>::value>::ArrayImpl;
template<typename Container>
inline operator Container() const
{
// ### should this be re-implemented in terms of Jni::Array API rather than direct JNI access?
return Jni::fromArray<Container>(this->m_array);
}
};
}
}
#endif

View File

@@ -1,192 +0,0 @@
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIMETHOD_H
#define KANDROIDEXTRAS_JNIMETHOD_H
#include "jniargument.h"
#include "jniarray.h"
#include "jniobject.h"
#include "jnipp.h"
#include "jnireturnvalue.h"
#include "jnitypetraits.h"
namespace KAndroidExtras
{
///@cond internal
// method parameter generation
#define JNI_PARAM(Type, Name) typename KAndroidExtras::Internal::argument<Type>::type Name
#define JNI_PARAMS_0(accu, arg)
#define JNI_PARAMS_1(accu, arg, ...) JNI_PARAM(arg, a1)
#define JNI_PARAMS_2(accu, arg, ...) JNI_PARAM(arg, a2), JNI_PARAMS_1(accu, __VA_ARGS__)
#define JNI_PARAMS_3(accu, arg, ...) JNI_PARAM(arg, a3), JNI_PARAMS_2(accu, __VA_ARGS__)
#define JNI_PARAMS_(N, accu, ...) JNI_PP_CONCAT(JNI_PARAMS_, N)(accu, __VA_ARGS__)
#define JNI_PARAMS(...) JNI_PARAMS_(JNI_PP_NARG(__VA_ARGS__), "", __VA_ARGS__)
// method argument forwarding generation
#define JNI_ARG(Type, Name) KAndroidExtras::Internal::argument<Type>::toCallArgument(Name)
#define JNI_ARGS_0(accu, arg)
#define JNI_ARGS_1(accu, arg, ...) JNI_ARG(arg, a1)
#define JNI_ARGS_2(accu, arg, ...) JNI_ARG(arg, a2), JNI_ARGS_1(accu, __VA_ARGS__)
#define JNI_ARGS_3(accu, arg, ...) JNI_ARG(arg, a3), JNI_ARGS_2(accu, __VA_ARGS__)
#define JNI_ARGS_(N, accu, ...) JNI_PP_CONCAT(JNI_ARGS_, N)(accu, __VA_ARGS__)
#define JNI_ARGS(...) JNI_ARGS_(JNI_PP_NARG(__VA_ARGS__), "", __VA_ARGS__)
namespace Internal
{
// method call wrapper
template<typename RetT>
struct caller {
static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!");
template<typename... Args>
static auto call(const QJniObject &handle, const char *name, const char *signature, Args &&...args)
{
if constexpr (std::is_same_v<RetT, void>) {
handle.callMethod<RetT>(name, signature, std::forward<Args>(args)...);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(
handle.callMethod<typename primitive_value<RetT>::JniType>(name, signature, std::forward<Args>(args)...));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(handle.callObjectMethod(name, signature, std::forward<Args>(args)...));
}
}
static auto call(const QJniObject &handle, const char *name, const char *signature)
{
if constexpr (std::is_same_v<RetT, void>) {
return handle.callMethod<RetT>(name, signature);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(handle.callMethod<typename primitive_value<RetT>::JniType>(name, signature));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(handle.callObjectMethod(name, signature));
}
}
};
// static method call wrapper
template<typename RetT>
struct static_caller {
static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!");
template<typename... Args>
static auto call(const char *className, const char *name, const char *signature, Args &&...args)
{
if constexpr (std::is_same_v<RetT, void>) {
return QJniObject::callStaticMethod<RetT>(className, name, signature, std::forward<Args>(args)...);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(
QJniObject::callStaticMethod<typename primitive_value<RetT>::JniType>(className, name, signature, std::forward<Args>(args)...));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(QJniObject::callStaticObjectMethod(className, name, signature, std::forward<Args>(args)...));
}
}
static auto call(const char *className, const char *name, const char *signature)
{
if constexpr (std::is_same_v<RetT, void>) {
return QJniObject::callStaticMethod<RetT>(className, name, signature);
} else if constexpr (Jni::is_primitive_type<RetT>::value) {
return Internal::return_wrapper<RetT>::toReturnValue(
QJniObject::callStaticMethod<typename primitive_value<RetT>::JniType>(className, name, signature));
} else {
return Internal::return_wrapper<RetT>::toReturnValue(QJniObject::callStaticObjectMethod(className, name, signature));
}
}
};
}
///@endcond
/**
* Wrap a JNI method call.
* This will add a method named @p Name to the current class. Argument types are checked at compile time,
* with the following inputs being accepted:
* - primitive types have to match exactly
* - non-primitive types can be either passed as @c QJniObject instance or with a type that has an
* conversion registered with @c JNI_DECLARE_CONVERTER.
*
* The return type of the method is determined as follows:
* - primitive types are returned directly
* - non-primitive types without a registered type conversion are returned as @c QJniObject.
* - non-primitive types with a registered type conversion are returned in a wrapper class that can
* be implicitly converted either to the destination type of the conversion, or a @c QJniObject.
* 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 QJniObject representing the JNI array.
*
* Thie macro can only be placed in classes having the @c JNI_OBJECT macro.
*
* @param RetT The return type. Must either be a primitive 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.
* @param Args A list or argument types (can be empty). Must either be primitive types or types declared
* with @c JNI_TYPE.
*/
#define JNI_METHOD(RetT, Name, ...) \
inline auto Name(JNI_PARAMS(__VA_ARGS__)) const \
{ \
using namespace KAndroidExtras; \
return Internal::caller<RetT>::call(jniHandle(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>() __VA_OPT__(, ) JNI_ARGS(__VA_ARGS__)); \
}
/**
* Wrap a JNI static method call.
* This will add a static method named @p Name to the current class. Argument types are checked at compile time,
* with the following inputs being accepted:
* - primitive types have to match exactly
* - non-primitive types can be either passed as @c QJniObject instance or with a type that has an
* conversion registered with @c JNI_DECLARE_CONVERTER.
*
* The return type of the method is determined as follows:
* - primitive types are returned directly
* - non-primitive types without a registered type conversion are returned as @c QJniObject.
* - non-primitive types with a registered type conversion are returned in a wrapper class that can
* be implicitly converted either to the destination type of the conversion, or a @c QJniObject.
* 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 QJniObject representing the JNI array.
*
* 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 primitive 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.
* @param Args A list or argument types (can be empty). Must either be primitive types or types declared
* with @c JNI_TYPE.
*/
#define JNI_STATIC_METHOD(RetT, Name, ...) \
static inline auto Name(JNI_PARAMS(__VA_ARGS__)) \
{ \
using namespace KAndroidExtras; \
return Internal::static_caller<RetT>::call(Jni::typeName<_jni_ThisType>(), \
"" #Name, \
Jni::signature<RetT(__VA_ARGS__)>() __VA_OPT__(, ) JNI_ARGS(__VA_ARGS__)); \
}
/**
* Wrap a JNI constructor call.
* This will add a constructor named @p Name to the current class. Argument types are checked at compile time,
* with the following inputs being accepted:
* - primitive types have to match exactly
* - non-primitive types can be either passed as @c QJniObject instance or with a type that has an
* conversion registered with @c JNI_DECLARE_CONVERTER.
*
* Thie macro can only be placed in classes having @c JNI_OBJECT macro.
*
* @param Name The name of the method. Must match the JNI method to be called exactly.
* @param Args A list or argument types (can be empty). Must either be primitive types or types declared
* with @c JNI_TYPE.
*/
#define JNI_CONSTRUCTOR(Name, ...) \
inline Name(JNI_PARAMS(__VA_ARGS__)) \
{ \
using namespace KAndroidExtras; \
setJniHandle(QJniObject(Jni::typeName<_jni_ThisType>(), (const char *)Jni::signature<void(__VA_ARGS__)>() __VA_OPT__(, ) JNI_ARGS(__VA_ARGS__))); \
}
}
#endif

View File

@@ -1,188 +0,0 @@
/*
SPDX-FileCopyrightText: 2019-2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIOBJECT_H
#define KANDROIDEXTRAS_JNIOBJECT_H
#include "jnitypetraits.h"
namespace KAndroidExtras
{
namespace java
{
namespace lang
{
struct String;
}
}
namespace Internal
{
struct FromHandleTag {
};
}
namespace Jni
{
template<typename T>
class Object;
template<typename T>
inline QJniObject handle(const T &wrapper)
{
return wrapper.jniHandle();
}
/** Convert an untyped JNI object handle to a typed wrapper.
* This is essentially the JNI equivalent to a reinterpret_cast from a void*.
* Only use this if you are sure @p handle has the correct type, otherwise
* things will horribly go wrong.
*/
template<typename T>
inline auto fromHandle(const QJniObject &handle)
{
if constexpr (Jni::is_object_wrapper<T>::value) {
return T(handle, Internal::FromHandleTag());
} else {
return Jni::Object<T>(handle);
}
}
template<typename T>
inline auto fromHandle(jobject handle)
{
return fromHandle<T>(QJniObject(handle));
}
/** Wrapper for JNI objects with a convertible C++ type.
* This provides implicit on-demand conversion to the C++ type, for types
* that don't have a manually defined wrapper.
*/
template<typename T>
class Object
{
private:
template<typename DummyT>
class _dummy_t
{
};
public:
inline explicit Object(const QJniObject &v)
: m_handle(v)
{
}
// implicit conversion from a compatible C++ type
inline Object(const std::conditional_t<std::is_same_v<typename Jni::converter<T>::type, void>, _dummy_t<T>, typename Jni::converter<T>::type> &v)
: m_handle(Jni::reverse_converter<T>::type::convert(v))
{
}
// implicit conversion from a custom wrapper for the same type
template<typename WrapperT, typename = std::enable_if_t<std::is_same_v<typename WrapperT::_jni_BaseType, T>, WrapperT>>
inline Object(const WrapperT &w)
: m_handle(Jni::handle(w))
{
}
// special-case QString, as that often comes out of implicitly converted types,
// and thus can't be consumed by another implicit conversion step
template<typename StrT = QString, typename = std::enable_if_t<std::is_same_v<T, java::lang::String>, StrT>>
inline Object(const QString &s)
: m_handle(QJniObject::fromString(s))
{
}
// special case for null values
inline Object(std::nullptr_t)
: m_handle((jobject) nullptr)
{
}
inline operator QJniObject() const
{
return m_handle;
}
inline operator typename Jni::converter<T>::type() const
{
return Jni::converter<T>::convert(m_handle);
}
inline QJniObject jniHandle() const
{
return m_handle;
}
// forward basic QJniObject API
inline bool isValid() const
{
return m_handle.isValid();
}
template<typename StrT = QString, typename = std::enable_if_t<std::is_same_v<T, java::lang::String>, StrT>>
inline QString toString() const
{
return m_handle.toString();
}
private:
QJniObject m_handle;
};
/** Annotates a class for holding JNI method or property wrappers.
*
* 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 QJniObject
* 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_UNMANAGED_OBJECT(Class, BaseType) \
public: \
typedef Class _jni_ThisType; \
typedef BaseType _jni_BaseType; \
\
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: \
QJniObject _m_jni_handle; \
inline QJniObject jniHandle() const \
{ \
return _m_jni_handle; \
} \
inline void setJniHandle(const QJniObject &h) \
{ \
_m_jni_handle = h; \
} \
friend QJniObject KAndroidExtras::Jni::handle<Class>(const Class &); \
friend auto KAndroidExtras::Jni::fromHandle<Class>(const QJniObject &); \
explicit inline Class(const QJniObject &handle, KAndroidExtras::Internal::FromHandleTag) \
: _m_jni_handle(handle) \
{ \
}
}
}
#endif

View File

@@ -1,28 +0,0 @@
/*
SPDX-FileCopyrightText: 2019-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIPP_H
#define KANDROIDEXTRAS_JNIPP_H
/** @file jnipp.h
* Preprocessor macro implementation details.
*/
///@cond internal
// determine how many elements are in __VA_ARGS__
#define JNI_PP_NARG(...) JNI_PP_NARG_(__VA_ARGS__ __VA_OPT__(, ) JNI_PP_RSEQ_N())
#define JNI_PP_NARG_(...) JNI_PP_ARG_N(__VA_ARGS__)
#define JNI_PP_ARG_N(_1, _2, _3, _4, _5, _6, _7, N, ...) N
#define JNI_PP_RSEQ_N() 7, 6, 5, 4, 3, 2, 1, 0
// preprocessor-level token concat
#define JNI_PP_CONCAT(arg1, arg2) JNI_PP_CONCAT1(arg1, arg2)
#define JNI_PP_CONCAT1(arg1, arg2) JNI_PP_CONCAT2(arg1, arg2)
#define JNI_PP_CONCAT2(arg1, arg2) arg1##arg2
///@endcond
#endif // KANDROIDEXTRAS_JNIPP_H

View File

@@ -1,125 +0,0 @@
/*
SPDX-FileCopyrightText: 2020-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIPRIMITIVETYPES_H
#define KANDROIDEXTRAS_JNIPRIMITIVETYPES_H
#include "jni.h"
#include <type_traits>
namespace KAndroidExtras
{
namespace Jni
{
/** Type trait to check whether @tparam T is a primitive JNI type or an object type. */
template<typename T>
struct is_primitive_type : std::false_type {
};
template<>
struct is_primitive_type<jboolean> : std::true_type {
};
template<>
struct is_primitive_type<jbyte> : std::true_type {
};
template<>
struct is_primitive_type<jchar> : std::true_type {
};
template<>
struct is_primitive_type<jshort> : std::true_type {
};
template<>
struct is_primitive_type<jint> : std::true_type {
};
template<>
struct is_primitive_type<jlong> : std::true_type {
};
template<>
struct is_primitive_type<jfloat> : std::true_type {
};
template<>
struct is_primitive_type<jdouble> : std::true_type {
};
template<>
struct is_primitive_type<void> : std::true_type {
};
// special case for bool <-> jboolean conversion
template<>
struct is_primitive_type<bool> : std::true_type {
};
}
namespace Internal
{
/** Utility trait to check for basic C++ types that are not JNI primitive types. */
template<typename T>
struct is_invalid_primitive_type : std::false_type {
};
template<>
struct is_invalid_primitive_type<uint8_t> : std::true_type {
};
template<>
struct is_invalid_primitive_type<char> : std::true_type {
};
template<>
struct is_invalid_primitive_type<uint32_t> : std::true_type {
};
template<>
struct is_invalid_primitive_type<uint64_t> : std::true_type {
};
// C++ primitive to JNI primitive conversion
template<typename T>
struct primitive_value {
static_assert(!is_invalid_primitive_type<T>::value, "Using an incompatible primitive type!");
typedef T JniType;
typedef T NativeType;
static constexpr inline T toJni(T value)
{
return value;
}
static constexpr inline T fromJni(T value)
{
return value;
}
};
template<>
struct primitive_value<void> {
};
template<>
struct primitive_value<jboolean> {
typedef jboolean JniType;
typedef bool NativeType;
static constexpr inline jboolean toJni(bool value)
{
return value ? 1 : 0;
}
static constexpr inline bool fromJni(jboolean value)
{
return value != 0;
}
};
template<>
struct primitive_value<bool> {
typedef jboolean JniType;
typedef bool NativeType;
static constexpr inline jboolean toJni(bool value)
{
return value ? 1 : 0;
}
static constexpr inline bool fromJni(jboolean value)
{
return value != 0;
}
};
}
}
#endif

View File

@@ -1,227 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIPROPERTIES_H
#define KANDROIDEXTRAS_JNIPROPERTIES_H
#include "jniargument.h"
#include "jniobject.h"
#include "jnisignature.h"
#include "jnitypes.h"
#include "jnitypetraits.h"
#include <type_traits>
namespace KAndroidExtras
{
namespace Jni
{
template<typename T>
class Array;
}
/** @cond internal */
namespace Internal
{
/** Wrapper for static properties. */
template<typename PropType, typename ClassType, typename NameHolder, bool PrimitiveType>
struct StaticProperty {
};
template<typename PropType, typename ClassType, typename NameHolder>
struct StaticProperty<PropType, ClassType, NameHolder, false> {
static_assert(!is_invalid_primitive_type<PropType>::value, "Using an incompatible primitive type!");
inline QJniObject get() const
{
return QJniObject::getStaticObjectField(Jni::typeName<ClassType>(), Jni::typeName<NameHolder>(), Jni::signature<PropType>());
}
inline operator QJniObject() const
{
return get();
}
inline operator typename Jni::converter<PropType>::type() const
{
return Jni::converter<PropType>::convert(get());
}
template<typename RetT = PropType, typename = std::enable_if_t<Jni::is_generic_wrapper<PropType>::value, RetT>>
inline operator Jni::Object<PropType>() const
{
return Jni::Object<PropType>(get());
}
};
template<typename PropType, typename ClassType, typename NameHolder>
struct StaticProperty<PropType, ClassType, NameHolder, true> {
inline operator auto() const
{
return primitive_value<PropType>::fromJni(
QJniObject::getStaticField<typename primitive_value<PropType>::JniType>(Jni::typeName<ClassType>(), Jni::typeName<NameHolder>()));
}
};
/** Shared code for non-static property wrappers. */
template<typename ClassType, typename OffsetHolder>
class PropertyBase
{
protected:
inline QJniObject handle() const
{
const auto owner = reinterpret_cast<const ClassType *>(reinterpret_cast<const char *>(this) - OffsetHolder::offset());
return owner->jniHandle();
}
};
/** Wrapper for non-static properties. */
template<typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder, bool PrimitiveType>
struct Property {
};
template<typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder>
class Property<PropType, ClassType, NameHolder, OffsetHolder, false> : public PropertyBase<ClassType, OffsetHolder>
{
private:
struct _jni_NoType {
};
static_assert(!is_invalid_primitive_type<PropType>::value, "Using an incompatible primitive type!");
public:
inline QJniObject get() const
{
return this->handle().getObjectField(Jni::typeName<NameHolder>(), Jni::signature<PropType>());
}
inline operator QJniObject() const
{
return get();
}
inline operator typename Jni::converter<PropType>::type() const
{
return Jni::converter<PropType>::convert(get());
}
template<typename RetT = PropType, typename = std::enable_if_t<Jni::is_array<PropType>::value, RetT>>
inline operator PropType() const
{
return PropType(get());
}
template<typename RetT = PropType, typename = std::enable_if_t<Jni::is_generic_wrapper<PropType>::value, RetT>>
inline operator Jni::Object<PropType>() const
{
return Jni::Object<PropType>(get());
}
inline Property &operator=(typename Internal::argument<PropType>::type value)
{
this->handle().setField(Jni::typeName<NameHolder>(), Jni::signature<PropType>(), Internal::argument<PropType>::toCallArgument(value));
return *this;
}
inline Property &operator=(const QJniObject &value)
{
this->handle().setField(Jni::typeName<NameHolder>(), Jni::signature<PropType>(), value.object());
return *this;
}
inline Property &operator=(const typename std::conditional<std::is_same_v<typename Jni::converter<PropType>::type, void>,
_jni_NoType,
typename Jni::converter<PropType>::type>::type &value)
{
this->handle().setField(Jni::typeName<NameHolder>(), Jni::signature<PropType>(), Jni::reverse_converter<PropType>::type::convert(value).object());
return *this;
}
// special case for string comparison, which is often done against different types and thus the implicit conversion operator
// isn't going to be enough
template<typename CmpT, typename = std::enable_if_t<std::is_same_v<PropType, java::lang::String>, CmpT>>
inline bool operator==(const CmpT &other) const
{
return QString(*this) == other;
}
};
template<typename PropType, typename ClassType, typename NameHolder, typename OffsetHolder>
class Property<PropType, ClassType, NameHolder, OffsetHolder, true> : public PropertyBase<ClassType, OffsetHolder>
{
public:
inline operator auto() const
{
return primitive_value<PropType>::fromJni(this->handle().template getField<typename primitive_value<PropType>::JniType>(Jni::typeName<NameHolder>()));
}
inline Property &operator=(PropType value)
{
this->handle().setField(Jni::typeName<NameHolder>(), primitive_value<PropType>::toJni(value));
return *this;
}
};
// TODO KF6: can be replaced by QT_WARNING_DISABLE_INVALID_OFFSETOF
#if defined(Q_CC_CLANG)
#define JNI_WARNING_DISABLE_INVALID_OFFSETOF QT_WARNING_DISABLE_CLANG("-Winvalid-offsetof")
#elif defined(Q_CC_GNU)
#define JNI_WARNING_DISABLE_INVALID_OFFSETOF QT_WARNING_DISABLE_GCC("-Winvalid-offsetof")
#else
#define JNI_WARNING_DISABLE_INVALID_OFFSETOF
#endif
/** @endcond */
}
/**
* Wrap a static final property.
* This will add a public static member named @p name to the current class. This member defines an
* implicit conversion operator which will trigger the corresponding a JNI read operation.
* Can only be placed in classes with a @c JNI_OBJECT.
*
* @note Make sure to access this member with a specific type, assigning to an @c auto variable will
* copy the wrapper type, not read the property value.
*
* @param type The data type of the property.
* @param name The name of the property.
*/
#define JNI_CONSTANT(type, name) \
private: \
struct _jni_##name##__NameHolder { \
static constexpr const char *jniName() \
{ \
return "" #name; \
} \
}; \
\
public: \
static inline const KAndroidExtras::Internal::StaticProperty<type, _jni_ThisType, _jni_##name##__NameHolder, Jni::is_primitive_type<type>::value> name;
/**
* Wrap a member property.
* This will add a public zero-size member named @p name to the current class. This member defines an
* implicit conversion operator which will trigger the corresponding a JNI read operation, as well
* as an overloaded assignment operator for the corresponding write operation.
* Can only be placed in classes with a @c JNI_OBJECT.
*
* @note Make sure to access this member with a specific type, assigning to an @c auto variable will
* copy the wrapper type, not read the property value.
*
* @param type The data type of the property.
* @param name The name of the property.
*/
#define JNI_PROPERTY(type, name) \
private: \
struct _jni_##name##__NameHolder { \
static constexpr const char *jniName() \
{ \
return "" #name; \
} \
}; \
struct _jni_##name##__OffsetHolder { \
static constexpr std::size_t offset() \
{ \
QT_WARNING_PUSH JNI_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(_jni_ThisType, name); \
QT_WARNING_POP \
} \
}; \
friend class KAndroidExtras::Internal::PropertyBase<_jni_ThisType, _jni_##name##__OffsetHolder>; \
\
public: \
[[no_unique_address]] KAndroidExtras::Internal:: \
Property<type, _jni_ThisType, _jni_##name##__NameHolder, _jni_##name##__OffsetHolder, KAndroidExtras::Jni::is_primitive_type<type>::value> name;
}
#endif // KANDROIDEXTRAS_JNIPROPERTIES_H

View File

@@ -1,70 +0,0 @@
/*
SPDX-FileCopyrightText: 2021-2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNIRETURNVALUE_H
#define KANDROIDEXTRAS_JNIRETURNVALUE_H
#include "jniobject.h"
#include "jnitypetraits.h"
namespace KAndroidExtras
{
namespace Jni
{
template<typename T>
class Array;
}
///@cond internal
namespace Internal
{
/** Return value wrapping helper. */
template<typename RetT, typename = std::void_t<>>
class return_wrapper
{
static_assert(!is_invalid_primitive_type<RetT>::value, "Using an incompatible primitive type!");
static inline constexpr bool is_primitive = Jni::is_primitive_type<RetT>::value;
static inline constexpr bool is_convertible = !std::is_same_v<typename Jni::converter<RetT>::type, void>;
typedef std::conditional_t<is_primitive, RetT, QJniObject> JniReturnT;
public:
static inline constexpr auto toReturnValue(JniReturnT value)
{
if constexpr (is_convertible) {
return Jni::Object<RetT>(value);
} else if constexpr (is_primitive) {
return primitive_value<RetT>::fromJni(value);
} else {
return value;
}
}
};
template<typename RetT>
class return_wrapper<RetT, std::void_t<typename RetT::_jni_ThisType>>
{
public:
static inline auto toReturnValue(const QJniObject &value)
{
return Jni::fromHandle<RetT>(value);
}
};
template<typename RetT>
class return_wrapper<Jni::Array<RetT>>
{
public:
static inline auto toReturnValue(const QJniObject &value)
{
return Jni::Array<RetT>(value);
}
};
template<>
struct return_wrapper<void> {
};
}
///@endcond
}
#endif

View File

@@ -1,190 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNISIGNATURE_H
#define KANDROIDEXTRAS_JNISIGNATURE_H
#include "jnitypes.h"
#include "jni.h"
#include <cstdint>
#include <utility>
namespace KAndroidExtras
{
namespace Jni
{
template<typename T>
class Array;
}
/** @cond internal */
namespace Internal
{
/** Compile-time concat-able string. */
template<char... String>
struct StaticString {
inline operator const char *() const
{
static const char data[] = {String..., 0};
return data;
}
};
/** Compile-time strlen. */
constexpr inline int static_strlen(const char *str)
{
return str[0] == '\0' ? 0 : static_strlen(str + 1) + 1;
}
/** Compile-time concat for two StaticString. */
template<char... String1, char... String2>
constexpr inline auto static_concat(const StaticString<String1...> &, const StaticString<String2...> &)
{
return StaticString<String1..., String2...>();
}
/** Compile-time concept for N StaticString. */
template<typename String1, typename String2, class... Strings>
constexpr inline auto static_concat(const String1 &str1, const String2 &str2, const Strings &...strs)
{
return static_concat(static_concat(str1, str2), strs...);
}
/** Conversion from const char* literals to StaticString. */
template<typename, typename>
struct staticStringFromJniType;
template<typename T, std::size_t... Indexes>
struct staticStringFromJniType<T, std::index_sequence<Indexes...>> {
typedef StaticString<Jni::typeName<T>()[Indexes]...> value;
};
/** Meta function for assembling JNI signatures. */
template<typename T>
struct JniSignature {
constexpr inline auto operator()() const
{
using namespace Internal;
return static_concat(StaticString<'L'>(),
typename staticStringFromJniType<T, std::make_index_sequence<static_strlen(Jni::typeName<T>())>>::value(),
StaticString<';'>());
}
};
template<>
struct JniSignature<jboolean> {
constexpr inline auto operator()() const
{
return StaticString<'Z'>();
}
};
template<>
struct JniSignature<jbyte> {
constexpr inline auto operator()() const
{
return StaticString<'B'>();
}
};
template<>
struct JniSignature<jchar> {
constexpr inline auto operator()() const
{
return StaticString<'C'>();
}
};
template<>
struct JniSignature<jshort> {
constexpr inline auto operator()() const
{
return StaticString<'S'>();
}
};
template<>
struct JniSignature<jint> {
constexpr inline auto operator()() const
{
return StaticString<'I'>();
}
};
template<>
struct JniSignature<jlong> {
constexpr inline auto operator()() const
{
return StaticString<'J'>();
}
};
template<>
struct JniSignature<jfloat> {
constexpr inline auto operator()() const
{
return StaticString<'F'>();
}
};
template<>
struct JniSignature<jdouble> {
constexpr inline auto operator()() const
{
return StaticString<'D'>();
}
};
template<>
struct JniSignature<void> {
constexpr inline auto operator()() const
{
return StaticString<'V'>();
}
};
// special case due to jboolean != bool
template<>
struct JniSignature<bool> {
constexpr inline auto operator()() const
{
return StaticString<'Z'>();
}
};
template<typename T>
struct JniSignature<T *> {
constexpr inline auto operator()() const
{
using namespace Internal;
return static_concat(StaticString<'['>(), JniSignature<T>()());
}
};
template<typename T>
struct JniSignature<Jni::Array<T>> {
constexpr inline auto operator()() const
{
using namespace Internal;
return static_concat(StaticString<'['>(), JniSignature<T>()());
}
};
template<typename RetT, typename... Args>
struct JniSignature<RetT(Args...)> {
constexpr inline auto operator()() const
{
using namespace Internal;
return static_concat(StaticString<'('>(), JniSignature<Args>()()..., StaticString<')'>(), JniSignature<RetT>()());
}
};
}
/** @endcond */
/** Helper methods to deal with JNI. */
namespace Jni
{
/** Returns the JNI signature string for the template argument types. */
template<typename... Args>
constexpr __attribute__((__unused__)) Internal::JniSignature<Args...> signature = {};
}
}
#endif // KANDROIDEXTRAS_JNISIGNATURE_H

View File

@@ -1,114 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNITYPES_H
#define KANDROIDEXTRAS_JNITYPES_H
#include "jnipp.h"
/** C++/Android integration utilities built on top of @c QJniObject. */
namespace KAndroidExtras
{
///@cond internal
// preprocessor "iteration" for regular classes
#define JNI_TYPE_1(name, type, ...) \
struct type { \
static constexpr const char *jniName() \
{ \
return name #type; \
} \
};
#define JNI_TYPE_2(name, type, ...) \
namespace type \
{ \
JNI_TYPE_1(name #type "/", __VA_ARGS__) \
}
#define JNI_TYPE_3(name, type, ...) \
namespace type \
{ \
JNI_TYPE_2(name #type "/", __VA_ARGS__) \
}
#define JNI_TYPE_4(name, type, ...) \
namespace type \
{ \
JNI_TYPE_3(name #type "/", __VA_ARGS__) \
}
#define JNI_TYPE_5(name, type, ...) \
namespace type \
{ \
JNI_TYPE_4(name #type "/", __VA_ARGS__) \
}
#define JNI_TYPE_6(name, type, ...) \
namespace type \
{ \
JNI_TYPE_5(name #type "/", __VA_ARGS__) \
}
#define JNI_TYPE_7(name, type, ...) \
namespace type \
{ \
JNI_TYPE_6(#type "/", __VA_ARGS__) \
}
#define JNI_TYPE_(N, name, ...) JNI_PP_CONCAT(JNI_TYPE_, N)(name, __VA_ARGS__)
// preprocessor "iteration" for nested classes
#define JNI_NESTED_TYPE_2(name, type, nested_type, ...) \
struct type##_##nested_type { \
static constexpr const char *jniName() \
{ \
return name #type "$" #nested_type; \
} \
};
#define JNI_NESTED_TYPE_3(name, type, ...) \
namespace type \
{ \
JNI_NESTED_TYPE_2(name #type "/", __VA_ARGS__) \
}
#define JNI_NESTED_TYPE_4(name, type, ...) \
namespace type \
{ \
JNI_NESTED_TYPE_3(name #type "/", __VA_ARGS__) \
}
#define JNI_NESTED_TYPE_5(name, type, ...) \
namespace type \
{ \
JNI_NESTED_TYPE_4(name #type "/", __VA_ARGS__) \
}
#define JNI_NESTED_TYPE_6(name, type, ...) \
namespace type \
{ \
JNI_NESTED_TYPE_5(name #type "/", __VA_ARGS__) \
}
#define JNI_NESTED_TYPE_7(name, type, ...) \
namespace type \
{ \
JNI_NESTED_TYPE_6(#type "/", __VA_ARGS__) \
}
#define JNI_NESTED_TYPE_(N, name, ...) JNI_PP_CONCAT(JNI_NESTED_TYPE_, N)(name, __VA_ARGS__)
///@endcond
/** Macro to define Java types with their corresponding JNI signature strings. */
#define JNI_TYPE(...) JNI_TYPE_(JNI_PP_NARG(__VA_ARGS__), "", __VA_ARGS__)
/** Macro to define a nested Java class with its corresponding JNI signature string. */
#define JNI_NESTED_TYPE(...) JNI_NESTED_TYPE_(JNI_PP_NARG(__VA_ARGS__), "", __VA_ARGS__)
/** Functions for interfacing with the Java Native Interface (JNI). */
namespace Jni
{
/** Returns the JNI type name of the given template argument. */
template<typename T>
inline constexpr const char *typeName()
{
return T::jniName();
}
}
}
#endif // KANDROIDEXTRAS_JNITYPES_H

View File

@@ -1,89 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KANDROIDEXTRAS_JNITYPETRAITS_H
#define KANDROIDEXTRAS_JNITYPETRAITS_H
#include "jniprimitivetypes.h"
#include "jnitypes.h"
#include <QJniObject>
namespace KAndroidExtras
{
namespace Jni
{
template<typename T>
class Array;
/** Type conversion trait, see @c JNI_DECLARE_CONVERTER. */
template<typename T>
struct converter {
typedef void type;
};
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 {
};
/** Type trais for checking whether @tparam T is a JNI object wrapper type. */
template<typename T, typename = std::void_t<>>
struct is_object_wrapper : std::false_type {
};
template<typename T>
struct is_object_wrapper<T, std::void_t<typename T::_jni_ThisType>> : std::true_type {
};
/** Type trais for checking whether @tparam T is needs the generic JNI object wrapper (Jni::Object). */
template<typename T>
struct is_generic_wrapper
: std::conditional_t<!is_primitive_type<T>::value && !is_array<T>::value && !is_object_wrapper<T>::value, std::true_type, std::false_type> {
};
}
/**
* Declare a JNI type to be convertible to a native type.
* @param JniType A type declared with @p JNI_TYPE.
* @param NativeType A C++ type @p JniType can be converted to/from.
* @param FromJniFn Code converting a @c QJniObject @c value to @p NativeType.
* @param ToJniCode converting a @p NativeType @c value to a QJniObject.
*/
#define JNI_DECLARE_CONVERTER(JniType, NativeType, FromJniFn, ToJniFn) \
namespace Jni \
{ \
template<> \
struct converter<NativeType> { \
typedef JniType type; \
static inline QJniObject convert(const NativeType &value) \
{ \
return (ToJniFn); \
} \
}; \
template<> \
struct converter<JniType> { \
typedef NativeType type; \
static inline NativeType convert(const QJniObject &value) \
{ \
return (FromJniFn); \
} \
}; \
}
}
#endif

View File

@@ -9,6 +9,7 @@
#include <KLocalizedString>
#include <KNotification>
#include <KNotificationPermission>
#include <KNotificationReplyAction>
#include <QPainter>
@@ -23,7 +24,6 @@
#include "controller.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "permissionmanager.h"
#include "roommanager.h"
#include "texthandler.h"
#include "windowcontroller.h"
@@ -43,7 +43,7 @@ NotificationsManager::NotificationsManager(QObject *parent)
void NotificationsManager::handleNotifications(QPointer<NeoChatConnection> connection)
{
if (!PermissionManager::instance().checkPermission(Permission::PostNotification)) {
if (KNotificationPermission::checkPermission() != Qt::PermissionStatus::Granted) {
return;
}

View File

@@ -1,88 +0,0 @@
/*
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "permissionmanager.h"
#include <QDebug>
#ifdef Q_OS_ANDROID
#include "private/qandroidextras_p.h"
#include <KAndroidExtras/ManifestPermission>
#endif
#ifdef Q_OS_ANDROID
static QString permissionName(Permission::Permission p)
{
using namespace KAndroidExtras;
switch (p) {
case Permission::InvalidPermission:
Q_UNREACHABLE();
case Permission::ReadCalendar:
return ManifestPermission::READ_CALENDAR;
case Permission::WriteCalendar:
return ManifestPermission::WRITE_CALENDAR;
case Permission::PostNotification: // only for SDK >= 33
return ManifestPermission::POST_NOTIFICATIONS;
case Permission::Camera:
return ManifestPermission::CAMERA;
}
}
#endif
PermissionManager &PermissionManager::instance()
{
static PermissionManager _instance;
return _instance;
}
PermissionManager::PermissionManager(QObject *parent)
: QObject(parent)
{
}
bool PermissionManager::checkPermission(Permission::Permission permission)
{
if (permission == Permission::InvalidPermission) {
qWarning() << "check for invalid permission - check your QML code!";
return false;
}
#ifdef Q_OS_ANDROID
if (permission == Permission::PostNotification && QtAndroidPrivate::androidSdkVersion() < 33) {
return true;
}
return QtAndroidPrivate::checkPermission(permissionName(permission)).result() == QtAndroidPrivate::PermissionResult::Authorized;
#else // non-Android
Q_UNUSED(permission);
return true;
#endif
}
void PermissionManager::requestPermission(Permission::Permission permission, QJSValue callback)
{
if (permission == Permission::InvalidPermission) {
qWarning() << "request for invalid permission - check your QML code!";
return;
}
qDebug() << permission;
// already got the permission
if (PermissionManager::checkPermission(permission)) {
callback.call();
return;
}
#ifdef Q_OS_ANDROID
// TODO make this properly async
if (QtAndroidPrivate::checkPermission(permissionName(permission)).result() != QtAndroidPrivate::PermissionResult::Authorized) {
if (QtAndroidPrivate::requestPermission(permissionName(permission)).result() != QtAndroidPrivate::PermissionResult::Authorized) {
return;
}
}
callback.call();
#endif
}
#include "moc_permissionmanager.cpp"

View File

@@ -1,44 +0,0 @@
// SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include <QJSValue>
#include <QObject>
#include <QQmlEngine>
/** Permission enum for use in QML. */
namespace Permission
{
Q_NAMESPACE
enum Permission {
InvalidPermission, // captures QML errors resulting in "0" enum values
ReadCalendar,
WriteCalendar,
PostNotification,
Camera,
};
Q_ENUM_NS(Permission)
}
/** Check and request platform permissions for access to controlled resources (calendar, location, etc). */
class PermissionManager : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
static PermissionManager &instance();
static PermissionManager *create(QQmlEngine *engine, QJSEngine *)
{
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
return &instance();
}
Q_INVOKABLE static bool checkPermission(Permission::Permission permission);
Q_INVOKABLE static void requestPermission(Permission::Permission permission, QJSValue callback);
private:
explicit PermissionManager(QObject *parent = nullptr);
};