Android Firebase Push Notification with Topic Message Subscription

In this tutorial, we are going to learn how we can implement toipc message subscription using android Firebase subscription.

There are situations in your application where you will like to give your app users the opportunity to subscribe to a certain topic in other to receive push notification related to the topic. This is a good idea because using group Firebase Push Notification can see your app uninstall my many users who do not give their consent for notification.

This android Firebase tutorial will be similar to my previous tutorial on Android Firebase Cloud Messaging (Push Notification) with Backend Admin in Php and Mysql database. If you have not read this tutorial I will suggest you do so first because most of the things we will discuss here are based on what have been done in the article above.

The IU of the android app will be simple. A setting page with a toggle button which is used for subscribing and unsubscribing from Firebase Push Notification.

We are not going to create Admin panel but if you want to include an admin panel in your project, you can used the admin panel I explained in the previous tutorial.

 

How the app will look like

firebase pushnotification

 

SETUP FIREBASE PROJECT

Follow the steps below to register and integrate Firebase SDK to your android project.

Go the Firebase webite and register if you don’t have an account already. The registration is very simple. After registration, go to the console dashboard and click on create new project button.

In the window modal, enter the name of your project and country as shown in the image below

Android firebase

Click on the create project button to continue. In the console, click the button add Firebase to your android app as shown below

Android firebase

In the next window, add the Package name of your application and click the Add App button as shown below.

Android firebase

Click the continue button to download a Json configuration file which will be added in the root App directory of your project. See the image below

Android firebase

Following the instructions on how to add the Google Services plugin and the Firebase SDK in build.gradle as shown below.

Android firebase

We have successfully added Firebase to our project. We will now focus on our android project.

 

CREATE ANDROID PROJECT

Lets start to soil our hands in code. Start up your IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 10

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 23

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidTopicMessaging

Package: com.inducesmile.androidtopicmessaging

Select Blank Activity

Name your activity : SettingActivity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

 

BUILD.GRADLE

We are going to complete Firebase integration in our android project by modifying the project build.gradle file.

Open the Project build.gradle file and add a classpath for google services as shown below.

classpath 'com.android.tools.build:gradle:2.1.2'
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.2'
        classpath 'com.google.gms:google-services:3.0.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    repositories {
        jcenter()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}

Open the app build.gradle and add Firebase ad dependency and google services plugin. Apart from the Firebase Libraries, we are also going to make use of the android volley and Gson library for network call and Json parsing.

apply plugin: 'com.android.application'
android {
    compileSdkVersion 24
    buildToolsVersion "24.0.0"
    defaultConfig {
        applicationId "com.inducesmile.androidtopicmessaging"
        minSdkVersion 14
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.1'
    compile 'com.google.firebase:firebase-messaging:9.0.1'
    compile 'com.mcxiaoke.volley:library:1.0.19'
    compile 'com.google.code.gson:gson:2.6.1'
}
apply plugin: 'com.google.gms.google-services'

 

MANIFEST.XML

Since Google is serving the advertisement from their server, it will require network call behind the hood.

Apart from adding the internet permission, we are going to add our Service classes to the Manifest.

The modify version of Manifest.xml file is shown below

 <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SettingActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".CustomFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
        <service
            android:name=".CustomFirebaseInstanceIDService">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
            </intent-filter>
        </service>
    </application>
</manifest>

 

STRINGS.XML

We are going to update our project strings.xml file located in the values folder inside the res folder. Open the file and add the code below to it.

<resources>
<string name="app_name">Subscribe to Push Notification</string>
<string name="pref_header_notifications">Push Notifications</string>
<string name="pref_title_new_message_notifications">Register or unregister for Push Notification</string>
<string name="pref_header_general">Tab Menu Website</string>
<string name="push_notification">Push Notification Settings</string>
</resources>

 

COLORS.XML

Open the colors.xml file in the same location as the strings.xml file and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
</resources>

 

SettingActivity.java

This is the default Activity page created for us by Android Studio when our project was created. Delete the corresponding layout file since we are not going to use it. Also we will delete the setContentView() method call in onCreate(Bundle bundle) method.

Add this line of code after deleting the method.

addPreferencesFromResource(R.xml.pref_general);

This will enable us use a Preference Fragment to create the UI of the SettingActivity page.

We will get the instance of the Preference control and attach a setOnPreferenceChangeListener to enable store the current state of the preference.

Create a new private method that will be use to check the availability of Google Play Services.

The complete code for the SettingActivity class is as shown.

import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.SwitchPreference;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
public class SettingActivity extends AppCompatActivity {
    private static final String TAG = SettingActivity.class.getSimpleName();
    private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new GeneralPreferenceFragment())
                .commit();
    }
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class GeneralPreferenceFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.pref_general);
            Preference notificationPref = (SwitchPreference)findPreference("notifications_new_message");
            notificationPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
                @Override
                public boolean onPreferenceChange(Preference preference, Object newValue) {
                    boolean isNotificationOn = (Boolean) newValue;
                    if(preference.getKey().equals("notifications_new_message") && isNotificationOn){
                        // call the push registration for this user
                        checkPlayServices();
                        FirebaseMessaging.getInstance().subscribeToTopic("news");
                        String token = FirebaseInstanceId.getInstance().getToken();
                        Log.d(TAG, "Token value " + token);
                    }else{
                        FirebaseMessaging.getInstance().unsubscribeFromTopic("news");
                    }
                    return true;
                }
            });
        }
        private boolean checkPlayServices() {
            GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
            int resultCode = apiAvailability.isGooglePlayServicesAvailable(getActivity());
            if (resultCode != ConnectionResult.SUCCESS) {
                if (apiAvailability.isUserResolvableError(resultCode)) {
                    apiAvailability.getErrorDialog(getActivity(), resultCode, PLAY_SERVICES_RESOLUTION_REQUEST).show();
                } else {
                    Log.i(TAG, "This device is not supported.");
                    getActivity().finish();
                }
                return false;
            }
            return true;
        }
    }
}

 

pref_general.xml

Create a new folder in your res folder and name it xml if it does not exist. Inside the xml folder, create a layout file and name it pref_general.xml. This is the layout file SettingActivity used to create its UI.

Add the SwitchPreference View control to the file as shown.

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <SwitchPreference
        android:defaultValue="false"
        android:key="notifications_new_message"
        android:summary="@string/pref_title_new_message_notifications"
        android:title="@string/pref_header_notifications" />
</PreferenceScreen>

 

CustomFirebaseInstanceIDService.java

In other to use Firebase Push Notification, the connecting device must obtain a unique token which Firebase Cloud Messaging System will be used to identify the device. If the app is uninstall, FCM handle the token management but it is also advisable to store each connecting token to your own remote database.

This class extends FirebaseInstanceIdService and override the onTokenRefresh() method of the class. A fresh token is obtain by calling this line of code

String refreshedToken = FirebaseInstanceId.getInstance().getToken();

To send the generated token to your remote database server, create a method and name it anything you like. Make network call inside the method to store the token.

Complete code for this class is as shown.

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.inducesmile.androidtopicmessaging.helpers.Helper;
import com.inducesmile.androidtopicmessaging.helpers.MySharedPreference;
import com.inducesmile.androidtopicmessaging.helpers.TokenObject;
import java.util.HashMap;
import java.util.Map;
public class CustomFirebaseInstanceIDService extends FirebaseInstanceIdService {
    private static final String TAG = CustomFirebaseInstanceIDService.class.getSimpleName();
    private RequestQueue queue;
    private TokenObject tokenObject;
    private MySharedPreference mySharedPreference;
    @Override
    public void onTokenRefresh() {
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Log.d(TAG, "Token Values: " + refreshedToken);
        sendTheRegisteredTokenToWebServer(refreshedToken);
    }
    private void sendTheRegisteredTokenToWebServer(final String token){
        queue = Volley.newRequestQueue(getApplicationContext());
        mySharedPreference = new MySharedPreference(getApplicationContext());
        StringRequest stringPostRequest = new StringRequest(Request.Method.POST, Helper.PATH_TO_SERVER_IMAGE_UPLOAD, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Toast.makeText(getApplicationContext(), response.toString(), Toast.LENGTH_LONG).show();
                GsonBuilder builder = new GsonBuilder();
                Gson gson = builder.create();
                tokenObject = gson.fromJson(response, TokenObject.class);
                if (null == tokenObject) {
                    Toast.makeText(getApplicationContext(), "Can't send a token to the server", Toast.LENGTH_LONG).show();
                    mySharedPreference.saveNotificationSubscription(false);
                } else {
                    Toast.makeText(getApplicationContext(), "Token successfully send to server", Toast.LENGTH_LONG).show();
                    mySharedPreference.saveNotificationSubscription(true);
                }
            }
        },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Toast.makeText(getApplicationContext(), error.toString(), Toast.LENGTH_LONG).show();
                    }
                }) {
            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<String, String>();
                params.put(Helper.TOKEN_TO_SERVER, token);
                return params;
            }
        };
        queue.add(stringPostRequest);
    }
}

 

CustomFirebaseMessagingService.java

We will create another service class which extends from FirebaseMessagingService. You can name the class anything, This class will override the onMessageReceived(RemoteMessage remoteMessage) method. This is where will can get the message sent to our device through or from Firebase Cloud Messaging System.

Create a send message notification method that will take to send message and up it up as a notification in our device.

The complete code for this class is as shown

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class CustomFirebaseMessagingService extends FirebaseMessagingService {
    private static final String TAG = CustomFirebaseMessagingService.class.getSimpleName();
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.d(TAG, "From: " + remoteMessage.getFrom());
        Log.d(TAG, "Notification Message Body: " + remoteMessage.getNotification().getBody());
        String message = remoteMessage.getNotification().getBody();
        sendNotification(message);
    }
    private void sendNotification(String message) {
        Intent intent = new Intent(this, SettingActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.notificationicon)
                .setContentTitle("Firebase Push Notification")
                .setContentText(message)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, notificationBuilder.build());
    }
}

 

Create a helpers package folder

Create a helpers package folder for different helper class we will use in this application. Below is the class and their complete code content.

 

TokenObject.java

import com.google.gson.annotations.SerializedName;
public class TokenObject {
    @SerializedName("token")
    private String token;
    public TokenObject(String token) {
        this.token = token;
    }
    public String getToken() {
        return token;
    }
    public void setToken(String token) {
        this.token = token;
    }
}

 

MySharedPreference

import android.content.Context;
import android.content.SharedPreferences;
public class MySharedPreference {
    private SharedPreferences prefs;
    private Context context;
    public static final String FIREBASE_CLOUD_MESSAGING = "fcm";
    public static final String SET_NOTIFY = "set_notify";
    public MySharedPreference(Context context){
        this.context = context;
        prefs = context.getSharedPreferences(FIREBASE_CLOUD_MESSAGING, Context.MODE_PRIVATE);
    }
    public void saveNotificationSubscription(boolean value){
        SharedPreferences.Editor edits = prefs.edit();
        edits.putBoolean(SET_NOTIFY, value);
        edits.apply();
    }
    public boolean hasUserSubscribeToNotification(){
        return prefs.getBoolean(SET_NOTIFY, false);
    }
    public void clearAllSubscriptions(){
        prefs.edit().clear().apply();
    }
}

 

Helper.java

public class Helper {
    public static final String PATH_TO_SERVER_TOKEN_STORAGE = "";
    public static final String TOKEN_TO_SERVER = "server_token";
}

 

This brings us to the end of this tutorial. I hope that you have learn something. Run your app and see for yourself.

You can download the code for this tutorial below. If you are having hard time downloading the tutorial, kindly contact me.

Remember to subscribe with your email address so that you will be among the first to receive my new android blog post once it is published.

Please if you love this tutorial, kindly download my android app – Complete Mathematics – in Google Play Store and let me know what you think about it.

6 Comments

    • Inducesmile

Add a Comment