feat(sdk): add Swift and Kotlin mobile client foundations with push token proto

Swift SDK: Swift Package wrapping libquicproquo_ffi with QpqClient class
(connect, login, send, receive, disconnect) for iOS 15+ / macOS 13+.

Kotlin SDK: JNI bridge to libquicproquo_ffi with QpqClient class for
Android (aarch64, armv7) and JVM, Gradle build configuration.

Adds RegisterPushToken RPC (method ID 710) to device.proto for
APNs/FCM/WebPush device push token registration.
This commit is contained in:
2026-03-04 20:58:23 +01:00
parent cbb76af6b1
commit f57dda3f36
14 changed files with 816 additions and 1 deletions

View File

@@ -0,0 +1,117 @@
/**
* JNI bridge between Kotlin/Java and libquicproquo_ffi.
*
* Compile with:
* gcc -shared -fPIC -o libquicproquo_jni.so \
* -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \
* dev_quicproquo_NativeBridge.c \
* -L ../../../../target/release -lquicproquo_ffi
*/
#include <jni.h>
#include <stdint.h>
#include <string.h>
/* Forward declarations for libquicproquo_ffi functions. */
typedef struct QpqHandle QpqHandle;
extern QpqHandle* qpq_connect(const char* server, const char* ca_cert, const char* server_name);
extern int qpq_login(QpqHandle* handle, const char* username, const char* password);
extern int qpq_send(QpqHandle* handle, const char* recipient,
const uint8_t* message, size_t message_len);
extern int qpq_receive(QpqHandle* handle, uint32_t timeout_ms, char** out_json);
extern void qpq_disconnect(QpqHandle* handle);
extern const char* qpq_last_error(const QpqHandle* handle);
extern void qpq_free_string(char* ptr);
/* --- JNI exports --- */
JNIEXPORT jlong JNICALL
Java_dev_quicproquo_NativeBridge_nativeConnect(
JNIEnv* env, jclass cls,
jstring server, jstring caCert, jstring serverName)
{
const char* s = (*env)->GetStringUTFChars(env, server, NULL);
const char* c = (*env)->GetStringUTFChars(env, caCert, NULL);
const char* n = (*env)->GetStringUTFChars(env, serverName, NULL);
QpqHandle* h = qpq_connect(s, c, n);
(*env)->ReleaseStringUTFChars(env, server, s);
(*env)->ReleaseStringUTFChars(env, caCert, c);
(*env)->ReleaseStringUTFChars(env, serverName, n);
return (jlong)(uintptr_t)h;
}
JNIEXPORT jint JNICALL
Java_dev_quicproquo_NativeBridge_nativeLogin(
JNIEnv* env, jclass cls,
jlong handle, jstring username, jstring password)
{
QpqHandle* h = (QpqHandle*)(uintptr_t)handle;
const char* u = (*env)->GetStringUTFChars(env, username, NULL);
const char* p = (*env)->GetStringUTFChars(env, password, NULL);
int code = qpq_login(h, u, p);
(*env)->ReleaseStringUTFChars(env, username, u);
(*env)->ReleaseStringUTFChars(env, password, p);
return code;
}
JNIEXPORT jint JNICALL
Java_dev_quicproquo_NativeBridge_nativeSend(
JNIEnv* env, jclass cls,
jlong handle, jstring recipient, jbyteArray message)
{
QpqHandle* h = (QpqHandle*)(uintptr_t)handle;
const char* r = (*env)->GetStringUTFChars(env, recipient, NULL);
jsize len = (*env)->GetArrayLength(env, message);
jbyte* bytes = (*env)->GetByteArrayElements(env, message, NULL);
int code = qpq_send(h, r, (const uint8_t*)bytes, (size_t)len);
(*env)->ReleaseByteArrayElements(env, message, bytes, JNI_ABORT);
(*env)->ReleaseStringUTFChars(env, recipient, r);
return code;
}
JNIEXPORT jstring JNICALL
Java_dev_quicproquo_NativeBridge_nativeReceive(
JNIEnv* env, jclass cls,
jlong handle, jint timeoutMs)
{
QpqHandle* h = (QpqHandle*)(uintptr_t)handle;
char* json = NULL;
int code = qpq_receive(h, (uint32_t)timeoutMs, &json);
if (code != 0 || json == NULL) {
if (json) qpq_free_string(json);
return NULL;
}
jstring result = (*env)->NewStringUTF(env, json);
qpq_free_string(json);
return result;
}
JNIEXPORT void JNICALL
Java_dev_quicproquo_NativeBridge_nativeDisconnect(
JNIEnv* env, jclass cls, jlong handle)
{
QpqHandle* h = (QpqHandle*)(uintptr_t)handle;
if (h) {
qpq_disconnect(h);
}
}
JNIEXPORT jstring JNICALL
Java_dev_quicproquo_NativeBridge_nativeLastError(
JNIEnv* env, jclass cls, jlong handle)
{
QpqHandle* h = (QpqHandle*)(uintptr_t)handle;
const char* err = qpq_last_error(h);
if (!err) return NULL;
return (*env)->NewStringUTF(env, err);
}