Files:
The headlesserviceui example demonstrates how to launch a headless service counterpart and communicate with it through the QSettings instance.
In this sample, we represent visually the status of the service and it's current state values.
In this sample, we represent the running service through a colored rectangle (red for stopped, green for running) and two labels indicating the initial flash count and the remaining flash count. A reset button is provided in addition to allow the user to reset the service in order to restart the LED countdown.
The business logic of the application is encapsulated in the applicationui component which is exposed as "_app" to the UI.
// Service status representation ColoredRectangle { topMargin: 100 id: service title: _app.isServiceRunning() ? "Headless Process Running" : "N/A" color: _app.isServiceRunning() ? Color.Green : Color.Red }
The following custom component "ColoredRectangle" is a Container with a image overlay to create an appealing visual component to represent the service status; it allows you to change it's label and color dependent on status.
Container { horizontalAlignment: HorizontalAlignment.Left verticalAlignment: VerticalAlignment.Center Container { layout: StackLayout { orientation: LayoutOrientation.LeftToRight } Label { text: "FlashCount: " } Label { id: flashCount text: _app.flashCount() textStyle.fontWeight: FontWeight.Bold } } // Container for cummunicating the remaining flash count Container { layout: StackLayout { orientation: LayoutOrientation.LeftToRight } Label { text: "RemainingCount: " } Label { id: remainingFlashCount textStyle.fontWeight: FontWeight.Bold text: _app.remainingFlashCount } } }
The following Labels convey the services flash count and remaining flash count to the user.
Container { horizontalAlignment: HorizontalAlignment.Center verticalAlignment: VerticalAlignment.Bottom Button { text: "Restart" onClicked: { _app.resetLED(); } } }
The reset Button is meant to stop the current LED request and start a new request with original request count.
The buisiness logic of the headlesserviceUI is encapsulated in the applicationui component.
class ApplicationHeadless: public QObject { Q_OBJECT // property holding value for remaining flash count Q_PROPERTY(int remainingFlashCount READ remainingFlashCount WRITE setRemainingFlashCount NOTIFY remainingFlashCountChanged) public: ApplicationHeadless(bb::cascades::Application *app); virtual ~ApplicationHeadless() {} Q_INVOKABLE void resetLED(); Q_SIGNALS: // Emitted when the remaining flash count value has changed void remainingFlashCountChanged(); public Q_SLOTS: /** * Method that can be invoked from within qml * to read QSettings key/value for ServiceStatus, * in order to decipher if service is running (active). */ bool isServiceRunning(); /** * Method hooked into the signal/slots mechanism, which * gets invoked upon receiving fileChanged() signal * from the settingsWatcher instance. */ void settingsChanged(const QString & path); /** * Method to retrieve the number of times the Led * was set to flash by reading this from the QSettings. */ int flashCount(); private: static const QString m_author; // for creating settings static const QString m_appName; // for creating settings static const QString m_flashNumber; static const QString m_remainingCount; static const QString m_reset; static const QString m_serviceStatus; QTranslator* m_pTranslator; bb::cascades::LocaleHandler* m_pLocaleHandler; // QTimer used to periodically read QSettings to retrieve // new remaining flash count value int m_remainingFlashCount; // setter to store the new remaining flash count value void setRemainingFlashCount(int x); // Convenience method to retrieve remaining flash count. int remainingFlashCount(); // Watcher for qsettigns file changes QFileSystemWatcher* settingsWatcher; };
Initialization of static QString const that represent the QSettings keys under which we will be retrieving and conveying the headless service status information to the UI.
const QString ApplicationHeadless::m_author = "Example Inc."; // for creating settings const QString ApplicationHeadless::m_appName = "HeadlesServiceApp"; // for creating settings // keys for setting file const QString ApplicationHeadless::m_serviceStatus = "ServiceStatus"; const QString ApplicationHeadless::m_flashNumber = "FlashCount"; const QString ApplicationHeadless::m_remainingCount = "RemainingFlashCount"; const QString ApplicationHeadless::m_reset = "Reset";
When the isServiceRunning() method is invoked it queries the QString instance, initiated by the headless service, using the "ServiceStatus" key in order to retrieve the service status string. Once it is determined that the service is in the running state the initial flash count and remaining count is queried as well.
bool ApplicationHeadless::isServiceRunning() { qDebug() << "check for service running via qsettings..."; QSettings settings(m_author, m_appName); if (settings.value("ServiceStatus").isNull()) { qDebug() << "found null value for ServiceStatus key..."; } else { QString status = settings.value("ServiceStatus").toString(); if (status == "running") { // update remaining flash count since service is already running settingsChanged(""); return true; } } return false; } int ApplicationHeadless::flashCount() { QSettings settings(m_author, m_appName); if (settings.contains(m_flashNumber)) { return settings.value(m_flashNumber).toInt(); } return 0; }
The resetLED() writes a boolean to the QSettings file in order to communicate to the service to stop the current led request and restart a new request with the same parameters. The setRemainingFlashCount() method emits a remainingFlashCountChanged() signal to the qml to change the label text representing the count, every time the count changes.
void ApplicationHeadless::resetLED() { QSettings settings(m_author, m_appName); settings.setValue(m_reset, true); } void ApplicationHeadless::setRemainingFlashCount(int x) { m_remainingFlashCount = x; qDebug() << "emitting update signal flc"; Q_EMIT remainingFlashCountChanged(); }
The settingsChanged() method is invoked by the QFileSystemWatcher when the QSettings file has been altered by another process, it then proceeds to invoke setRemainingFlashCount() in order to update the remaining flash count as described by the paragraph above this one.
The HeadlesService example demonstrates how to create a long-running headless service and communicate with it's UI counterpart. Make note that this is NOT a standalone, independantly deployable application, it is packaged with headlesserviceui sample and deployed via it's bar file. This project does not contain an asset folder due to the fact that it's not allowed to have a UI element to it.
In this sample, there is no UI portion since a headless service cannot have one. The primary goal of this service is to initiate a led instance and manipulate its flash count/color while communicating the results of this back to the UI.
The buisiness logic of the headless service is encapsulated in the applicationheadless component.
class ApplicationHeadless: public QObject { Q_OBJECT public: ApplicationHeadless(bb::Application *app); virtual ~ApplicationHeadless() { } public Q_SLOTS: /** * Method invoked by the invocation framework * upon bb.action.system.STARTED. Meaning when this * service is install for the first time and upon startup * of the system. */ void onInvoked(const bb::system::InvokeRequest& request); /** * Method hooked into the signal/slots mechanism, which * gets invoked upon receiving remainingFlashCountChanged() signal * from the Led instance. */ void flashCountChanged(int x); /** * Method hooked into the signal/slots mechanism, which * gets invoked upon receiving fileChanged() signal * from the settingsWatcher instance. */ void settingsChanged(const QString & path); /** * Method invoked when a activeChanged() signal is * received from the Led instance, indicating * a led state change. */ void activeUpdate(bool active); /** * Initialization method to create QSettings entries * and prepare Led instance. */ void init(); private: // Invocation Manager instance for receiving system events bb::system::InvokeManager *m_invokeManager; static const QString m_author; // for creating settings static const QString m_appName; // for creating settings // keys used in setting static const QString m_flashNumber; static const QString m_remainingCount; static const QString m_serviceStatus; static const QString m_ledActive; static const QString m_reset; // The Led instance bb::device::Led *m_led; // flash count holder int m_flashCount; // Watcher for qsettigns file changes QFileSystemWatcher* m_settingsWatcher; };
This class represents the basic service of led manipulation by setting to Color X and initiating led with flash count N.
const QString ApplicationHeadless::m_author = "Example Inc."; // for creating settings const QString ApplicationHeadless::m_appName = "HeadlesServiceApp"; // for creating settings // keys for setting file const QString ApplicationHeadless::m_serviceStatus = "ServiceStatus"; const QString ApplicationHeadless::m_flashNumber = "FlashCount"; const QString ApplicationHeadless::m_remainingCount = "RemainingFlashCount"; const QString ApplicationHeadless::m_ledActive = "ActiveLed"; const QString ApplicationHeadless::m_reset = "Reset";
Initialization of static QString const that represent the QSettings keys under which we will be storing and conveying the Led status information to it's UI counterpart.
ApplicationHeadless::ApplicationHeadless(bb::Application *app) : QObject(app) , m_invokeManager(new InvokeManager(this)) , m_led(0) , m_flashCount(20) , m_settingsWatcher(new QFileSystemWatcher(this)) { QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); // log the service PID qDebug() << "PID------------" << QString::number(QCoreApplication::applicationPid()); }
The default constructor that initializes its member variables and logs the service PID. It is important to take a note that the headless service should be using bb::Application for it's parent instead of the usual bb::cascades::Application instance. This is due to the fact that the service is not a cascades application since it cannot have a UI portion to it, it's strictly like any other background service you would code up.
void ApplicationHeadless::init() { m_led = new Led(LedColor::Blue, this); // set the initial qsettings keys/values upon startup QSettings settings(m_author, m_appName); settings.setValue(m_serviceStatus, "running"); settings.setValue(m_flashNumber, m_flashCount); settings.setValue(m_remainingCount, m_flashCount); // Force the creation of the settings file so that we can watch it for changes. settings.sync(); // Watcher for changes in the settings file. m_settingsWatcher->addPath(settings.fileName()); // Do all the necessary signal/slot connections to be invokable to receive led updates. bool ok = connect(m_invokeManager, SIGNAL(invoked(const bb::system::InvokeRequest&)), this, SLOT(onInvoked(const bb::system::InvokeRequest&))); Q_ASSERT (ok); ok = connect(m_led, SIGNAL(remainingFlashCountChanged(int)), this, SLOT(flashCountChanged(int))); Q_ASSERT(ok); ok = connect(m_led, SIGNAL(activeChanged(bool)), this, SLOT(activeUpdate(bool))); Q_ASSERT(ok); ok = connect(m_settingsWatcher, SIGNAL(fileChanged(const QString&)), this, SLOT(settingsChanged(const QString&))); Q_ASSERT(ok); Q_UNUSED(ok); }
This initializer method creates the unique QSettings which will be used as the moderator for sharing information between the service and its UI. It also does all the signal/slot connections to receive invocation requests and Led status updates.
void ApplicationHeadless::onInvoked(const bb::system::InvokeRequest& request) { qDebug() << "##service got invoked: " << request.action(); // start led flashing once the start request is received if (request.action().compare("bb.action.system.STARTED") == 0) { m_led->flash(m_flashCount); } else { // write service running status to qsettings QSettings settings(m_author, m_appName); settings.setValue(m_serviceStatus, request.action()); } }
This method is invoked when a invocation request is received. It listens for the system start event in order to activate the led functionality; this event is generated upon system start and/or when the application is installed initially. This is an example of a system startup triggered service.
void ApplicationHeadless::flashCountChanged(int x) { qDebug() << "---------" + QString::number(x); QSettings settings(m_author, m_appName); settings.setValue(m_remainingCount, x); } void ApplicationHeadless::activeUpdate(bool active) { qDebug() << "---active: " << active; QSettings settings(m_author, m_appName); settings.value(m_ledActive, active); if(!active) { QSettings settings(m_author, m_appName); settings.setValue(m_remainingCount, m_led->remainingFlashCount()); } }
These methods are invoked through Qt's signal/slot mechanism when updates are done to the led active state and flash count instance variables.
void ApplicationHeadless::settingsChanged(const QString & path) { QSettings settings(m_author, m_appName); if (settings.value(m_reset).toBool()) { settings.setValue(m_reset, false); settings.setValue(m_flashNumber, m_flashCount); settings.setValue(m_remainingCount, m_flashCount); settings.value(m_ledActive, false); m_led->cancel(); disconnect(m_led, SIGNAL(remainingFlashCountChanged(int)), this, SLOT(flashCountChanged(int))); disconnect(m_led, SIGNAL(activeChanged(bool)), this, SLOT(activeUpdate(bool))); delete m_led; m_led = new Led(LedColor::Blue, this); bool ok = connect(m_led, SIGNAL(remainingFlashCountChanged(int)), this, SLOT(flashCountChanged(int))); Q_ASSERT(ok); ok = connect(m_led, SIGNAL(activeChanged(bool)), this, SLOT(activeUpdate(bool))); Q_ASSERT(ok); Q_UNUSED(ok); m_led->flash(m_flashCount); } }
The settingsChanged() is invoked when the QFileSystemWatcher senses that the QSettings file has been altered. The main purpose of this method is to reset the QSetting key values, stop the current LED request and initiate a new LED flash request when the requesting UI requests for the reset via the reset Button.