How to add push notifications to Android

May 25, 2016
Written by

t_3uIdWRwKl2jWx2HWpR25RUdhElEBaTolo0n9bi_oQJ5YDgKILOCety6JPZRvUEoINAbGVb84aB6kNs9McbIZtYTDvCF7z7fjSgkYF6tgzmMOLLOFqD_-tAMc0QeNX7u6GDGDFt

Having the right engagement strategy within a mobile app has never been more important since users expect your app to provide them with useful and timely information.

With Notify, the new Twilio notifications API you can send your users real-time push notifications even when the app is not open.

Let’s build an Android app that can receive and react to push notifications sent from the server.

Our tools

Backend Configuration

To get our backend server going, we first need to create some credentials. Head to the Credentials section on the Notify Console and create a new push credential of type GCM. You will need a GCM API Key and a configuration file which you can generate by following the steps described in this page.

small-enable-gcm.gif

The Android Package Name must match your application’s package. If you have an existing application, you can copy this information from app/src/main/AndroidManifest.xml and in the manifest tag copy the value for package. If you’re creating an application from scratch, use the values as seen above.

When you’ve done that click Download google-services.json and copy the Server API Key. You will have to paste that key back where we were creating a push credential.

Make sure you save that and make a note of the SID of this push credential. You can find it back on the Credentials page.

Head to Services, click Create Service and name it anything you fancy. I’ve named mine Twilio Push. Under GCM Credential SID, choose the push credential you just created and hit Save.


Make a note of the SID for that too as we will need it when configuring our backend app. You can get that by going back to the Services page.

Our backend

Now that we have all the necessary credentials to send push notifications let’s do some work on our backend.

Clone the Notifications Quickstart repository and change the config.json file to use the credentials we’ve just created along with your Account SID and Auth Token which you can get from here.

git clone git@github.com:TwilioDevEd/notifications-quickstart-node.git

Your config.json should look similar to this when you’re done with it:

{
  "TWILIO_ACCOUNT_SID": "ACXXXXXXXXXXXXXXXXXXXXXX",
  "TWILIO_AUTH_TOKEN": "6bXXXXXXXXXXXXXXXXXXXXXX",
  "TWILIO_CREDENTIAL_SID": "CRXXXXXXXXXXXXXXXXXXXXXX",
  "TWILIO_NOTIFICATION_SERVICE_SID": "ISXXXXXXXXXXXXXXXXXXXXXX"
}

Fire up the application by going to your terminal and running:

npm install && npm start

Head to http://127.0.0.1:3000 and you should see a screen that looks similar to this:


Our backend is now configured and we just need to make it’s available outside of our environment so our mobile app can see it. We will use ngrok for this.

In a new terminal screen run the following and take a note of the forwarding URL generated.

ngrok http 3000

If you don’t feel like going through the entire app build and just want to download and deploy an app to your device, feel free to clone this repository.

Building the app

At this point we have a backend ready to send push notifications and you can either follow the next steps to add functionality to an existing app you’re already building or create a new Android app with an empty activity.

On your app open up your project level build.gradle and add the following dependency to it.


dependencies {
   classpath 'com.android.tools.build:gradle:2.1.0'
   classpath 'com.google.gms:google-services:2.0.0-alpha9'
}

On the module level build.gradle file add the following dependencies:


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:design:23.1.1'
    compile 'com.google.android.gms:play-services-gcm:8.4.0'
    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
    compile 'com.squareup.retrofit2:converter-jackson:2.0.0-beta4'
}

apply plugin: ‘com.google.gms.google-services’

Android Studio will then ask you to sync, but before you do that make sure you move the google-services.json file you downloaded earlier into your app folder so you end up with something like this:

Now that we have all necessary dependencies, we will change our AndroidManifest.xml so it knows our app can receive push notifications and that those notifications can be received even when our application is running in background.

Add the following permission requests:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.testapp">

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

Next we will register our Broadcast Receiver and Service classes. These will make sure our application can receive messages and that it responds to an intent even when in background.


<application
   android:allowBackup="true"
   android:icon="@mipmap/ic_launcher"
   android:label="@string/app_name"
   android:supportsRtl="true"
   android:theme="@style/AppTheme">

   <receiver
       android:name="com.google.android.gms.gcm.GcmReceiver"
       android:exported="true"
       android:permission="com.google.android.c2dm.permission.SEND">
       <intent-filter>
           <action android:name="com.google.android.c2dm.intent.RECEIVE" />
       </intent-filter>
   </receiver>

   <service
       android:name=".service.MyGcmListenerService"
       android:exported="false">
       <intent-filter>
           <action android:name="com.google.android.c2dm.intent.RECEIVE" />
       </intent-filter>
   </service>

   <service
       android:name=".service.MyInstanceIDListenerService"
       android:exported="false">
       <intent-filter>
           <action android:name="com.google.android.gms.iid.InstanceID" />
       </intent-filter>
   </service>

   <service
       android:name=".service.RegistrationIntentService"
       android:exported="false" />

   <activity android:name=".MainActivity">
       <intent-filter>
           <action android:name="android.intent.action.MAIN" />

           <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
   </activity>
</application>

But our services refer to classes that still haven’t been created. Those classes are pretty much the same as the ones on the Google Services Sample Code, so let’s just copy those classes into our project. To keep things neat add the following four classes to a new folder called service in src/main/java/com/testapp/.

In this last class you’re gonna want to replace YOUR_NGROK_URL with the url you got from ngrok earlier. Make sure you only use the URL without the protocol, so it will be something like:

private static final String host = "6143c1b0.ngrok.io";

If you look in that file, you will see we’re referencing another class called BindingResource which wasn’t originally part of the Google Sample code. That is because the sample code didn’t have an implementation for the server registration as there are different servers you can register with.

In the application’s root along with MainActivity.java, create a new class called BindingResource.java. We will use a very nifty library called Retrofit which converts an HTTP API into a Java interface. This means we can get our app to bind directly to our backend application.

package com.testapp;

import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;

public interface BindingResource {
   @FormUrlEncoded
   @POST("/register")
   Call<Void> createBinding(@Field("identity") String identity, @Field("endpoint") String endpoint, @Field("Address") String address, @Field("BindingType") String bindingType);
}

We have all the classes in place now, so let’s make a change to our layout to make sure our application started correctly and is bound to our services. Open src/main/res/layout/activity_main.xml and replace the existing TextView with the following:

<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@ id/output_result"
        android:inputType="none"
        android:textColor="#E43F3F"
        android:textIsSelectable="false"
        android:textSize="@dimen/abc_text_size_medium_material" />

Now open src/main/java/com/testapp/MainActivity.java and at the top add the following imports and member variables.


import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import com.testapp.service.RegistrationIntentService;
import static com.testapp.service.QuickstartPreferences.*;

public class MainActivity extends AppCompatActivity {
    TextView _resultText;
    private WakefulBroadcastReceiver mRegistrationBroadcastReceiver;

The onCreate method is where we’re going to register our BroadCastReceiver and update the status of our layout’s status field to tell us whether the binding was successful or not.


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

        mRegistrationBroadcastReceiver = new WakefulBroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {

                boolean sentToken = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SENT_TOKEN_TO_SERVER,false);
                if (sentToken) {
                    _resultText.setText("Binding successfully created");
                    _resultText.setVisibility(View.VISIBLE);
                } else {
                    _resultText.setText("Could not create Binding. Consult logs about the error.");
                    _resultText.setVisibility(View.VISIBLE);
                }

            }
        };

        LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
                new IntentFilter(REGISTRATION_COMPLETE));
        setContentView(R.layout.activity_main);

        _resultText = (TextView) findViewById(R.id.output_result);
        _resultText.setVisibility(View.INVISIBLE);

        createBinding();
    }

 

We’re using SharedPreferences to store our tokens and identity information. You can use a database for that as well if you want.

Next implement a couple more methods for creating the service registration and resuming the activity upon wakeup.


public void createBinding() {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString(IDENTITY, "marcos");
        editor.putString(ENDPOINT, "Marcos' Android Phone");
        editor.apply();

        // Start IntentService to register this application with GCM.
        Intent intent = new Intent(this, RegistrationIntentService.class);
        startService(intent);
    }


    @Override
    protected void onResume() {
        super.onResume();
        LocalBroadcastManager.getInstance(this.getApplicationContext()).registerReceiver(mRegistrationBroadcastReceiver,
                new IntentFilter(REGISTRATION_COMPLETE));
    }

When our application starts we create an identity and an endpoint for this device. An Identity is defined by your application and can have multiple endpoints, so if your application has a concept of users you could use that instead.

An Endpoint is the identifier of the device to which this registration belongs. You can find out more about bindings here. Feel free to change those values with yours instead.

Go to the Build menu item and choose Rebuild Application. Once that’s done run the application on the device or emulator and you should see that it successfully registers with our endpoint.

Run App Bind.gif

Now that you know your application is correctly bound to the endpoint, let’s try to send a notification to it.

Back in your terminal application open a new window making sure you’re inside the notifications-quickstart-node folder and enter the following:

node notify.js marcos

You will need to replace marcos with whatever you used as the Identity on the createBinding method.You should now get a nice push notification on your device.

Push Notification Device.gif

Timely information at your fingertips

With only a few lines of code we transformed a dumb app into something that gives users time sensitive information.

You can add this to your shopping app to let users know when the product they’ve been looking for becomes available. You could also add it to your game to tell the players when a new DLC has been released, or to your messaging app to let users know when someone wants to communicate with them.

I would love to see what you build. Hit me up on Twitter @marcos_placona or by email on marcos@twilio.com to tell me more about it.