Client app for an enterprise push app

This client-side push-enabled sample app demonstrates how you can build push technology into your enterprise apps. This client-side push-enabled app works with the server-side push initiator using .NET.

The server app monitors a server IP address that you specify. If the server goes down or is unavailable, the server app sends a push message using an HTTP POST request to the BlackBerry Enterprise Server. The BlackBerry Enterprise Server then delivers the push message to the BlackBerry device. The Push Service on the device receives the push message and starts the client-side push-enabled app, which displays the push message.

Download and install the client app

Follow these instructions to build the client-side push-enabled app, deploy it on the BlackBerry Enterprise Server, and then install it on your device.

  1. Clone the ServerNotify repository. The repository includes the client-side and server-side sample apps.

  2. Import the SimplePushClient project into your workspace in the Momentics IDE for BlackBerry, and then open the project.

  3. In the Momentics IDE for BlackBerry, open the bar-descriptor.xml file, and change the value for the invoke-target id tag to something that's unique for your company and app. This value identifies the client app that's the target of a push message. For example, if your company is named ABC Company, and your app is called Server Notify, then your invoke-target id could be com.abccompany.servernotify.

    <invoke-target id="com.abccompany.servernotify">
         <type>APPLICATION</type>
         <filter>
              <action>bb.action.PUSH</action>
              <mime-type>application/vnd.push</mime-type>
         </filter>
    </invoke-target>

    The action tag is set to bb.action.PUSH. This value indicates that the invoke event relates specifically to push messages.

  4. In the src folder, open the PushManager.cpp file, and set the BLACKBERRY_INVOKE_TARGET_ID value to match the invoke-target id in the bar-descriptor.xml file.

    const QString PushManager::BLACKBERRY_INVOKE_TARGET_ID = 
         "com.abccompany.servernotify";
  5. You can also set the BLACKBERRY_PUSH_APPLICATION_ID to a value that's unique to your company and app. The client app uses the application ID to register with the Push Service to receive push messages. The application ID must be the same in the client and server apps.

    const QString PushManager::BLACKBERRY_PUSH_APPLICATION_ID = 
         "bb_server_notify";
  6. Build, sign, and export the project to create the .bar file.

  7. Deploy the signed .bar file to the BlackBerry Enterprise Server by asking the administrator to do the following:

    • Add the .bar file to the BlackBerry Enterprise Server.
    • Add the .bar file to the software configuration.
    • Apply the software configuration to the user account that you're testing with. Software policies can take some time to be applied to your device.
  8. The administrator can make the software configuration either required or optional. If the software configuration is required, the client app is installed automatically through BlackBerry World for Work on your device. If the software configuration is optional, install the app in the work space on your device from BlackBerry World for Work.

  9. Run the app once on your device so that it can register with the Push Service to start receiving push messages.

Client app

The code for the client-side push-enabled app is in the PushManager.cpp file. Here's how the code processes push messages.

Create PushService object

First, the app creates a PushService object to create a push session and perform push-related operations. The application ID and invoke-target id are parameters of the PushService() constructor.

const QString PushManager::BLACKBERRY_PUSH_APPLICATION_ID = 
    "bb_server_notify";
const QString PushManager::BLACKBERRY_INVOKE_TARGET_ID = 
   "com.abccompany.servernotify";

// ...
m_pushService = new PushService ( BLACKBERRY_PUSH_APPLICATION_ID,
   BLACKBERRY_INVOKE_TARGET_ID );

The app also connects the slots for the createSessionCompleted() and createChannelCompleted() signals.

connect(m_pushService,
  SIGNAL(createSessionCompleted (const bb::network::PushStatus &)),
  this,
  SLOT(createSessionCompleted(const bb::network::PushStatus&)));

 // ...
connect(m_pushService,
  SIGNAL(createChannelCompleted (const bb::network::PushStatus&,
  const QString &)), this,
  SLOT(createChannelCompleted(const bb::network::PushStatus&,
  const QString &)));

Create a session

After the PushService object is created, the app creates a session by calling createSession(). Creating a session sets up interprocess communication between the app and the Push Service.

m_pushService->createSession();

Create a channel

When the session is created, the app calls createChannel() to create a channel to the BlackBerry Enterprise Server to receive push messages. The call to createChannel() takes a URL as an argument. When creating a channel to the BlackBerry Enterprise Server, an empty string is passed as the URL.

const QString PushManager::BLACKBERRY_PUSH_URL = "";

// ...
void PushManager::createSessionCompleted(
		const bb::network::PushStatus& status) {
	if (!status.isError() && m_pushService) {
		log("Session creation completed successfully");
// ...
m_pushService->createChannel(QUrl(BLACKBERRY_PUSH_URL));

Register to launch

After the app creates a channel, it calls registerToLaunch(). This call tells the Push Service to start running the app in the background if it isn't already running when a push message arrives. In your app, if you want to see only push messages when your app is open, you don't need to call registerToLaunch().

void PushManager::createChannelCompleted(const 
          bb::network::PushStatus& status,
		const QString& token) {
	Q_UNUSED(token);
	if (!status.isError() && m_pushService) {
		log("Channel creation completed successfully");
		m_pushService->registerToLaunch();
	} else {
		log("Channel creation failed: " + status.errorDescription());
	}
}

Handle push messages

Now that a session and channel have been created, the app can handle push messages. When a push message arrives on the device, the Push Service matches the application ID in the push header to a client app that's registered with the Push Service. The Push Service then invokes the app associated with the ID.

In the PushHandler constructor, connect the invoked() signal with the invoked() slot.

invokeManager = new InvokeManager(this);
     connect(invokeManager,
        SIGNAL(invoked(const bb::system::InvokeRequest&)), 
        this,
        SLOT(invoked(const bb::system::InvokeRequest&)));

The void PushManager::invoked() function takes in the invoke request that includes the push message, and processes the push message.

Check the invoke request to see if it came from the Push Service.

void PushManager::invoked(const InvokeRequest& request) {
	//Check whether the app was invoked via a push
	if (request.action().compare("bb.action.PUSH") != 0) {
		log("Not a push invocation :("); //Nope, so return
		return;
	}

Create a payload object from the invoke request.

PushPayload payload(request);

Check that the payload is valid.

if (payload.isValid()) {

Use the Notify URL to acknowledge that the push message was received. In your app, you would acknowledge the push message if the server app requires notification that the client app received the message.

if (payload.isAckRequired()) { 
          m_pushService->acceptPush(payload.id());
     }

Read the push message.

QString message = payload.data();

The app assumes that push messages consist of four parts that are delimited with a "|" character. The push messages also have three priority levels: low, medium, and high.

// For the purposes of this sample only, all push 
// content is formatted into 4 parts using a '|' character as a delimiter
QStringList messageParts = message.split('|');
if (messageParts.size() != 4) {
		log(
			"Invalid list length. Expected 4, received "
			+ messageParts.size());
        return;
}

// ...
// The first part of the push denotes the priority of the message
switch (messageParts.at(0).toInt()) {
case PushManager::Low:
	 handleLowPriorityPush();
	 break;
case PushManager::Medium:
     handleMediumPriorityPush(messageParts.at(1), 
      messageParts.at(2), messageParts.at(3));
 	break;
case PushManager::High:
  	handleHighPriorityPush(messageParts.at(1), 
       messageParts.at(2), messageParts.at(3));
  	break;
default:
  	break;
		}

If the message is in the correct format, the app splits the message into its various parts and stores them.

void PushManager::logPush(const QString & pushMessage) {
	pushList = settings.value("pushList").toList();
	QString pushMessageWithDate = pushMessage;
	pushMessageWithDate = pushMessageWithDate.append(
			"|" + QDateTime::currentDateTime().toString("h:mm:ss ap"));
	log(pushMessageWithDate);
	pushList.append(pushMessageWithDate);
	settings.setValue("pushList", pushList);

For low priority messages, the app adds the Splat indicator to the app icon, and closes the app if the user hasn't opened it full screen.

void PushManager::handleLowPriorityPush() {
	app->setIconBadge(bb::IconBadge::Splat);
	if (!appWasLaunchedFromRibbon() && !hasBeenFullScreened) {
		app->requestExit();
	}
}

For medium priority messages, the app sends a system notification (with the associated sound notification, LED notification, or device vibration), adds the Splat indicator to the app icon, and closes the app if the user hasn't opened it full screen.

void PushManager::handleMediumPriorityPush(const QString & title,
		const QString & body, const QString & url) {
	notification.setTitle(title);
	notification.setBody(body);
	InvokeRequest invokeRequest;
	invokeRequest.setUri(url);
	notification.setInvokeRequest(invokeRequest);
	notification.notify();
	if (!appWasLaunchedFromRibbon() && !hasBeenFullScreened) {
		app->requestExit();
	}
}

For high priority messages, the app sends a notification repeatedly using the notificationDialog (with the associated sound notification, LED notification, or device vibration), adds the Splat indicator to the app icon, and leaves the app open. The notification continues to appear until the user dismisses it.

void PushManager::handleHighPriorityPush(const QString & title,
		const QString & body, const QString & url) {
	notificationDialog.cancel();
	notificationDialog.setTitle(title);
	notificationDialog.setBody(body);
	notificationDialog.setRepeat(true);
	notificationDialog.show();
}

In your app, you should use only system dialog boxes in urgent situations. In other situations, you can display a notification to the user in the BlackBerry Hub, or you can use some other method for processing non-urgent messages.

Exit after processing a push message

Because the app calls registerToLaunch() to run in the background when a push message arrives, it continues to run after it processes the message. This can consume device resources, so you should exit the app after it processes a push message. This approach is a good practice that you should implement in your app. You have to be careful, however, not to exit your app if it was ever displayed full screen (in the foreground) because the user might still be using your app.

To exit after processing a push message, the sample app uses the hasBeenFullScreened variable to check whether the app was ever displayed full screen. The variable is set initially to false.

PushManager::PushManager(Application *app) :
		app(app) {

	hasBeenFullScreened = false;

The sample app also connects a slot to the fullscreen() signal. If this signal is emitted, the app was displayed full screen, and the hasBeenFullScreened variable is set to true. The app also clears the notifications when it's displayed full screen.

connect ( app , SIGNAL ( fullscreen ()), this , 
     SLOT ( appFullScreened ()));

// ...
void PushManager::appFullScreened() {
	notification.clearEffectsForAll();
	app->setIconBadge(bb::IconBadge::None);
	hasBeenFullScreened = true;
}

After the sample app processes a low or medium priority push message, it checks the hasBeenFullScreened variable. If the variable is false, the app was never displayed full screen. The app also checks that it was started in the background when a push message arrived (instead of by the user). If the app was started when a message arrived, it can exit after it processes the message.

if (!appWasLaunchedFromRibbon() && !hasBeenFullScreened) {
		app->requestExit();

Last modified: 2015-07-24



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

comments powered by Disqus