Kushan Sameera
Kushan Sameera

Reputation: 249

Android notification using Firebase

I'm developing a android application and different users will be use it. I need to give some notification to a user when another user do some activity. I planed to do it with firebase cloud messaging(FCM). but i don't know how to detect the relevant device.

Upvotes: 1

Views: 1576

Answers (1)

Saurav Ghimire
Saurav Ghimire

Reputation: 568

Step 1: Register with Firebase Developers Console

In order to use FCM, you need to login to console. You should be given a choice on the right-hand side as to whether to create a new project or import an existing Google app into Firebase.

Click on Add App and then copy the google-services.json file that will get downloaded into yourapp/ dir. This file includes the project information and API keys needed to connect to Firebase. Make sure to preserve the filename, since it will be used in the next step.

Finally, add the Google services to your root build.gradle file's classpath:

buildscript {
  dependencies {
    // Add this line
    classpath 'com.google.gms:google-services:3.0.0'
  }
}

Add to your existing app/build.gradle at the end of the file:

apply plugin: 'com.google.gms.google-services'

Step 2 - Download Google Play Services

First, let's download and setup the Google Play Services SDK. Open Tools->Android->SDK Manager and check whether or not you have already downloaded Google Play Services under the Extras section. Make sure to update to the latest version to ensure the Firebase package is available.

Step 3 - Add Google Repository

Also open Tools->Android->SDK Manager and click on the SDK Tools tab. Make sure that under Support Repository you have installed the Google Repository. If you forget this step, you are not likely to be able to include the Firebase messaging library in the next step.

Step 4 - Update to SDK Tools

Also make sure to upgrade to SDK Tools 25.2.2. You may have Firebase authentication issues with lower SDK Tools version.

Step 6 - Import Firebase Messaging Library

Add the following to your Gradle file:

dependencies {
   compile 'com.google.firebase:firebase-messaging:9.4.0'
}

Step 7 - Implement a Registration Intent Service

You will want to implement an Intent Service, which will execute as a background thread instead of being tied to the lifecycle of an Activity. In this way, you can ensure that push notifications can be received by your app if a user navigates away from the activity while this registration process is occuring.

First, you will need to create a RegistrationIntentService class and make sure it is declared in your AndroidManifest.xml file and within the application tag:

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

Inside this class, you will need to request an instance ID from Google that will be a way to uniquely identify the device and app. Assuming this request is successful, a token that can be used to send notifications to the app should be generated too.

public class RegistrationIntentService extends IntentService {

    // abbreviated tag name
    private static final String TAG = "RegIntentService";

    public RegistrationIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Make a call to Instance API
        FirebaseInstanceId instanceID = FirebaseInstanceId.getInstance();
        String senderId = getResources().getString(R.string.gcm_defaultSenderId);
        try {
            // request token that will be used by the server to send push notifications
            String token = instanceID.getToken();
            Log.d(TAG, "FCM Registration Token: " + token);

            // pass along this data
            sendRegistrationToServer(token);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sendRegistrationToServer(String token) {
        // Add custom implementation, as needed.
    }
}

You will want to record whether the token was sent to the server and may wish to store the token in your Shared Preferences:

public static final String SENT_TOKEN_TO_SERVER = "sentTokenToServer";
  public static final String FCM_TOKEN = "FCMToken";

  @Override
  protected void onHandleIntent(Intent intent) {
      SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);

      // Fetch token here
      try {
        // save token
        sharedPreferences.edit().putString(FCM_TOKEN, token).apply();
        // pass along this data
        sendRegistrationToServer(token);
      } catch (IOException e) {
          Log.d(TAG, "Failed to complete token refresh", e);
          // If an exception happens while fetching the new token or updating our registration data
          // on a third-party server, this ensures that we'll attempt the update at a later time.
          sharedPreferences.edit().putBoolean(SENT_TOKEN_TO_SERVER, false).apply();
      }
  }

  private void sendRegistrationToServer(String token) {
      // send network request

      // if registration sent was successful, store a boolean that indicates whether the generated token has been sent to server
      SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
      sharedPreferences.edit().putBoolean(SENT_TOKEN_TO_SERVER, true).apply();
   }

You will want to make sure to dispatch this registration intent service when starting up your main activity. There is a Google Play Services check that may require your activity to be launched in order to display a dialog error message, which is why it is being initiated in the activity instead of application.

public class MyActivity extends AppCompatActivity {

  /**
     * Check the device to make sure it has the Google Play Services APK. If
     * it doesn't, display a dialog that allows users to download the APK from
     * the Google Play Store or enable it in the device's system settings.
     */
    private boolean checkPlayServices() {
        GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
        int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.SUCCESS) {
            if (apiAvailability.isUserResolvableError(resultCode)) {
                apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
                        .show();
            } else {
                Log.i(TAG, "This device is not supported.");
                finish();
            }
            return false;
        }
        return true;
    }

    @Override
    protected void onCreate() {
        if(checkPlayServices()) {
          Intent intent = new Intent(this, RegistrationIntentService.class);
          startService(intent);
        }
    }
}

Step 8 - Create a InstanceID ListenerService

According to this Google official documentation, the instance ID server issues callbacks periodically (i.e. 6 months) to request apps to refresh their tokens. To support this possibility, we need to extend from InstanceIDListenerService to handle token refresh changes. We should create a file called MyInstanceIDListenerService.java that will override this base method and launch an intent service for RegistrationIntentService to fetch the token:

public class MyInstanceIDListenerService extends FirebaseInstanceIdService {

    @Override
    public void onTokenRefresh() {
        // Fetch updated Instance ID token and notify of changes
        Intent intent = new Intent(this, RegistrationIntentService.class);
        startService(intent);
    }
}

You also need to add the service to your AndroidManifest.xml file within the application tag:

<service
  android:name="com.example.MyInstanceIDListenerService"
  android:exported="false">
  <intent-filter>
     <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
  </intent-filter>
</service>

Step 9 - Listening for push notifications

Let's define FCMMessageHandler.java that extends from FirebaseMessagingService that will process the message received:

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;

public class FCMMessageHandler extends FirebaseMessagingService {
    public static final int MESSAGE_NOTIFICATION_ID = 435345;

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Map<String, String> data = remoteMessage.getData();
        String from = remoteMessage.getFrom();

        Notification notification = remoteMessage.getNotification();
        createNotification(notification);
    }

    // Creates notification based on title and body received
    private void createNotification(Notification notification) {
        Context context = getBaseContext();
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.mipmap.ic_launcher).setContentTitle(notification.getTitle())
                .setContentText(notification.getBody());
        NotificationManager mNotificationManager = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(MESSAGE_NOTIFICATION_ID, mBuilder.build());
    }

}

We need to register the receiver class with FCM in the AndroidManifest.xml tagging the type of request (category) of the push:

<application
     ...>
         <!-- ... -->

         <activity
             ...>
         </activity>

         <service
            android:name=".FCMMessageHandler"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
         </service>

     </application>

</manifest>

Upvotes: 2

Related Questions