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, or the device starts or restarts.

A headless app can be triggered using the invocation framework. A trigger is an invocation message that specifies the trigger event to the InvokeManager class in Cascades or the navigator_invoke() function in the core API. The InvokeManager class or the navigator starts and manages the headless app by processing the invocation request and starting the headless app entry point. 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();
}

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();
}

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. 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;
};

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;
}

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);

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();
}

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 you specified in the timer request.

If you're using Cascades, there are two types of timers that you can specify: anchored timer and roaming timer. The anchored timer allows you to trigger the timer at a specified time for a given time zone. To use an anchored timer, specify a time zone in the InvokeDateTime class. The roaming timer allows you to trigger the timer at a specified time of the time zone the device is in. To use a roaming timer, you do not need to specify the time zone in the InvokeDateTime class.

In Cascades, the InvokeTimerRequest class handles the trigger for a specific timer in the invocation framework. You must first register your timer by creating a bound invocation using the registerTimer() function. This timer registration request contains the data for the specified timer trigger object. The data includes timerId and the target app value. The timerID is used to uniquely identify each request made by the app. The setTarget parameter specifies the target in your request object.

Last modified: 2014-12-04



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

comments powered by Disqus