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

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.

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)

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.

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

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.

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.

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.

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

If you're using core APIs, to create a trigger for a specific timer, you must first register your timer by creating a timer registration using the navigator_invoke_timer_registration_t function. This timer registration request contains the data for the specified timer trigger object. The data includes the ID, action, and the target app attributes.

To learn more about functions that you can use to create a specific timer trigger, see navigator_invoke class.

Last modified: 2014-12-04



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

comments powered by Disqus