example 'meshtastic': add background mode for android (very limited for now)

This commit is contained in:
pls.153 2024-02-27 19:36:17 +01:00
parent 4a9434d7ea
commit c2890cd6c4
13 changed files with 127 additions and 237 deletions

View file

@ -3,9 +3,11 @@ class QtAndroidService {
SLOT(void setDeviceFilter(const QString&)); SLOT(void setDeviceFilter(const QString&));
SLOT(void read()); SLOT(void read());
SLOT(void write(const QByteArray&)); SLOT(void write(const QByteArray&));
SLOT(void setBackgroundMode(bool));
SIGNAL(deviceDiscovered(const QString&)); SIGNAL(deviceDiscovered(const QString&));
SIGNAL(bleError()); SIGNAL(bleError());
SIGNAL(setReady(bool, const QString&, const QStringList&)); SIGNAL(setReady(bool, const QString&, const QStringList&));
SIGNAL(receivedFromRadio(const QByteArray&, const QString&)); SIGNAL(receivedFromRadio(const QByteArray&, const QString&));
SIGNAL(receivingDone()); SIGNAL(receivingDone());
SIGNAL(sendSavedPackets(const QVariant&));
} }

View file

@ -10,4 +10,5 @@ public slots:
void setDeviceFilter(const QString& a1) override { ble->setDeviceFilter(a1); } void setDeviceFilter(const QString& a1) override { ble->setDeviceFilter(a1); }
void read() override { ble->read(); } void read() override { ble->read(); }
void write(const QByteArray& a1) override { ble->write(a1); } void write(const QByteArray& a1) override { ble->write(a1); }
void setBackgroundMode(bool a1) override { ble->setBackgroundMode(a1); }
}; };

View file

@ -1,216 +0,0 @@
#ifndef REP_QTANDROIDSERVICE_SOURCE_H
#define REP_QTANDROIDSERVICE_SOURCE_H
// This is an autogenerated file.
// Do not edit this file, any changes made will be lost the next time it is generated.
#include <QtCore/qobject.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmetatype.h>
#include <QtRemoteObjects/qremoteobjectnode.h>
#include <QtRemoteObjects/qremoteobjectsource.h>
class QtAndroidServiceSource : public QObject
{
Q_OBJECT
Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "QtAndroidService")
Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_SIGNATURE, "5239a8ec9c5c20228d0b3b90f08230011762e23c")
public:
explicit QtAndroidServiceSource(QObject *parent = nullptr) : QObject(parent)
{
}
public:
~QtAndroidServiceSource() override = default;
Q_SIGNALS:
void deviceDiscovered(const QString & __repc_variable_1);
void bleError();
void setReady(bool __repc_variable_1, const QString & __repc_variable_2, const QStringList & __repc_variable_3);
void receivedFromRadio(const QByteArray & __repc_variable_1, const QString & __repc_variable_2);
void receivingDone();
public Q_SLOTS:
virtual void startDeviceDiscovery(const QString & __repc_variable_1) = 0;
virtual void setDeviceFilter(const QString & __repc_variable_1) = 0;
virtual void read() = 0;
virtual void write(const QByteArray & __repc_variable_1) = 0;
private:
friend class QT_PREPEND_NAMESPACE(QRemoteObjectNode);
};
class QtAndroidServiceSimpleSource : public QtAndroidServiceSource
{
Q_OBJECT
public:
explicit QtAndroidServiceSimpleSource(QObject *parent = nullptr) : QtAndroidServiceSource(parent)
{
}
public:
~QtAndroidServiceSimpleSource() override = default;
private:
};
template <class ObjectType>
struct QtAndroidServiceSourceAPI : public SourceApiMap
{
QtAndroidServiceSourceAPI(ObjectType *object, const QString &name = QLatin1String("QtAndroidService"))
: SourceApiMap(), m_name(name)
{
Q_UNUSED(object);
m_enums[0] = 0;
m_properties[0] = 0;
m_signals[0] = 5;
m_signals[1] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::deviceDiscovered, static_cast<void (QObject::*)(QString)>(0),m_signalArgCount+0,&m_signalArgTypes[0]);
m_signals[2] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::bleError, static_cast<void (QObject::*)()>(0),m_signalArgCount+1,&m_signalArgTypes[1]);
m_signals[3] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::setReady, static_cast<void (QObject::*)(bool,QString,QStringList)>(0),m_signalArgCount+2,&m_signalArgTypes[2]);
m_signals[4] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::receivedFromRadio, static_cast<void (QObject::*)(QByteArray,QString)>(0),m_signalArgCount+3,&m_signalArgTypes[3]);
m_signals[5] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::receivingDone, static_cast<void (QObject::*)()>(0),m_signalArgCount+4,&m_signalArgTypes[4]);
m_methods[0] = 4;
m_methods[1] = QtPrivate::qtro_method_index<ObjectType>(&ObjectType::startDeviceDiscovery, static_cast<void (QObject::*)(QString)>(0),"startDeviceDiscovery(QString)",m_methodArgCount+0,&m_methodArgTypes[0]);
m_methods[2] = QtPrivate::qtro_method_index<ObjectType>(&ObjectType::setDeviceFilter, static_cast<void (QObject::*)(QString)>(0),"setDeviceFilter(QString)",m_methodArgCount+1,&m_methodArgTypes[1]);
m_methods[3] = QtPrivate::qtro_method_index<ObjectType>(&ObjectType::read, static_cast<void (QObject::*)()>(0),"read()",m_methodArgCount+2,&m_methodArgTypes[2]);
m_methods[4] = QtPrivate::qtro_method_index<ObjectType>(&ObjectType::write, static_cast<void (QObject::*)(QByteArray)>(0),"write(QByteArray)",m_methodArgCount+3,&m_methodArgTypes[3]);
}
QString name() const override { return m_name; }
QString typeName() const override { return QStringLiteral("QtAndroidService"); }
int enumCount() const override { return m_enums[0]; }
int propertyCount() const override { return m_properties[0]; }
int signalCount() const override { return m_signals[0]; }
int methodCount() const override { return m_methods[0]; }
int sourceEnumIndex(int index) const override
{
if (index < 0 || index >= m_enums[0])
return -1;
return m_enums[index+1];
}
int sourcePropertyIndex(int index) const override
{
if (index < 0 || index >= m_properties[0])
return -1;
return m_properties[index+1];
}
int sourceSignalIndex(int index) const override
{
if (index < 0 || index >= m_signals[0])
return -1;
return m_signals[index+1];
}
int sourceMethodIndex(int index) const override
{
if (index < 0 || index >= m_methods[0])
return -1;
return m_methods[index+1];
}
int signalParameterCount(int index) const override
{
if (index < 0 || index >= m_signals[0])
return -1;
return m_signalArgCount[index];
}
int signalParameterType(int sigIndex, int paramIndex) const override
{
if (sigIndex < 0 || sigIndex >= m_signals[0] || paramIndex < 0 || paramIndex >= m_signalArgCount[sigIndex])
return -1;
return m_signalArgTypes[sigIndex][paramIndex];
}
int methodParameterCount(int index) const override
{
if (index < 0 || index >= m_methods[0])
return -1;
return m_methodArgCount[index];
}
int methodParameterType(int methodIndex, int paramIndex) const override
{
if (methodIndex < 0 || methodIndex >= m_methods[0] || paramIndex < 0 || paramIndex >= m_methodArgCount[methodIndex])
return -1;
return m_methodArgTypes[methodIndex][paramIndex];
}
int propertyIndexFromSignal(int index) const override
{
Q_UNUSED(index);
return -1;
}
int propertyRawIndexFromSignal(int index) const override
{
Q_UNUSED(index);
return -1;
}
const QByteArray signalSignature(int index) const override
{
switch (index) {
case 0: return QByteArrayLiteral("deviceDiscovered(QString)");
case 1: return QByteArrayLiteral("bleError()");
case 2: return QByteArrayLiteral("setReady(bool,QString,QStringList)");
case 3: return QByteArrayLiteral("receivedFromRadio(QByteArray,QString)");
case 4: return QByteArrayLiteral("receivingDone()");
}
return QByteArrayLiteral("");
}
QList<QByteArray> signalParameterNames(int index) const override
{
if (index < 0 || index >= m_signals[0])
return QList<QByteArray>();
return ObjectType::staticMetaObject.method(m_signals[index + 1]).parameterNames();
}
const QByteArray methodSignature(int index) const override
{
switch (index) {
case 0: return QByteArrayLiteral("startDeviceDiscovery(QString)");
case 1: return QByteArrayLiteral("setDeviceFilter(QString)");
case 2: return QByteArrayLiteral("read()");
case 3: return QByteArrayLiteral("write(QByteArray)");
}
return QByteArrayLiteral("");
}
QMetaMethod::MethodType methodType(int) const override
{
return QMetaMethod::Slot;
}
QList<QByteArray> methodParameterNames(int index) const override
{
if (index < 0 || index >= m_methods[0])
return QList<QByteArray>();
return ObjectType::staticMetaObject.method(m_methods[index + 1]).parameterNames();
}
const QByteArray typeName(int index) const override
{
switch (index) {
case 0: return QByteArrayLiteral("void");
case 1: return QByteArrayLiteral("void");
case 2: return QByteArrayLiteral("void");
case 3: return QByteArrayLiteral("void");
}
return QByteArrayLiteral("");
}
QByteArray objectSignature() const override { return QByteArray{"5239a8ec9c5c20228d0b3b90f08230011762e23c"}; }
int m_enums[1];
int m_properties[1];
int m_signals[6];
int m_methods[5];
const QString m_name;
int m_signalArgCount[5];
const int* m_signalArgTypes[5];
int m_methodArgCount[4];
const int* m_methodArgTypes[4];
};
QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
#endif // REP_QTANDROIDSERVICE_SOURCE_H

View file

@ -5,7 +5,6 @@
#include <QBluetoothServiceDiscoveryAgent> #include <QBluetoothServiceDiscoveryAgent>
#include <QList> #include <QList>
#include <QMetaEnum> #include <QMetaEnum>
#include <QTimer>
#include <QDebug> #include <QDebug>
BLE::BLE(const QBluetoothUuid& uuid) : mainServiceUuid(uuid) { BLE::BLE(const QBluetoothUuid& uuid) : mainServiceUuid(uuid) {
@ -50,11 +49,11 @@ void BLE::deviceScanFinished() {
if (devices.isEmpty()) { if (devices.isEmpty()) {
qDebug() << "no BLE devices found"; qDebug() << "no BLE devices found";
discoveryAgent->setLowEnergyDiscoveryTimeout(3000); discoveryAgent->setLowEnergyDiscoveryTimeout(3000);
QTimer::singleShot(0, this, [&]() { startDeviceDiscovery(); }); startDeviceDiscovery();
} else { } else {
qDebug() << "device scan done"; qDebug() << "device scan done";
discoveryAgent->setLowEnergyDiscoveryTimeout(1000); // reset to default discoveryAgent->setLowEnergyDiscoveryTimeout(1000); // reset to default
QTimer::singleShot(0, this, &BLE::scanServices); scanServices();
} }
} }
@ -152,7 +151,7 @@ void BLE::deviceConnected() {
void BLE::retryScan() { void BLE::retryScan() {
if (connected && !scanned) { if (connected && !scanned) {
QTimer::singleShot(0, this, &BLE::scanServices); scanServices();
} }
} }
@ -184,4 +183,3 @@ void BLE::deviceScanError(QBluetoothDeviceDiscoveryAgent::Error error) {
qDebug() << "error: " + QLatin1String(qme.valueToKey(error)); qDebug() << "error: " + QLatin1String(qme.valueToKey(error));
} }
} }

View file

@ -1,6 +1,8 @@
#include "ble_meshtastic.h" #include "ble_meshtastic.h"
#include <QMetaEnum> #include <QMetaEnum>
#include <QTimer> #include <QStandardPaths>
#include <QFile>
#include <QDataStream>
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
#include "../android_service/qtandroidservice_ro.h" #include "../android_service/qtandroidservice_ro.h"
@ -96,30 +98,42 @@ void BLE_ME::searchCharacteristics() {
for (auto device : qAsConst(devices)) { for (auto device : qAsConst(devices)) {
names << device.name().right(4); names << device.name().right(4);
} }
emitter->setReady(true, currentDevice.name().right(4), names); if (!backgroundMode) {
emitter->setReady(true, currentDevice.name().right(4), names);
}
} }
} }
void BLE_ME::characteristicChanged(const QLowEnergyCharacteristic&, void BLE_ME::characteristicChanged(const QLowEnergyCharacteristic&,
const QByteArray& data) { const QByteArray& data) {
if (!data.isEmpty()) { if (!data.isEmpty()) {
emitter->receivedFromRadio(data, "notified"); if (backgroundMode) {
read();
} else {
emitter->receivedFromRadio(data, "notified");
}
} }
} }
void BLE_ME::characteristicRead(const QLowEnergyCharacteristic&, void BLE_ME::characteristicRead(const QLowEnergyCharacteristic&,
const QByteArray& data) { const QByteArray& data) {
if (data.isEmpty()) { if (data.isEmpty()) {
emitter->receivingDone(); if (!backgroundMode) {
emitter->receivingDone();
}
} else { } else {
emitter->receivedFromRadio(data, QString()); if (backgroundMode) {
QTimer::singleShot(0, this, &BLE_ME::read); saveBytes(data);
} else {
emitter->receivedFromRadio(data, QString());
}
read();
} }
} }
void BLE_ME::characteristicWritten(const QLowEnergyCharacteristic&, void BLE_ME::characteristicWritten(const QLowEnergyCharacteristic&,
const QByteArray&) { const QByteArray&) {
QTimer::singleShot(0, this, &BLE_ME::read); read();
} }
void BLE_ME::serviceError(QLowEnergyService::ServiceError error) { void BLE_ME::serviceError(QLowEnergyService::ServiceError error) {
@ -155,7 +169,51 @@ void BLE_ME::disconnecting() {
// disable notifications // disable notifications
mainService->writeDescriptor(notifications, QByteArray::fromHex("0000")); mainService->writeDescriptor(notifications, QByteArray::fromHex("0000"));
} }
emitter->setReady(false, QString(), QStringList()); if (!backgroundMode) {
emitter->setReady(false, QString(), QStringList());
}
delete mainService; mainService = nullptr; delete mainService; mainService = nullptr;
} }
// background mode
void BLE_ME::setBackgroundMode(bool background) {
#if (defined Q_OS_ANDROID) || (defined Q_OS_IOS)
backgroundMode = background;
qDebug() << "background mode:" << backgroundMode;
if (!backgroundMode) {
sendSavedBytes();
}
#endif
}
static QString packetsFile() {
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/packets.bin";
}
void BLE_ME::saveBytes(const QByteArray& packet) {
QFile file(packetsFile());
if (file.open(QIODevice::WriteOnly | QIODevice::Append)) {
QDataStream ds(&file);
ds << packet;
file.close();
}
}
void BLE_ME::sendSavedBytes() {
QVariantList packets;
QFile file(packetsFile());
if (file.open(QIODevice::ReadOnly)) {
QDataStream ds(&file);
while (!ds.atEnd()) {
QByteArray packet;
ds >> packet;
packets.append(packet);
}
file.close();
if (!packets.isEmpty()) {
emitter->sendSavedPackets(QVariant(packets));
QFile::remove(packetsFile());
}
}
}

View file

@ -25,11 +25,13 @@ public Q_SLOTS:
void setDeviceFilter(const QString& s) { filter = s; } void setDeviceFilter(const QString& s) { filter = s; }
void read(); void read();
void write(const QByteArray&); void write(const QByteArray&);
void setBackgroundMode(bool);
Q_SIGNALS: Q_SIGNALS:
void setReady(bool, const QString&, const QStringList&); void setReady(bool, const QString&, const QStringList&);
void receivedFromRadio(const QByteArray&, const QString&); void receivedFromRadio(const QByteArray&, const QString&);
void receivingDone(); void receivingDone();
void sendSavedPackets(const QVariant&);
/*** </INTERFACE> ***************************************/ /*** </INTERFACE> ***************************************/
@ -48,6 +50,7 @@ public:
#else #else
BLE_ME* emitter = nullptr; BLE_ME* emitter = nullptr;
#endif #endif
bool backgroundMode = false;
QString filter = "meshtastic"; QString filter = "meshtastic";
QLowEnergyDescriptor notifications; QLowEnergyDescriptor notifications;
@ -63,4 +66,8 @@ private Q_SLOTS:
void characteristicWritten(const QLowEnergyCharacteristic&, const QByteArray&); void characteristicWritten(const QLowEnergyCharacteristic&, const QByteArray&);
void serviceError(QLowEnergyService::ServiceError); void serviceError(QLowEnergyService::ServiceError);
void disconnecting(); void disconnecting();
private:
void saveBytes(const QByteArray&);
void sendSavedBytes();
}; };

View file

@ -3,6 +3,7 @@
#include <QSqlError> #include <QSqlError>
#include <QNetworkInterface> #include <QNetworkInterface>
#include <QHostAddress> #include <QHostAddress>
#include <QGuiApplication>
#include <QtDebug> #include <QtDebug>
#ifdef PLUGIN #ifdef PLUGIN
@ -105,6 +106,27 @@ QT::QT() : QObject() {
[]() { []() {
ecl_fun("lora:receiving-done"); ecl_fun("lora:receiving-done");
}); });
#ifdef Q_OS_ANDROID
QObject::connect(ble.data(), &QtAndroidServiceReplica::sendSavedPackets,
#else
QObject::connect(ble, &BLE_ME::sendSavedPackets,
#endif
[](const QVariant& packets) {
ecl_fun("lora:process-saved-packets", packets);
});
#if (defined Q_OS_ANDROID) || (defined Q_OS_IOS)
// background mode
QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
[&](Qt::ApplicationState state) {
if (state == Qt::ApplicationInactive) {
ble->setBackgroundMode(true);
} else if (state == Qt::ApplicationActive) {
ble->setBackgroundMode(false);
}
});
#endif
} }
// BLE // BLE
@ -129,6 +151,11 @@ QVariant QT::write2(const QVariant& bytes) {
return QVariant(); return QVariant();
} }
QVariant QT::setBackgroundMode(const QVariant& vBackground) {
// for testing
ble->setBackgroundMode(vBackground.toBool());
}
// GPS // GPS
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID

View file

@ -28,6 +28,7 @@ public:
Q_INVOKABLE QVariant setDeviceFilter(const QVariant&); Q_INVOKABLE QVariant setDeviceFilter(const QVariant&);
Q_INVOKABLE QVariant read2(); Q_INVOKABLE QVariant read2();
Q_INVOKABLE QVariant write2(const QVariant&); Q_INVOKABLE QVariant write2(const QVariant&);
Q_INVOKABLE QVariant setBackgroundMode(const QVariant&);
// GPS // GPS
Q_INVOKABLE QVariant iniPositioning(); Q_INVOKABLE QVariant iniPositioning();

View file

@ -169,6 +169,13 @@
(process-received) (process-received)
(values)) (values))
(defun process-saved-packets (packets) ; see Qt
"Called when app changes from background to foreground (mobile only)."
(dolist (packet packets)
(received-from-radio packet))
(receiving-done)
(values))
(defun node-to-name (num) (defun node-to-name (num)
(if (= +broadcast-id+ num) (if (= +broadcast-id+ num)
*broadcast-name* *broadcast-name*

View file

@ -1,8 +1,7 @@
(in-package :msg) (in-package :msg)
(defvar *states* '(:not-received :sending :received)) (defvar *states* '(:not-received :sending :received))
(defvar *message-id* 0) (defvar *message-id* 0)
(defvar *message-ids* nil "associate (temporary) QML :mid to (unique) DB 'mid'")
(defun ini () (defun ini ()
(q> |fontSize| ui:*message-view* (q> |fontSize| ui:*message-view*
@ -11,8 +10,12 @@
(defun new-message-id () (defun new-message-id ()
(mod (incf *message-id*) #.(expt 2 32))) (mod (incf *message-id*) #.(expt 2 32)))
(defun db-mid (mid) (let (message-ids)
(cdr (assoc mid *message-ids*))) ;; associate (temporary) QML :mid to (unique) DB 'mid'
(defun add-message-id (ids)
(push ids message-ids))
(defun db-mid (mid)
(cdr (assoc mid message-ids))))
(defun show-message-p (message) (defun show-message-p (message)
(let ((user (app:setting :latest-receiver))) (let ((user (app:setting :latest-receiver)))
@ -31,8 +34,8 @@
(unless loading (unless loading
(let ((db-mid (db:save-message (parse-integer x:it :radix 16) ; uid (let ((db-mid (db:save-message (parse-integer x:it :radix 16) ; uid
(prin1-to-string message)))) (prin1-to-string message))))
(push (cons (getf message :mid) db-mid) (add-message-id (cons (getf message :mid)
*message-ids*))) db-mid))))
(if (or loading (show-message-p message)) (if (or loading (show-message-p message))
(qjs |addMessage| ui:*messages* message) (qjs |addMessage| ui:*messages* message)
(let* ((sender (getf message :sender)) (let* ((sender (getf message :sender))

View file

@ -57,6 +57,7 @@
#:keywords #:keywords
#:my-name #:my-name
#:my-num #:my-num
#:process-saved-packets
#:send-position #:send-position
#:start-device-discovery #:start-device-discovery
#:read-radio #:read-radio

View file

@ -12,6 +12,7 @@
#:local-ip #:local-ip
#:start-device-discovery #:start-device-discovery
#:read* #:read*
#:set-background-mode ; for testing
#:set-device-filter #:set-device-filter
#:sql-query #:sql-query
#:write*)) #:write*))

View file

@ -79,7 +79,7 @@
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/> <meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
</activity> </activity>
<service android:process=":qt_service" android:name=".QtAndroidService" android:exported="true"> <service android:process=":qt_service" android:name=".QtAndroidService" android:exported="false">
<meta-data android:name="android.app.lib_name" android:value="service"/> <meta-data android:name="android.app.lib_name" android:value="service"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>