Liferay Mobile – Creating Great Omnichannel Experiences - Part 2: Liferay Push

This blog entry is part of a three-part blog series that explores some of the great features provided by Liferay Screens and Liferay Mobile SDK. In this this series, I will cover:
  • Liferay Screens – Use screenlets in your native Android or iOS app to leverage content and services available on your Liferay server
  • Liferay Push Notifications – Push offers and notifications directly to user handsets
  • Liferay Audience Targeting – Deliver targeted content and create personalised experiences within your native Android or iOS app
All examples in this series were developed for Android, using Android Developer Studio 3 and Liferay DXP.
 
Part 2 – Liferay Push

Liferay Push is a framework for sending push notifications from a Liferay server to Android and iOS mobile apps. It’s another great Liferay feature to add to your omnichannel armoury and delivers many great benefits, including:

  • Proactive engagement with your clients. Engagement through push notifications helps to provide up-date relevant information to clients, create a personalised experience, increase traffic and drive conversions.
  • Real-time communication. While emails might take time to be delivered or get lost in a spam folder, push messages reach clients instantly. Especially important for time-critical campaigns or offers!
  • Direct communication. A push notification is a message that is sent directly to a client’s mobile device. The message will pop up even if the device is locked or if the client is in a different app. 
  • Effective communication. Push notifications are a lot less messy then emails. They are simple, uncomplicated messages that provide only essential information, making them user friendly and effective.

In this article, we will take the Android app we developed in the first article and modify it so that it:

  • Registers with our Liferay server in order to receive notifications
  • Receives and parses incoming notification messages
  • Displays notifications in the Android notification drawer

Before we dive into this example, it’s important to briefly explain some of the fundamental mechanics behind it: 

1. Once a Liferay Push-enabled app has registered with a Liferay server, it can wait for notifications from the server. It can even be notified when it's not running - the system will wake it – so you can avoid polling the server for new messages every few seconds (and draining the device’s battery in the process).

2. Messages sent from Liferay to an Android handset are not transmitted directly, rather they go through an intermediary service – the Google Cloud Messaging (GCM) service. GCM is a free service that caters for downstream messages from servers to client apps, and upstream messages from client apps to servers.

3. When a Liferay Push-enabled mobile app registers with a Liferay server, it does so directly.

4. Currently, Liferay Push supports two push notification providers: Google and Apple.

In order to follow this example, you will need a Google account, a Google Firebase project and a Google Cloud Messaging (aka Firebase Cloud Messaging) server key.

 

Configure the Liferay Push plugin

1.       Install the Liferay Push plugin from the Market place

2.       Obtain a “Server key” from Google Cloud Messaging (Firebase > Project > Settings > Cloud Messaging > Server key).

(Take note also of your Sender ID - you will need this later in your app.)

3.       Configure the Liferay Push plugin. Go to Control Panel > System Settings > Other > Android Push Notifications Sender and input your Server key from step #2 into the API Key field.

 

Configure the mobile app

1.       Now it’s time to switch to our mobile app. The first step here is to add the Push gradle dependency.

compile 'com.liferay.mobile:liferay-push:1.1.0'

 

2.       Next, add the necessary app permissions to AndroidManifest.xml:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

3.       Create PushReceiver  - a custom notifications receiver for our app.

package com.jfeeney.liferay.mobile.push;

import com.liferay.mobile.push.PushNotificationsReceiver;

public class PushReceiver extends PushNotificationsReceiver {
    @Override
    public String getServiceClassName() {
        return PushService.class.getName();
    }
}

 

4.       Add PushReceiver to AndroidManifest.xml:

<receiver
    android:name=".push.PushReceiver">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="com.liferay.mobile.push" />
    </intent-filter>
</receiver>

 

5.       Create PushService – this class will parse the incoming message and display a notification (with custom icon) in the notification drawer at the top of the device.

package com.jfeeney.liferay.mobile.push;

import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.jfeeney.liferay.mobile.R;
import com.liferay.mobile.push.PushNotificationsService;

import org.json.JSONException;
import org.json.JSONObject;

public class PushService extends PushNotificationsService {

    @Override
    public void onPushNotification(JSONObject jsonObject) {
        super.onPushNotification(jsonObject);

        try {

            jsonObject = new JSONObject(jsonObject.getString("body"));

            String title = getString(jsonObject, "title");
            String description = getString(jsonObject, "description");

            createGlobalNotification(title, description);
        } catch (JSONException e) {
            Log.e("Error", e.getMessage());
        }
    }

    private void createGlobalNotification(String title, String description) {
        Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setContentTitle(title)
                .setContentText(description)
                .setAutoCancel(true)
                .setSound(uri)
                .setVibrate(new long[]{2000, 1000, 2000, 1000})
                .setSmallIcon(R.drawable.liferay_glyph);

        Notification notification = builder.build();
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(1, notification);
    }


    private String getString(final JSONObject json, final String element) throws JSONException {
        return json.has(element) ? json.getString(element) : "";
    }
}

  

6.       Add the PushService service to AndroidManifest.xml: 

<service android:name=".push.PushService" />

 

7.       Add our Sender ID (from above) to strings.xml. Replace GCM_SENDER_ID with actual value.

<string name="push_sender_id">[GCM_SENDER_ID]</string>

 

8.       Change the MainActivity so that it extends PushScreensActivity and implements the necessary abstract methods by adding the following.

package com.jfeeney.liferay.mobile;

import android.os.Bundle;

import com.liferay.mobile.screens.push.PushScreensActivity;
import com.liferay.mobile.screens.util.LiferayLogger;
import com.liferay.mobile.android.service.Session;
import com.liferay.mobile.screens.context.SessionContext;

import org.json.JSONObject;

public class MainActivity extends PushScreensActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected Session getDefaultSession() {
        return SessionContext.createSessionFromCurrentSession();
    }

    @Override
    protected void onPushNotificationReceived(JSONObject jsonObject) {
        LiferayLogger.d("Received! " + jsonObject.toString());
    }

    @Override
    protected void onErrorRegisteringPush(String message, Exception e) {
        LiferayLogger.e("Error registering!", e);
    }

    @Override
    protected String getSenderId() {
        return this.getString(R.string.push_sender_id);
    }

}

 

Test registration and notifications

1.       OK, now it’s time to test our app and make sure that it registers the device with our Liferay portal upon successful login. Verify that there are currently no registered devices (Control Panel > Configuration > Push Notifications > Devices).

2.       Login to the mobile app. Verify that there is now a registered device with the Push plugin:

3.       Note that, if you delete a registered device on the Liferay server and try to re-register it by performing another app login, registration will not re-occur. This is by design - it is bad practice to perform registration every time you launch an app. If you inspect the code within PushScreensActivity you will see that you can force re-registration by either changing your app version (versionCode in build.gradle) or by running the app in debug mode:

if (!userAlreadyRegistered || appUpdated || BuildConfig.DEBUG) {
   push.onSuccess(this).onFailure(this).register(this, getSenderId());
} else {
   push.onPushNotification(this);
}

 

4.       Now that our device is registered with the Liferay portal, it’s time to send a message to it! Go to the Test tab of the Liferay Push plugin and submit the following JSON payload:

5.       Click send and note that a notification is now present on the device.

 

 

Next steps

1.       The notifications in this example don’t actually do anything but, in a real-world scenario, you may want to include a call-to-action that takes the user somewhere, e.g., to a specific activity within your app.

 

2.       At this point, you might also want to develop a custom portlet to make it easy for someone from the marketing department to create notification messages and to target specific devices as part of a marketing campaign. This is easily achieved through Liferay’s Audience Targeting feature and through the Liferay API, but beyond the scope of this article.

In order to use the API to create and send the notifications themselves, the following code will get you started:

import com.liferay.push.notifications.model.PushNotificationsDevice;
import com.liferay.push.notifications.service.PushNotificationsDeviceLocalService;
import com.liferay.push.notifications.service.PushNotificationsDeviceLocalServiceUtil;
import com.liferay.portal.kernel.json.JSONFactoryUtil;
import com.liferay.portal.kernel.json.JSONObject;

/* ......... */

PushNotificationsDeviceLocalService pushService = PushNotificationsDeviceLocalServiceUtil.getService();

/* ......... */

StringBuilder payloadString = new StringBuilder();
payloadString.append("{\"body\":\"{\\\"title\\\":\\\"").append(msgTitle);
payloadString.append("\\\", \\\"description\\\":\\\"").append(msgBody);
payloadString.append("\\\"}\"}");

JSONObject payload = JSONFactoryUtil.createJSONObject(payloadString.toString());
pushService.sendPushNotification(deliveryDeviceUserIds, payload);

 

Conclusion

Liferay provides the tooling you need to send push notifications from your Liferay server to your users’ Android and iOS apps. With a little configuration - mostly on the mobile app side of things - this convenient framework helps you to create great, immersive digital experiences.

The source code for the Android app used in this example can be found here: 

https://github.com/johnfeeney/liferay-push-demo

 

Feel free to clone or fork the code - and have fun playing with Liferay push notifications!