Документация
ОС Аврора 5.1.1

Пример кода клиентского приложения

Пример приложения-клиента демонстрирует, как использовать 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

В следующем фрагменте кода представлен класс, отвечающий за взаимодействие между 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 &registrationId);

public slots:
    void showGui();

private:
    QString getApplicationId() const;
    void setApplicationId(const QString &applicationId);
    void setRegistrationId(const QString &registrationId);

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 &registrationId) {
        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 &registrationId)
{
    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

Мы используем cookies для персонализации сайта и его более удобного использования. Вы можете запретить cookies в настройках браузера.

Пожалуйста ознакомьтесь с политикой использования cookies.