Triggers

The BlackBerry 10 OS provides the ability to run headless apps on demand. This is done 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 exits 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 navigator_invoke(). 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.

Trigger for a push message

Here are the attributes you use to create an invocation request that triggers a headless app when a push message is received. Only headless apps that are registered with the Push Service can receive this message. For more information about the Push Service, see Push Service.

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

Trigger for entering a geofenced location

Here are the attributes you use to create an invocation request when a BlackBerry device enters a geofenced location.

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

Trigger for leaving a geofenced location

Here are the attributes you use to create an invocation request when a BlackBerry device leaves a geofenced location.

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 portion of your app. In this sample we choose to register the UI portion of the headless app with the geomonitor service. Here's how the .pro file of the UI portion 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 portion of the app to add relevant headers, 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>

//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();
    //register's 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 portion 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(){

	//Calling 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();
	//Setting the geomonitor region with radius 100 meter. 
    //Therefore if you go outside the radius of 100 meter, 
    //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 );

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

To enable instant preview for your app, configure the onInvoked() slot in the headless portion 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 settings
    bb::platform::NotificationDefaultApplicationSettings settings;
    settings.setPreview
    (bb::platform::NotificationPriorityPolicy::Allow);
    settings.apply();

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


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

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

    //Displaying the Instant Preview
    _notification.notify();

}

Trigger for a port-directed SMS message

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 use 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. To start off, 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 should look like:

#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() { }
//registers 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 launches the registerWithPortDirectedSms() function. The registerResultReceived() signal from the SmsTransport API is connected to the onRegisterResultReceived slot as shown below:

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

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 and edit the onInvoked() slot as shown below:

#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);
	//Connecting the invoked signal with slot onInvoked
	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 settings
    bb::platform::NotificationDefaultApplicationSettings settings;
    settings.setPreview
    (bb::platform::NotificationPriorityPolicy::Allow);
    settings.apply();

    //Setting 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();

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

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

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

Trigger for when the device starts

This trigger is launched 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 will be displayed when the app is installed.

Trigger for when the app is updated

This trigger is launched 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 will be displayed when the app is installed.

Last modified: 2013-12-24

comments powered by Disqus