Triggers

The BlackBerry 10 OS allows headless apps to run on demand by using the invocation framework to trigger your apps. A trigger is delivered as an invocation request and results in the execution of the headless process. A trigger can start a headless app when content is received through the Push Service, an SMS message is received at a specified port, the device enters or leaves a geofenced location, the device starts or restarts, or a certain time is reached.

A headless app can be triggered using the invocation framework. You use the InvokeManager class or the navigator_invoke_timer_registration_send() function to register an invocation trigger with the invocation framework. A headless app cannot invoke itself.

To qualify as a headless app, your app must specify itself as a target with the type application.headless. A headless app cannot specify itself as a card invoke-target-type. Also, the entry point can be referenced only once as an invocation target. The entry point cannot be assigned to another invocation target.

In the sections below, the label at the top of each section indicates the version of the BlackBerry 10 OS that the trigger was introduced in. For example, the trigger for receiving a push message requires BlackBerry 10 OS version 10.2.1 or later.

Push message is received

10.2.1

This trigger starts your headless app when a push message is received. Only headless apps that are registered with the Push Service can receive a push message. For more information about the Push Service, see Push Service. Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type specified by application.headless that is registered with the Push Service

Action

bb.action.PUSH

URI

data://local

MIME type

application/vnd.push

Data

Push content

To learn more about how a headless app receives and processes push messages, including developing a push solution that delivers content using the BlackBerry Enterprise Service 10, see Developing a push-enabled app in C++ and the PushCollector sample app.

Email is received

10.3.0

This trigger starts your headless app when an email message is received. Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type specified by application.headless

Action

bb.action.email.SENT, bb.action.email.RECEIVED

URI

data://local

MIME type

application/vnd.blackberry.MESSAGE_IDS

Data

The data is sent in JSON format and contains the account ID and message IDs that the app uses to get the message. You must also make sure that the data payload has base64 encoding.

For example, "{"account_id": 1392396912, "trigger_data": [48, 49]}" where trigger_data represents the list of message IDs.

For more information on how data is transferred in an invocation request, see Data transfer.

The app receives the data sent in the format mentioned above as follows:

{"source_gid":200,
"source_dname":"sys.service.internal.200",
"action":"bb.action.email.SENT",
"target":"com.example.ESHeadlessAppService",
"perimeter":"personal",
"type":"application/vnd.blackberry.MESSAGE_IDS",
"data":"eyJhY2NvdW50X2lkIjogMTM5MjM5NjkxMiwgInRyaWdnZXJfZGF0YSI6IFs0Ml19"}

The triggers are batched so that when multiple email messages are received together for a single account, only a single trigger is invoked. The trigger for receiving email is only active after the account is synced for the first time and is then fired only for new or unread email messages.

If your app implements some form of personal information management (PIM) service, it can handle duplicate notifications by listening for PIM notifications and trigger notifications for email at the same time.

Device enters a geofenced location

10.2.1

This trigger starts your headless app when a BlackBerry device enters a geofenced location. Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type that is specified by application.headless and is registered with the Geofencing API. The user must specify the geomonitored area and the radius.

Action

bb.action.GEOREGIONENTER

MIME type

text/plain

Data

Name of the location

Device leaves a geofenced location

10.2.1

This trigger starts your headless app when a BlackBerry device leaves a geofenced location. Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type that is specified by application.headless and is registered with the Geofencing API. The user must specify the geomonitored area and the radius.

Action

bb.action.GEOREGIONEXITS

MIME type

text/plain

Data

Name of the location

The following sample shows how you can register your headless app with the geomonitor service. You can register your app from either the UI or the headless part of your app. In this sample, we choose to register the UI part of the headless app with the geomonitor service. Here's how the .pro file of the UI part of the app should look:

APP_NAME = HeadlessUI

CONFIG += qt warn_on cascades10

LIBS += -lgeomonitor -lQtLocationSubset

include(config.pri)

Not applicable

Configure the .hpp file of the UI part of the app to add relevant header files, retrieve current location, add a slot, and add a function to register the headless app:

#include <QObject>
#include <bb/cascades/Application>
#include <bb/cascades/LocaleHandler>
#include <bb/cascades/Button>
#include <bb/cascades/Page>

// Include location Libraries
#include <QtLocationSubset/QGeoCoordinate>
#include <QtLocationSubset/QGeoPositionInfo>
#include <QtLocationSubset/QGeoPositionInfoSource>

#include <geomonitor.h>

QTMS_USE_NAMESPACE

class QTranslator;

class ApplicationUI : public QObject
{
    Q_OBJECT

public:
    ApplicationUI(bb::cascades::Application *app);
    virtual ~ApplicationUI() { }

    // Obtains the location data source 
    // and starts listening for position
    // changes
    void startGPS();

    // Registers the headless app with geomonitor service     
    void registerWithGeofencingTrigger();

private slots:
    void onSystemLanguageChanged();

    // When the button is clicked 
    // from QML, this function is called	
    void onButtonClicked();

    // Called when the current position is updated     
    void onPositionUpdated(const QGeoPositionInfo& geoPositionInfo);

private:
    QTranslator* m_pTranslator;
    bb::cascades::LocaleHandler* m_pLocaleHandler;
    bb::cascades::Page *pPage;
    bb::cascades::Button *_pButton;

    QGeoPositionInfoSource* _plocationDataSource;
    QGeoPositionInfo _myPositionInfo;
};

Here's how these functions are added in the .cpp file of the UI part of the headless app:

void ApplicationUI::startGPS()
{
    // Obtain the location data source 
    // if it is not obtained already
    _plocationDataSource =
    QGeoPositionInfoSource::createDefaultSource(this);

    // Whenever the location data source signals that the current
    // position is updated, the positionUpdated() function is called
    bool connection= QObject::connect(_plocationDataSource, 
        SIGNAL(positionUpdated(const QGeoPositionInfo&)),
        this, SLOT(onPositionUpdated(const QGeoPositionInfo&)));

    if (!connection){
        qWarning() << "Connection failed for positionUpdated";
    } else {
        _plocationDataSource->requestUpdate(120000);
    }
}

void ApplicationUI::onPositionUpdated
    (const QGeoPositionInfo& geoPositionInfo)
{
    if (geoPositionInfo.isValid()) {
        // We've got the position. No need to continue the listening
        _plocationDataSource->stopUpdates();

        // Save the position information into a member variable
        _myPositionInfo = geoPositionInfo;

        // Get the current location as latitude and longitude
        QGeoCoordinate geoCoordinate = geoPositionInfo.coordinate();
        qreal latitude = geoCoordinate.latitude();
        qreal longitude = geoCoordinate.longitude();
        qDebug() << QString
            ("Latitude: %1 Longitude: %2").arg(latitude).arg(longitude);
    } else {
        qWarning()<<"geoPositionInfo isn't valid";
    }
}

void ApplicationUI::registerWithGeofencingTrigger()
{
    // Call the function to get the current location
    startGPS();

    // Create a new region, give it a name and set its location
    geomonitor_region_t region = NULL;
    geomonitor_create_region(&region, "Home");

    QGeoCoordinate geoCoordinate = _myPositionInfo.coordinate();

    // Set the geomonitor region with radius 100 meters.
    // If you go outside the radius of 100 meters, 
    // your headless app will be triggered
    geomonitor_region_set_circle_shape(region,geoCoordinate.latitude(),
        geoCoordinate.longitude(), 100.0);

    qDebug()<<"Latitude"+ QString::number(geoCoordinate.latitude());
    qDebug()<<"Longitude"+QString::number(geoCoordinate.longitude());

    geomonitor_region_set_monitoring_mode
    (region, GEOMONITOR_MONITORING_MODE_PERSISTENT );

    geomonitor_region_set_notification_invoke_target
    (region, "com.example.HEADLESSPORTION",
        GEOMONITOR_NOTIFICATION_DIRECT );

    // Add the region
    int errorCode =  geomonitor_add(region);

    if (errorCode!= 0){
        qWarning() << "Error occurred with code "
            << QString::number(errorCode)<<endl;
    }

    geomonitor_destroy_region(&region);
}

void ApplicationUI::onButtonClicked() {
    registerWithGeofencingTrigger();
}

Not applicable

You must grant your app permission to retrieve the location. In the bar-descriptor.xml file of the UI part of your headless app, navigate to the Application tab and select Location in the Permissions area.

Not applicable

To enable instant preview for your app, configure the onInvoked() slot in the headless part of the sample as shown below:

void HeadlessApplication::onInvoked
    (const bb::system::InvokeRequest& request)
{
    // Clear any existing notifications
    Notification::clearEffectsForAll();
    Notification::deleteAllFromInbox();

    // Turn on instant previews in the Settings app
    bb::platform::NotificationDefaultApplicationSettings settings;
    settings.setPreview
        (bb::platform::NotificationPriorityPolicy::Allow);
    settings.apply();

    // Set the InvokeRequest for the UI
    InvokeRequest invokeReqNotification;
    invokeReqNotification.setTarget("com.example.UI");

    // Set the content of the body
    QString body;
    body = "action:- 
    "+request.action()+" \n "+"mime: "+ request.mimeType()+"\n"
    				+" Region :- "+ QString(request.data());

    // Set the body and the InvokeRequest of the notification
    _notification.setBody(QString::fromUtf8(body.toAscii()));
    _notification.setInvokeRequest(invokeReqNotification);

    // Display the instant preview
    _notification.notify();
}

Not applicable

Port-directed SMS message is received

10.2.1

A port-directed SMS message is different from a regular SMS message because it is directed to a specific port and another app needs to listen to this port to get the SMS message. A port-directed SMS message also doesn't appear in the BlackBerry Hub. To set up a trigger for a port-directed SMS message, your app must use SmsTransport to register with a port. If you want to monitor regular SMS messages, then you should use the MessageService class in conjunction with a long running headless app.

Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request that indicates that an SMS message is received.

Attribute Value

Target or type

Any target or type specified by application.headless and registered with SmsTransport

Action

bb.action.PORT_DIRECTED_SMS

URI

data://local

MIME type

application/vnd.sms

Data

Text data

The following sample shows how you can enable your headless app to be triggered when a port-directed SMS message is received. You can register your headless app with a specific port (for example, 5499) and invoke the headless app if a message is received at this port. First, you must register the headless or the UI part of your app with the Message API. Import the library by adding the following line to your project's .pro file:

LIBS += -lbbpim

Not applicable

Here's an example of the .hpp file of the UI part of your app:

#include <QObject>
#include <bb/cascades/Application>
#include <bb/cascades/LocaleHandler>
#include <bb/cascades/Button>
#include <bb/cascades/Page>

#include <bb/pim/message/SmsTransport>

class QTranslator;

class ApplicationUI : public QObject
{
    Q_OBJECT
public:
    ApplicationUI(bb::cascades::Application *app);
    virtual ~ApplicationUI() { }

    // Register the headless target ID with port 5499
    void registerWithPortDirectedSms();

private slots:
    void onSystemLanguageChanged();

    // Signal is emitted when the target gets registered with the port
    void onRegisterResultReceived 
    (unsigned int port, 
    bb::pim::message::SmsTransportRegistrationResult::Type status);

    // This function is called when the button is clicked using QML
    void onButtonClicked();
private:
    QTranslator* m_pTranslator;
    bb::cascades::LocaleHandler* m_pLocaleHandler;
    bb::pim::message::SmsTransport  _smsTransport;
    bb::cascades::Page *pPage;
    bb::cascades::Button *_pButton;
};

Not applicable

The example above uses a button to register the headless app with the port 5499. When tapped, this button calls the registerWithPortDirectedSms() function. The registerResultReceived() signal from the SmsTransport API is connected to the onRegisterResultReceived slot.

Not applicable

Here's how to connect the registerResultReceived() signal and the onRegisterResultReceived slot:

// Connect with the registerResultReceived() signal to
// check the registration status
if (!connect(&_smsTransport,
    SIGNAL(registerResultReceived(unsigned int,
    bb::pim::message::SmsTransportRegistrationResult::Type)),
    this,
    SLOT(onRegisterResultReceived(unsigned int,
    bb::pim::message::SmsTransportRegistrationResult::Type)))) {

    qWarning() << "Could not connect to directed SMS" << endl;
}

Here's how to register your app to a unique port using the invoke target ID of the headless part of your app:

_smsTransport.registerPort(5499, "com.example.HEADLESSPART");

Here's how you can define the slot:

void ApplicationUI::onRegisterResultReceived(unsigned int port,
        bb::pim::message::SmsTransportRegistrationResult::Type status) {
    qDebug() << "port number----------" << port;
    qDebug() << "Status---------------" << status;
}

Not applicable

You must grant your app permission for text messaging. In the bar-descriptor.xml file of the UI part of your headless app, navigate to the Application tab and select Text Messages in the Permissions area.

Not applicable

You can verify the port-directed SMS message by converting the binary data received from the InvokeRequest::data() function to text. Use the following function from the SmsTransport API:

static const bb::pim::message::Message 
        messageFromData(const QByteArray &data, 
        unsigned int *originatorPort, unsigned int *destinationPort);

Not applicable

You also need to update the .pro file of the headless part of your app:

APP_NAME = HeadlessPart

CONFIG += qt warn_on sql
QT += sql xml
LIBS += -lbbsystem -lbb -lbbplatform -lbbpim 

include(config.pri)

Finally, configure the .cpp file of the headless part of your app to add the SmsTransport header file and edit the onInvoked() slot.

Not applicable

Here's how to add the SmsTransport header file and modify the onInvoked() slot:

#include "HeadlessApplication.hpp"
#include <bb/pim/message/SmsTransport>

using namespace bb::pim::message;

HeadlessApplication::HeadlessApplication(bb::Application *app) :
        QObject(app), _invokeManager(new InvokeManager())
{
    _invokeManager->setParent(this);

    // Connect the invoked() signal to the onInvoked slot
    connect(_invokeManager,
        SIGNAL(invoked(const bb::system::InvokeRequest&)), this,
        SLOT(onInvoked(const bb::system::InvokeRequest&)));
}

void HeadlessApplication::onInvoked
    (const bb::system::InvokeRequest& request)
{
    // Clear any existing notifications
    Notification::clearEffectsForAll();
    Notification::deleteAllFromInbox();

    // Turn on previews in the Settings app
    bb::platform::NotificationDefaultApplicationSettings settings;
    settings.setPreview
    (bb::platform::NotificationPriorityPolicy::Allow);
    settings.apply();

    // Set the InvokeRequest for the UI
    InvokeRequest invokeReqNotification;
    invokeReqNotification.setTarget("com.example.UI");

    unsigned int portNumber = 5499;

    bb::pim::message::Message message=
        bb::pim::message::SmsTransport::messageFromData
        (request.data(), &portNumber , &portNumber );
    QString body;
    QString bodyOfMessage;
    for (int i =0; i< message.attachmentCount();i++){
        bodyOfMessage =  bodyOfMessage + 
        message.attachmentAt(i).data();
    }

    // Set the content of the body
    body = "action:- "+request.action()+" \n 
        "+"mime: "+ request.mimeType()+"\n"
        +" message received :- "+ bodyOfMessage;

    // Set the body and the InvokeRequest of the notification
    _notification.setBody(QString::fromUtf8(body.toAscii()));
    _notification.setInvokeRequest(invokeReqNotification);

    // Display the Instant Preview
    _notification.notify();
}

Not applicable

If you want to register your headless app when the device restarts, use the system.started trigger and insert the registration content in the registerWithPortDirectedSms() function inside the onInvoked() slot.

Device starts

10.2.1

This trigger starts your headless app when your device restarts or the app is installed for the first time. Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type specified by application.headless

Action

bb.action.system.STARTED

URI

data://local

MIME type

application/vnd.blackberry.system.event.STARTED

Data

Start-up data

If you added code for instant preview in the headless part of your app, an instant preview is displayed when the app is installed.

App is updated

10.3.0

This trigger starts your headless app when the app is updated. Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type specified by application.headless

Action

bb.action.system.UPDATED

URI

data://local

MIME type

application/vnd.blackberry.system.event.UPDATED

Data

Start-up data

You also need to update the versionNumber and buildId elements in the bar-descriptor.xml file of your app to reflect the update revision. If you added code for the instant preview in the headless part of your app, an instant preview is displayed when the app is installed.

App is installed

10.3.0

This trigger starts your headless app when the app is installed. Here are the attributes you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type specified by application.headless

Action

bb.action.system.INSTALLED

URI

data://local

MIME type

application/vnd.blackberry.system.event.INSTALLED

Data

None

The above invocation attributes are used in an invocation request, which is matched with any target filter that is of type application.headless.

Timer is used

10.3.0

This trigger starts your app at the time that you specified in a timer request. Here are the attributes that you can use in the bar-descriptor.xml file of the UI part of your app to create an invocation request for this trigger.

Attribute Value

Target or type

Any target or type specified by application.headless

Action

bb.action.system.TIMER_FIRED

URI

data://local

The following is an example of how to use the attributes in an invocation target entry in the bar-descriptor.xml file of the UI part of your app:

<invoke-target id="com.example.HeadlessService">
    <invoke-target-type>application.headless</invoke-target-type>
    <invoke-target-name>Headless Service</invoke-target-name>
    <entry-point-id>HeadlessService</entry-point-id>
    <filter>
        <action>bb.action.system.TIMER_FIRED</action>
        <mime-type>*</mime-type>
        <property var="uris" value="headless:" />
    </filter>
</invoke-target>
        

You can use timer triggers with the Cascades APIs, the BlackBerry Platform Services (BPS) APIs, or a combination of both.

The following code sample demonstrates how to create and register a timer trigger in the UI part of your headless app that invokes the service part of your app at a specific time. The sample sets the timer trigger to fire 3 minutes after the app is launched. You can use this sample code in the UI component of your headless app as a starting point to use a timer trigger in your app.

Not applicable

In the applicationui.hpp header file of the UI project, include the InvokeManager and InvokeReply classes and declare a member variable of each type.

// File: applicationui.hpp                    
                    
#include <QObject>

namespace bb {
    namespace system {
        class InvokeManager;
        class InvokeReply;
    }
}

class ApplicationUI: public QObject
{
    Q_OBJECT

public:
    ApplicationUI();
    virtual ~ApplicationUI() { }

private:
    bb::system::InvokeManager* m_invokeManager;
    bb::system::InvokeReply* m_invokeReply;
};
                

The file applicationui.cpp implements the UI part of the headless app.

This code creates an instance of the InvokeTimerRequest class to build and register a timer trigger in the invocation framework. The sample registers the timer by creating a bound invocation using the registerTimer() function. A timer registration request includes a unique timer ID, a specific time or a recurrence rule specifying when to invoke the target, and the target to invoke.

This sample uses an InvokeDateTime object to specify a specific time for your app to be triggered. You can use an InvokeRecurrenceRule object to specify a recurring schedule of when to trigger your app.

// File: applicationui.cpp                    
                    
#include "applicationui.hpp"

#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>
#include <bb/system/InvokeManager>
#include <bb/system/InvokeTimerRequest>
#include <bb/system/InvokeReply>

#include <QString>

using namespace bb::cascades;
using namespace bb::system;

ApplicationUI::ApplicationUI() :
        QObject(),
        m_invokeManager(new InvokeManager(this)),
        m_invokeReply(new InvokeReply(this))
{
    QDate trigdate;
    QTime trigtime;

    // Set the timer trigger for 3 minutes from now
    InvokeDateTime trigdatetime(trigdate.currentDate(),
                           trigtime.currentTime().addSecs(180),
                           "");

    // Create the timer request
    InvokeTimerRequest timer_request("timertriggerid",
                                trigdatetime,
                                "com.example.HeadlessService");

    // Register the timer request with the invoke manager.
    // The invoke manager will send the invocation to
    // com.example.HeadlessService when the system reaches
    // the date and time specified in the request.
    m_invokeReply = 
          m_invokeManager->registerTimer(timer_request);

    QmlDocument *qml = 
       QmlDocument::create("asset:///main.qml").parent(this);

    qml->setContextProperty("app", this);

    AbstractPane *root = qml->createRootObject<AbstractPane>();

    Application::instance()->setScene(root);
}
                
This sample demonstrates how to use the navigator_invoke_*() functions to create and register a timer request for a specific time.
/* File: main.c (UI component) */

#include <bps/bps.h>
#include <bps/navigator_invoke.h>
#include <stdio.h>
#include <time.h>

#define UNUSED(x) (void)(x)

int main(int argc, char **argv)
{
    UNUSED(argc);
    UNUSED(argv);

    time_t cur_time;
    struct tm *cur_time_tm;
    cur_time = time(NULL);
    cur_time_tm = localtime(&cur_time);

    /* Get date and time values */
    int hour = cur_time_tm->tm_hour;
    int minute = cur_time_tm->tm_min;
    int year = cur_time_tm->tm_year + 1900;
    int month = cur_time_tm->tm_mon + 1;
    int day = cur_time_tm->tm_mday;

    /* Set timer to 3 minutes from current time */
    minute += 3;
    if (minute > 59)
    {
        minute = (minute % 60);
        hour +=1;
    }

    navigator_invoke_timer_registration_t *reg = NULL;
    navigator_invoke_specific_time_t      *start_date = NULL;
    navigator_invoke_timer_registration_create(&reg,
              NAVIGATOR_INVOKE_TIMER_TRIGGER_SPECIFIC_LOCAL);

    navigator_invoke_specific_time_create(&start_date);

    /* Set the date and time to fire the trigger */
    navigator_invoke_specific_time_set_year(start_date,year);
    navigator_invoke_specific_time_set_month(start_date,month);
    navigator_invoke_specific_time_set_day(start_date,day);
    navigator_invoke_specific_time_set_hour(start_date,hour);
    navigator_invoke_specific_time_set_minute(start_date,
                                              minute);

    /* Set the ID, action and target of the timer */
    navigator_invoke_timer_registration_set_id(reg,"123");
    navigator_invoke_timer_registration_set_action(reg,
              NAVIGATOR_INVOKE_TIMER_ACTION_REGISTER);
    navigator_invoke_timer_registration_set_target(reg,
              "com.example.HeadlessService");

    /* Register the timer */
    navigator_invoke_timer_registration_send(reg);

    /* Clean up */
    navigator_invoke_specific_time_destroy(start_date);
    navigator_invoke_timer_registration_destroy(reg);

    return 0;
}

If you want to use a recurrence rule instead of a specific time, you can use the following code sample:

Not applicable

Replace the InvokeDateTime code in applicationui.cpp with the following InvokeRecurrenceRule code to set up and register a recurrence rule timer.

InvokeRecurrenceRule recurrenceRule(
      bb::system::InvokeRecurrenceRuleFrequency::Minutely);
recurrenceRule.setInterval(10);
QString timerId = "1234-unique-id";

InvokeTimerRequest timer_request;

if (recurrenceRule.isValid()) {
    timer_request.set(timerId,  recurrenceRule, 
                      "com.example.HeadlessService");
    m_invokeReply = 
            m_invokeManager->registerTimer(timer_request);
} else {
    // Handle invalid recurrence rule
}

// You must explicitly deregister a recurrence rule,
// otherwise it will stay active forever

m_invokeReply = m_invokeManager->deregisterTimer(timerId);

// The deregisterTimer() call is asynchronous. Connect the
// finished() signal to the finishedFired() slot and perform
// some action when the timer is deregistered.

if (m_invokeReply != NULL) {
    connect(m_invokeReply, SIGNAL(finished()), 
            this, SLOT(finishedFired()));
}
                

Use the following variables and function calls to create a recurrence rule timer:

navigator_invoke_timer_registration_t *reg = NULL;
navigator_invoke_specific_time_t      *start_date = NULL;

navigator_invoke_recurrence_rule_t    *rule = NULL;
navigator_invoke_timer_registration_create(&reg,
          NAVIGATOR_INVOKE_TIMER_TRIGGER_RECURRENT);
navigator_invoke_recurrence_rule_create(&rule,
          NAVIGATOR_INVOKE_RECURRENCE_RULE_FREQUENCY_MINUTELY);

/* Date and time recurrence rule starts */
navigator_invoke_specific_time_create(&start_date);
navigator_invoke_specific_time_set_year(start_date, year);
navigator_invoke_specific_time_set_month(start_date, month);
navigator_invoke_specific_time_set_day(start_date, day);
navigator_invoke_specific_time_set_hour(start_date, hour);
navigator_invoke_specific_time_set_minute(start_date, minute);

/* Set timer values */
navigator_invoke_timer_registration_set_id(reg, "a427");
navigator_invoke_timer_registration_set_action(reg,
          NAVIGATOR_INVOKE_TIMER_ACTION_REGISTER);
navigator_invoke_timer_registration_set_target(reg,
"com.example.HeadlessService");
navigator_invoke_recurrence_rule_set_start_date(rule,
                                                start_date);
navigator_invoke_timer_registration_set_recurrence_rule(reg,
                                                        rule);

/* Register the timer with the invocation manager */
navigator_invoke_timer_registration_send(reg);

/* Timer cleanup calls */
navigator_invoke_specific_time_destroy(start_date);
navigator_invoke_recurrence_rule_destroy(rule);
navigator_invoke_timer_registration_destroy(reg);
                

Here's an example of how to implement the service part of your app.

Not applicable

// File: service.hpp
#include <QObject>
    
namespace bb {
    class Application;
    namespace platform {
        class Notification;
    }
    namespace system {
        class InvokeManager;
        class InvokeRequest;
    }
}

class Service: public QObject
{
    Q_OBJECT
public:
    Service();
    virtual ~Service() {}
private slots:
    // Slot to execute when the service is invoked
    void handleInvoke(const bb::system::InvokeRequest &);

private:
    // Variable used to build a notification
    bb::platform::Notification * m_notify;
    
    // Invocation system manager
    bb::system::InvokeManager * m_invokeManager;
};
// File: service.cpp

#include "service.hpp"

#include <bb/Application>
#include <bb/platform/Notification>
#include <bb/platform/NotificationDefaultApplicationSettings>
#include <bb/system/InvokeManager>

using namespace bb::platform;
using namespace bb::system;

Service::Service() :
         QObject(),
         m_notify(new Notification(this)),
         m_invokeManager(new InvokeManager(this))
{
    // Whenever the app is invoked, call handleInvoke()
    m_invokeManager->connect(m_invokeManager,
        SIGNAL(invoked(const bb::system::InvokeRequest&)),
        this,
        SLOT(handleInvoke(const bb::system::InvokeRequest&)));

    // Configure app to allow notifications
    NotificationDefaultApplicationSettings settings;
    settings.setPreview(NotificationPriorityPolicy::Allow);
    settings.apply();

    // Set a common notification title
    m_notify->setTitle("Headless service");
}

void Service::handleInvoke(
                    const bb::system::InvokeRequest & request)
{
  // Check if timer trigger invoked the app
  if (request.action().compare("bb.action.system.TIMER_FIRED") 
                               == 0) {
      m_notify->setBody("Timer alert!");
      Notification::clearEffectsForAll();
      Notification::deleteAllFromInbox();
      m_notify->notify();
  }
}
                

Here's an example of how to use BPS functions to implement the service part of your app:

/* File: main.c (service component) */                    

#include <bps/bps.h>
#include <bps/navigator.h>
#include <bps/navigator_invoke.h>
#include <bps/led.h>
#include <bps/sensor.h>

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define UNUSED(x) (void)(x)

int main(int argc, char **argv)
{
  UNUSED(argc);
  UNUSED(argv);

  int rc = 0;

  rc = bps_initialize();
  if (rc != BPS_SUCCESS) {
    return EXIT_FAILURE;
  }

  /* Request navigator events */
  navigator_request_events(0);

  bps_event_t *event;

  bool running = true;

  /* Start the event loop. Look for invoke events coming in
     from the UI app, and navigator events like exit. */
  while (running) {
    rc = bps_get_event(&event, -1);

    if (BPS_SUCCESS == rc && NULL != event)
    {
      int domain = bps_event_get_domain(event);
      int code = bps_event_get_code(event);

      if (domain == navigator_get_domain())
      {
        /* Service received a navigator event */

        switch (code) {
        case NAVIGATOR_EXIT:
          /* Terminate app */
          running = false;
          break;
        case NAVIGATOR_INVOKE_TARGET:
        {
          /* Service received an invoke target event */

          const navigator_invoke_invocation_t *invoke;
          invoke = 
               navigator_invoke_event_get_invocation(event);

          if (invoke)
          {
            const char* action = 
               navigator_invoke_invocation_get_action(invoke);

            /* Test if a timer fired */
            if (strcmp(action, 
                       "bb.action.system.TIMER_FIRED") == 0) {

              /* Timer fired! Now perform the tasks we set 
               * the timer for. */
            }
          }
          break;
        }
        default:
          break;
        }
      }
    }
  }

  bps_shutdown();

  return EXIT_SUCCESS;
}
                

Last modified: 2015-07-24



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus