Пример кода клиентского приложения
Пример приложения-клиента демонстрирует, как использовать API push-демона, получать,обрабатывать и показывать push-уведомления.
Приложение может получать push-уведомления и в обычном, и в фоновом режиме.
Перед отправкой уведомлений необходимо запустить приложение хотя бы один раз.
При первом запуске оно зарегистрируется в push-демоне
и получит от него registrationId
для push-сервера.
Далее с помощью этого идентификатора приложению можно будет отправлять push-уведомления.
При получении уведомления от push-сервера сначала происходит проверка, запущено ли сейчас приложение-клиент. Если оно не запущено, то производится его запуск в фоновом режиме (без графического интерфейса). Далее через API push-демона производится получение всех доставленных на устройство уведомлений и для каждого из них создаются графические представления через API уведомлений. Если приложение запущенно в фоне и в течение некоторого времени для него не приходило никаких уведомлений, то оно завершает свою работу. Графическое представление уведомлений может как всплывать, так и помещаться в список уведомлений. По нажатию на любое из них будет либо произведён запуск приложения с графическим интерфейсом, если оно не было запущено, либо для текущего процесса отобразится графический интерфейс.
Само приложение Push Receiver представляет собой одну страницу с двумя секциями.
Верхняя часть — это список полученных данным экземпляром приложения push-уведомлений
и заголовок, в котором выводится количество отображаемых уведомлений.
В нижней части выводятся applicationId
и registrationId
.
В приложении также есть верхнее выезжающее меню, в котором пять пунктов:
- Очистить модель;
- Скопировать Application ID;
- Скопировать Registration ID;
- Скопировать оба ID;
- Вывести в журнал.
С помощью верхнего меню, например, можно скопировать registrationId
для сервера.
Архитектура примера клиентского приложения:
ru.omp.PushReceiver.pro
— главный конфигурационный файл приложения;ru.omp.PushReceiver.cpp
— стартовая точка приложения;ApplicationController
— класс, реализующий клиент и управляющий событиями в приложении;ControlService
— класс, реализующий сервис для взаимодействия с push-демоном через D-Bus;NotificationsModel
— вспомогательный класс для отображения данных из push-уведомления;- qml-код для графического интерфейса (в данной статье не приводится, его можно изучить самостоятельно).
Содержание:
- Объявление клиента: класс
ApplicationController
- Идентификатор приложения
- Регистрационный идентификатор
- Уведомления
- Сервис для управления приложением
- Дополнительные настройки конфигурации
Объявление клиента: класс ApplicationController
В следующем фрагменте кода представлен класс, отвечающий за взаимодействие между API push-демона и интерфейсом приложения, графическим и консольным.
#pragma once
#include <QObject>
#include <push_client.h>
class QQuickView;
class NotificationsModel;
class ApplicationController : public QObject
{
Q_OBJECT
Q_PROPERTY(NotificationsModel *notificationsModel READ notificationsModel CONSTANT)
Q_PROPERTY(QString applicationId READ applicationId NOTIFY applicationIdChanged)
Q_PROPERTY(QString registrationId READ registrationId NOTIFY registrationIdChanged)
public:
explicit ApplicationController(QObject *parent = nullptr);
ApplicationController(const QStringList &arguments, QObject *parent = nullptr);
NotificationsModel *notificationsModel() const;
QString applicationId() const;
QString registrationId() const;
signals:
void applicationIdChanged(const QString &applicationId);
void registrationIdChanged(const QString ®istrationId);
public slots:
void showGui();
private:
QString getApplicationId() const;
void setApplicationId(const QString &applicationId);
void setRegistrationId(const QString ®istrationId);
private:
Aurora::PushNotifications::Client *m_notificationsClient { nullptr };
NotificationsModel *m_notificationsModel { nullptr };
QQuickView *m_view { nullptr };
QString m_registrationId { };
};
Подключение библиотеки push_client.h
позволяет использовать API Aurora::PushNotifications
.
В конструкторе происходит регистрация NotificationsModel
, установка идентификатора приложения
и подключение сигналов Aurora::PushNotifications::Client
к слотам ApplicationController
:
ApplicationController::ApplicationController(const QStringList &arguments, QObject *parent)
: QObject(parent), m_notificationsClient(new Client(this)), m_notificationsModel(new NotificationsModel(this))
{
qRegisterMetaType<NotificationsModel *>("NotificationsModel *");
setApplicationId(getApplicationId());
if (arguments.indexOf(QStringLiteral("/no-gui")) == -1)
showGui();
connect(m_notificationsClient, &Client::clientInactive, [this]() {
if (m_view == nullptr)
qApp->quit();
});
connect(m_notificationsClient, &Client::pushSystemReadinessChanged, [](bool status) {
qWarning() << "Push system is" << (status ? "available" : "not available");
});
connect(m_notificationsClient, &Client::registrationId, [this](const QString ®istrationId) {
setRegistrationId(registrationId);
});
connect(m_notificationsClient, &Client::registrationError, []() {
qWarning() << "Push system have problems with registrationId";
});
connect(m_notificationsClient, &Client::notifications, [this](const PushList &pushList) {
...
});
}
Идентификатор приложения
Для успешной работы примера приложения нужно указать ему идентификатор applicationID
.
Сделать это можно двумя способами: либо в pro-файле проекта присвоить переменной APP_ID
,
либо после установки приложения на устройство нужно записать applicationID
,
полученный от push-сервера, в файл applicationid,
расположенный в каталоге /usr/share/ru.omp.PushReceiver/.
В pro-файле примера имеется код для прямого указания идентификатора и для хранения идентификатора в отдельном файле:
APP_ID =
APP_ID_PATH = /usr/share/$${TARGET}
APP_ID_FILE = applicationid
applicationid_install.extra = echo $${APP_ID} > $${OUT_PWD}/$${APP_ID_FILE}
applicationid_install.files = $${OUT_PWD}/$${APP_ID_FILE}
applicationid_install.path = $${APP_ID_PATH}
applicationid_install.CONFIG = no_check_exist
QMAKE_CLEAN += \
$${applicationid_install.files}
QMAKE_EXTRA_TARGETS += \
applicationid_install
INSTALLS += \
applicationid_install
Если указать значение переменной APP_ID
, то при запуске приложения будет автоматически создан
файл /usr/share/ru.omp.PushReceiver/applicationid, содержащий необходимый идентификатор.
В классе ApplicationController
идентификатор из данного файла
считывается в методе getApplicationId()
следующим образом:
QString ApplicationController::getApplicationId() const
{
QFile applicationIdFile(applicationIdFilePath);
if (applicationIdFile.exists() && applicationIdFile.open(QIODevice::ReadOnly)) {
auto data = applicationIdFile.readAll().trimmed();
applicationIdFile.close();
return data;
} else {
return QStringLiteral("applicationId not set");
}
}
В методе setApplicationId()
класса ApplicationController
считывается идентификатор приложения,
затем он передаётся push-демону:
void ApplicationController::setApplicationId(const QString &applicationId)
{
if (m_notificationsClient->applicationId() == applicationId)
return;
m_notificationsClient->setApplicationId(applicationId);
m_notificationsClient->registrate();
emit applicationIdChanged(applicationId);
}
Регистрационный идентификатор
Результат регистрации в push-демоне будет обработан в слоте setRegistrationId()
у ApplicationController
:
void ApplicationController::setRegistrationId(const QString ®istrationId)
{
if (registrationId == m_registrationId)
return;
m_registrationId = registrationId;
emit registrationIdChanged(registrationId);
}
После запуска приложение пытается зарегистрироваться на push-сервере, и в случае успеха оно получит идентификатор регистрации.
registrationId
необходимо отправить автоматизированной системе,
чтобы она могла отправлять push-уведомления приложению-клиенту через сервер push-уведомлений.
Уведомления
Когда приложение получает push-уведомление, оно сначала проверяет,
не находится ли приложение на переднем плане.
Затем оно анализирует поле данных push-уведомления и ищет ключ mtype
в полученном JSON.
Если mtype
равно:
action
— уведомление не будет отображаться;notify
— будет показан только верхний баннер;system_notify
— уведомление будет отображаться только в центре уведомлений.
При любом другом значении, пустом или даже при отсутствии
mtype
будут показаны оба уведомления (баннер и центр уведомлений).
Выбор способа отображения уведомления реализован в слоте pushListToNotificationList
класса ApplicationController
, подключаемом в конструкторе и обрабатывающем
сигнал notifications
класса Aurora::PushNotifications::Client
:
ApplicationController::ApplicationController(const QStringList &arguments, QObject *parent)
: QObject(parent),
m_notificationsClient(new Client(this)),
m_notificationsModel(new NotificationsModel(this))
{
...
connect(m_notificationsClient, &Client::notifications, this,
&ApplicationController::pushListToNotificationList);
}
void ApplicationController::pushListToNotificationList(const PushList &pushList)
{
for (const auto &push : pushList) {
m_notificationsModel->insertPush(push);
QJsonDocument jsonDcoument = QJsonDocument::fromJson(push.data.toUtf8());
QString notifyType = jsonDcoument.object().value("mtype").toString();
if (notifyType == QStringLiteral("action"))
continue;
static QVariant defaultAction = Notification::remoteAction(
QStringLiteral("default"), tr("Open app"), ApplicationService::notifyDBusService(),
ApplicationService::notifyDBusPath(), ApplicationService::notifyDBusIface(),
ApplicationService::notifyDBusMethod());
Notification notification;
notification.setAppName(tr("Push Receiver"));
notification.setSummary(push.title);
notification.setBody(push.message);
notification.setIsTransient(false);
notification.setItemCount(1);
notification.setHintValue("x-nemo-feedback", "sms_exists");
notification.setRemoteAction(defaultAction);
notification.publish();
}
}
Сервис для управления приложением
Для управления приложением, которое может как работать в фоновом режиме,
так и запускать графический интерфейс, создаётся класс ApplicationService
.
Он наследуется от QDBusAbstractAdaptor
:
class ApplicationService : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", DBUS_INTERFACE)
public:
explicit ApplicationService(QObject *parent = nullptr);
~ApplicationService() override;
static bool isRegistered();
static QString notifyDBusService();
static QString notifyDBusPath();
static QString notifyDBusIface();
static QString notifyDBusMethod();
static int updateApplicationArgs(const QStringList &arguments);
signals:
void showGui();
public slots:
void handleApplicationArgs(const QStringList &arguments);
void handleApplicationWakeUp();
};
Название сервиса, путь и интерфейс можно задать как константы в pro-файле:
DEFINES += \
DBUS_SERVICE=\\\"ru.omp.PushReceiver\\\" \
DBUS_PATH=\\\"/ru/omp/PushReceiver\\\" \
DBUS_INTERFACE=\\\"ru.omp.PushReceiver\\\" \
Эти константы экспортируются в С++-код в ApplicationService.cpp
.
Кроме того, в списке констант содержатся названия методов, которые можно вызвать
через API D-Bus.
namespace {
const QString dbusServiceStr = QStringLiteral(DBUS_SERVICE);
const QString dbusPathStr = QStringLiteral(DBUS_PATH);
const QString dbusIfaceStr = QStringLiteral(DBUS_INTERFACE);
const QString dbusMethodAppArgs = QStringLiteral("handleApplicationArgs");
const QString dbusMethodAppWakeUp = QStringLiteral("handleApplicationWakeUp");
}
В конструкторе регистрируется D-Bus-сервис:
ApplicationService::ApplicationService(QObject *parent) : QDBusAbstractAdaptor(parent)
{
setAutoRelaySignals(true);
auto dbus = QDBusConnection::sessionBus();
dbus.registerObject(dbusPathStr, this, QDBusConnection::ExportAllSlots);
if (!isRegistered()) {
bool success = dbus.registerService(dbusServiceStr);
if (!success)
qApp->quit();
}
}
Метод updateApplicationArgs(const QStringList &arguments)
использует этот сервис,
чтобы применить для приложения аргументы командной строки,
вызвав handleArguments(const QStringList &arguments)
через константу dbusMethodAppArgs
:
int ApplicationService::updateApplicationArgs(const QStringList &arguments)
{
auto message = QDBusMessage::createMethodCall(dbusServiceStr, dbusPathStr, dbusIfaceStr, dbusMethodAppArgs);
message.setArguments(QList<QVariant>() << arguments);
auto reply = QDBusConnection::sessionBus().call(message);
return 0;
}
Метод handleArguments(const QStringList &arguments)
отправляет сигнал о том,
что нужно открыть графический интерфейс, если в аргументах командной строки имеется --gui
:
void ApplicationService::handleApplicationArgs(const QStringList &arguments)
{
if (arguments.indexOf(QStringLiteral("/no-gui")) != -1)
return;
emit showGui();
}
Альтернативный метод handleApplicationWakeUp()
отправляет сигнал о том,
что нужно открыть графический интерфейс без дополнительных условий.
void ApplicationService::handleApplicationWakeUp()
{
emit showGui();
}
Сервис, как и клиент ApplicationController
, создаётся и запускается в ru.omp.PushReceiver.cpp
:
int main(int argc, char *argv[])
{
auto application = SailfishApp::application(argc, argv);
application->setOrganizationName(QStringLiteral("ru.omp"));
application->setApplicationName(QStringLiteral("PushReceiver"));
auto applicationArgs = application->arguments();
applicationArgs.removeFirst();
if (ApplicationService::isRegistered()) {
return ApplicationService::updateApplicationArgs(applicationArgs);
} else {
auto applicationService = new ApplicationService(application);
auto applicationController = new ApplicationController(applicationArgs, application);
QObject::connect(applicationService, &ApplicationService::showGui,
applicationController, &ApplicationController::showGui);
return application->exec();
}
}
Сначала проверяется, не был ли сервис уже запущен.
В этом случае у него только обновляются аргументы командной строки.
Иначе создаётся и запускается новый сервис ApplicationService
,
а также клиент ApplicationController
,
который получает аргументы командной строки приложения.
Дополнительные настройки конфигурации
Для приложения аргументы командной строки можно задать в desktop-файле,
в поле Exec
в основном разделе [Desktop Entry]
,
или для запуска через D-Bus в поле ExecDBus
раздела [X-Application]
:
[Desktop Entry]
Type=Application
X-Nemo-Application-Type=silica-qt5
X-Nemo-Single-Instance=no
X-Aurora-Single-Cover=yes
Icon=ru.omp.PushReceiver
Exec=/usr/bin/ru.omp.PushReceiver %u
Name=Push Receiver
Name[ru]=Приемник пушей
[X-Application]
Permissions=PushNotifications
OrganizationName=ru.omp
ApplicationName=PushReceiver
ExecDBus=/usr/bin/ru.omp.PushReceiver %u