Tutorial: Using BPS

This tutorial shows you how to use several platform services in a Cascades app using BPS. We'll create an app that displays the current locale and network connection status on a device. The app uses APIs in the BPS library to retrieve locale and network status events, and the app updates the information accordingly if these values change.

You will learn to:

  • Create a custom handler class for BPS events
  • Request events from different platform services
  • Handle events that are received through BPS and update Cascades controls in response
Screen showing the end result of the tutorial.

Downloading the full source code

This tutorial uses a step-by-step approach to build our BPS app from scratch. If you want to take a look at the complete source code for the finished app, you can download the entire project and import it into the Momentics IDE. To learn how, see Import an existing project.

Download the full source code

Create the UI

To start, open the Momentics IDE and create a new Cascades project using the Standard empty project template. To make it easier to follow along, the code in this tutorial assumes that you give your project a name of BpsTutorial. After the project is created, open the main.qml file, which is where we put the QML code for the UI of our app. Go ahead and remove the pre-populated code that's included in the file; we'll start coding from a blank file.

We create several Label controls, each of which displays a different piece of information that we'll retrieve using BPS functions later on. The Label controls are centered on the screen using a DockLayout. We make sure to give each Label an objectName, which lets us populate the values from C++.

import bb.cascades 1.0

Page {
    Container {
        layout: DockLayout {
        }
        Container {
            horizontalAlignment: HorizontalAlignment.Center
            verticalAlignment: VerticalAlignment.Center
            Label {
                id: countryLabel
                objectName: "countryLabel"
            }
            Label {
                id: languageLabel
                objectName: "languageLabel"
            }
            Label {
                id: localeLabel
                objectName: "localeLabel"
            }
            Label {
                id: networkStatusLabel
                objectName: "networkStatusLabel"
            }
            Label {
                id: networkStatusType
                objectName: "networkStatusType"
            }
        }
    }
}

Define the application class

Next, open the BpsTutorial.cpp file. This file represents our application class and is located in the src folder of our project.

If you don't see a file called BpsTutorial.cpp, look for a file called applicationui.cpp instead. Some versions of the SDK may name the application class ApplicationUI automatically. If you have an ApplicationUI.cpp file, remember to change the tutorial code that's presented below to use ApplicationUI instead of BpsTutorial.

This class sets the appropriate text for each Label, while another custom class called StatusEventHandler takes care of the BPS operations that our app uses. We'll create the StatusEventHandler class a bit later.

In BpsTutorial.cpp (or applicationui.cpp), we can keep the pre-populated code that's provided. In the app constructor, we add code that locates each Label that we created in QML (by using their objectName properties) and assign the objects to C++ variables. We also call a function called initUI(), which sets the initial text of each Label. Here's what the complete constructor looks like:

BpsTutorial::BpsTutorial(bb::cascades::Application *app)
: QObject(app)
{
    QmlDocument *qml =
        QmlDocument::create("asset:///main.qml").parent(this);
    AbstractPane *root = qml->createRootObject<AbstractPane>();

    countryLabel = root->findChild<Label*>("countryLabel");
    languageLabel = root->findChild<Label*>("languageLabel");
    localeLabel = root->findChild<Label*>("localeLabel");
    networkStatusLabel =
        root->findChild<Label*>("networkStatusLabel");
    networkStatusType =
        root->findChild<Label*>("networkStatusType");

    app->setScene(root);

    initUI();
}

Let's define the initUI() function now. In addition to setting the text of each Label, this function creates an instance of StatusEventHandler, which we'll use to interact with the BPS library. The functions in this class emit a couple of custom signals, networkStatusUpdated() and localeUpdated(), when either the network status or locale changes. We connect these signals to slot functions in our app class so that the text of each Label can be updated appropriately.

void BpsTutorial::initUI() {
    countryLabel->setText("Country: ?");
    languageLabel->setText("Language: ?");
    localeLabel->setText("Locale: ?");
    networkStatusLabel->setText("Network Status: ?");

    statusEventHandler = new StatusEventHandler();
    
    // If any Q_ASSERT statement(s) indicate that the slot failed to
    // connect to the signal, make sure you know exactly why this has
    // happened. This is not normal, and will cause your app to
    // stop working!!
    bool connectResult;
    
    // Since the variable is not used in the app, this is added to
    // avoid a compiler warning.
    Q_UNUSED(connectResult);
    
    connectResult = connect(statusEventHandler,
                      SIGNAL(networkStatusUpdated(bool, QString)),
                      this,
                      SLOT(networkStatusUpdateHandler(bool, QString)));
            
    // This is only available in Debug builds.
    Q_ASSERT(connectResult);
            
    connectResult = connect(statusEventHandler,
                      SIGNAL(localeUpdated(QString, QString, QString)),
                      this,
                      SLOT(localeUpdateHandler(
                          QString, QString, QString)));
    
    // This is only available in Debug builds.
    Q_ASSERT(connectResult);
}

To complete our app class definition, we define our two slot functions, networkStatusUpdateHandler() and localeUpdateHandler(). These functions are straightforward: they simply update the Label controls with the new network status and locale information (which is provided by the function parameters).

void BpsTutorial::networkStatusUpdateHandler(bool status,
        QString type) {
    if (status) {
        networkStatusLabel->setText("Network Status: true");
    } else {
        networkStatusLabel->setText("Network Status: false");
    }
    networkStatusType->setText("Network Type: " + type);
}

void BpsTutorial::localeUpdateHandler(QString language,
                                      QString country,
                                      QString locale) {

    countryLabel->setText("Country: " + country);
    languageLabel->setText("Language: " + language);
    localeLabel->setText("Locale: " + locale);
}

Update the application header file

Now, let's update the corresponding header file to reflect the changes we've made to the source file. In the src folder of your project, open the BpsTutorial.hpp file.

Similar to the BpsTutorial.cpp file above, you may have an applicationui.hpp file instead of a BpsTutorial.hpp file.

We need to include a few additional header files, so we add the appropriate include statements at the top of the file (right below #include <QObject>):

#include <QString>
#include <bb/cascades/Label>
#include "StatusEventHandler.h"

In a public slots: section of the header, we declare our two slot functions:

public slots:
    void localeUpdateHandler(QString language,
                             QString country,
                             QString locale);
    void networkStatusUpdateHandler(bool status, QString type);

Finally, we declare our private variables (the Label controls), our StatusEventHandler instance, and the initUI() function:

private:
    bb::cascades::Label *countryLabel;
    bb::cascades::Label *languageLabel;
    bb::cascades::Label *localeLabel;
    bb::cascades::Label *networkStatusLabel;
    bb::cascades::Label *networkStatusType;
    StatusEventHandler  *statusEventHandler;
    void initUI();

Create the event handler class

Most of our app's real work is done in a custom class called StatusEventHandler. Go ahead and create a class with this name in the src folder of your project (right-click the src folder and click New > Class).

Open the StatusEventHandler.cpp file. We start by including the appropriate BPS headers for the platform services that our app uses (namely, network status and locale), right below #include "StatusEventHandler.h".

#include <bps/bps.h>
#include <bps/netstatus.h>
#include <bps/locale.h>

In the class constructor, we call the subscribe() function, which registers our app to receive BPS events from the platform services that we're interested in. These function calls are required because our custom class, StatusEventHandler, inherits from a special class called AbstractBpsEventHandler. You'll learn why we chose to use this class a bit later in the tutorial.

We initialize the BPS library by calling bps_initialize(), which lets us use BPS functions in our app. We want our app to receive events related to network status and locale, so we call the netstatus_request_events() and locale_request_events() functions to request events from these services.

StatusEventHandler::StatusEventHandler() {
    subscribe(netstatus_get_domain());
    subscribe(locale_get_domain());

    bps_initialize();

    netstatus_request_events(0);
    locale_request_events(0);
}

Remember that when our app is finished using the BPS library, we need to call bps_shutdown() to properly free the resources that were allocated. We can use the destructor in StatusEventHandler for this:

StatusEventHandler::~StatusEventHandler() {
    bps_shutdown();
}

Handle BPS events

The key to handling BPS events lies in the next function that we implement, the event() function. As you'll see when we complete the corresponding header file, our StatusEventHandler class inherits not only from QObject (as most Cascades classes do) but also from AbstractBpsEventHandler. This abstract class lets us implement the event() function, which is called when an event arrives in the BPS event queue. In our constructor, we subscribed to both network status and locale events (by calling subscribe()), so these types of events are passed to event() when they arrive in the queue.

By subclassing AbstractBpsEventHandler, we don't need to implement an event loop to continuously check for and process new events. Relevant events are delivered to our event() function as they arrive, so we can handle them at that time instead of polling for new events. This approach makes our app more efficient.

We start our event() function implementation by creating variables to store the network status and locale information that we'll retrieve from BPS events:

void StatusEventHandler::event(bps_event_t *event) {
    bool status = false;
    const char* language = "";
    const char* country = "";
    const char* locale = "";
    const char* interface = "";
    const char* type = "none";

When we receive an event, we need to know which platform service generated that event. Each event has a domain that corresponds to the service that generated it, and we can use the bps_event_get_domain() function to retrieve this domain. We first determine whether the event is a network status event:

if (bps_event_get_domain(event) == netstatus_get_domain()) {

In addition to a domain, each event also has a code that identifies the specific type of event that was generated. We're interested in network information events, so we test for those types of events:

if (NETSTATUS_INFO == bps_event_get_code(event)) {

The network status service gives us access to a lot of information about the active network connection on the device. For example, we can retrieve the status of the network connection (active or not active):

netstatus_info_t *info = netstatus_event_get_info(event);
if (info) 
{
    status = netstatus_info_get_availability(info);

The network status service gives us access to a lot of information about the active network connection on the device. For example, we can determine whether the device is connected using a wired, Wi-Fi, or Bluetooth connection. We retrieve the default interface used by the device and retrieve the detailed information that's associated with this interface:

interface = netstatus_info_get_default_interface(info);

netstatus_interface_details_t *details;

int success = netstatus_get_interface_details(interface, &details);

If we're able to retrieve the interface details successfully, we determine what specific type of interface is being used for the network connection and store that information:

if (success == BPS_SUCCESS) {
    switch (netstatus_interface_get_type(details)) {

    case NETSTATUS_INTERFACE_TYPE_UNKNOWN:
        type = "Unknown";
        break;

    case NETSTATUS_INTERFACE_TYPE_WIRED:
        type = "Wired";
        break;

    case NETSTATUS_INTERFACE_TYPE_WIFI:
        type = "Wi-Fi";
        break;

    case NETSTATUS_INTERFACE_TYPE_BLUETOOTH_DUN:
        type = "Bluetooth";
        break;

    case NETSTATUS_INTERFACE_TYPE_USB:
        type = "USB";
        break;

    case NETSTATUS_INTERFACE_TYPE_VPN:
        type = "VPN";
        break;

    case NETSTATUS_INTERFACE_TYPE_BB:
        type = "BB";
        break;

    case NETSTATUS_INTERFACE_TYPE_CELLULAR:
        type = "Cellular";
        break;

    case NETSTATUS_INTERFACE_TYPE_P2P:
        type = "P2P";
        break;
    }
}

After we're finished with the interface details, we need to free the memory that was allocated for the details structure, so we call netstatus_free_interface_details() to accomplish this. To indicate that the network status information has been updated, we emit a custom signal called networkStatusUpdated(). Earlier in the tutorial, we connected this signal to a slot function, networkStatusUpdateHandler(), in our BpsTutorial.cpp file (or ApplicationUI.cpp file). When we emit the signal, the slot function is called and the text of each Label is updated appropriately.

    netstatus_free_interface_details(&details);
    emit networkStatusUpdated(status, type);
}

Now that we've handled network status events, we can test to determine if the event is a locale event. If so, we make sure that the event is a locale information event, and update the language, country, and locale values appropriately. The locale service includes functions that retrieve each of these values from an event. Finally, we emit the custom signal localeUpdated() to indicate that the locale information has changed and that our app's UI should be updated to reflect the change.

    } else if (bps_event_get_domain(event) == locale_get_domain()) {
        if (LOCALE_INFO == bps_event_get_code(event)) {
            language = locale_event_get_language(event);
            country = locale_event_get_country(event);
            locale = locale_event_get_locale(event);
            emit localeUpdated(language, country, locale);
        }
    }
}

Update the handler header file

At this point, our app is almost complete. We just need to update the header file for our handler class, StatusEventHandler.h. Open this file and add the following include statements:

#include <QObject>
#include <QString>
#include <bb/AbstractBpsEventHandler>

Remember that our class inherits from both QObject and AbstractBpsEventHandler, so we update the class signature to reflect this. Every QObject subclass also needs to include the Q_OBJECT macro, which allows the class to use Qt features such as signals and slots.

class StatusEventHandler:
    public QObject, public bb::AbstractBpsEventHandler {
        Q_OBJECT

When you create a class that inherits from QObject and any other classes (in our example, AbstractBpsEventHandler), QObject must appear first in the list of inherited classes.

We declare our constructor, destructor, and the event() function, as well as our two custom signals:

public:
    StatusEventHandler();
    virtual ~StatusEventHandler();
    virtual void event(bps_event_t *event);

signals:
    void networkStatusUpdated(bool status, QString type);
    void localeUpdated(QString language,
                       QString country,
                       QString locale);
};

Include the appropriate library

Our final step is to include the bb library in our project. This library includes AbstractBpsEventHandler, so we need to add a line to the .pro file in our project to be able to use this class. Open the BpsTutorial.pro file and add the following line immediately after the CONFIG line:

LIBS += -lbb

We're done! Build and run the app to see the results.

Last modified: 2013-12-21

comments powered by Disqus