Implement Your Application
Now we will start implementing the initialization logic and the reception handler for push messages.
Overall structure
As mentioned at the beginning of this tutorial, this tutorial explains how to implement the push notification feature by using a modified version of the FCM sample code provided by Google. The sample code in the tutorial is customized for Kii Cloud while the original sample code is available from the link below:
Source code: https://github.com/firebase/quickstart-android/tree/master/messaging
The FCM sample code and this tutorial implement the following features:
- Getting the FCM device token: The service implemented with the FCM library gets the device token.
- Receiving the push messages: the reception process is implemented as a Service, so you can implement some time-consuming process (e.g., the logic that will access the network).
- Detecting the device token refresh: the sample will re-register the device token when the token is refreshed on the FCM server.
Class structure
The following class diagram shows the implementation of the tutorial code.
The FCM sample code does not include the processes written in light green letters, surrounded by the dotted line, or with the dotted arrow. These processes and the process to check if the Google Play services are installed or not being implemented in this tutorial code in a similar way to that of the GCM sample code.
This structure is considered as a standard implementation, though you can configure classes such as those for the user interface according to the specification of your mobile app. If you change the class or package names, make sure to update the AndroidManifest.xml accordingly.
It is roughly divided into three classes.
MainActivity
The push notification feature should be initialized in this class at proper timing, such as when the app screen is processed. This tutorial simply processes it with the
onCreate()
method.Additionally, this class internally creates a subclass of BroadcastReceiver to reflect the completion notice from the
MyFirebaseInstanceIdService
class to the app screen.MyFirebaseMessagingService
This service processes push messages from the server. You can write the reception processing in
onMessageReceived()
.This tutorial implements a process which ends with receiving a message. In a real mobile app, you can display a message on the status bar or broadcast it to a class.
MyFirebaseInstanceIdService
This class re-registers a device token when Firebase refreshes it. You can detect a changed device token with the
onTokenRefresh()
method by running this service as a subclass ofFirebaseInstanceIdService
.This class acquires a device token and registers it to Kii Cloud like the MainActivity.
This class cannot display a result of re-registration because it is a service. The sample code displays a result on the screen by broadcasting it to
BroadcastReceiver
implemented in the MainActivity.
Android does not display a push message on the user interface. If you want to display it, you need to write a reception processing which displays the message on the status bar. You can start the main activity when the status bar is clicked by letting PendingIntent
start the activity.
Adding the initialization preparation code
Before we install the device token, the target Kii Cloud user must be logged in. The user login is required for binding the user with the device.
The following sample code shows how to process the user login. We will add the sample code in the MainActivity.onCreate()
method.
public class MainActivity extends AppCompatActivity {
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
if (!checkPlayServices()) {
Toast.makeText(MainActivity.this, "This application needs Google Play services", Toast.LENGTH_LONG).show();
return;
}
String username = "user1";
String password = "123ABC";
KiiUser.logIn(new KiiUserCallBack() {
@Override
public void onLoginCompleted(int token, KiiUser user, Exception exception) {
if (exception != null) {
Toast.makeText(MainActivity.this, "Error login user:" + exception.getMessage(), Toast.LENGTH_LONG).show();
return;
}
Toast.makeText(MainActivity.this, "Succeeded to login", Toast.LENGTH_LONG).show();
String fcmToken = FirebaseInstanceId.getInstance().getToken();
if (fcmToken == null) {
Toast.makeText(MainActivity.this, "Error FCM is not ready", Toast.LENGTH_LONG).show();
return;
}
boolean development = true;
KiiUser.pushInstallation(development).install(fcmToken, new KiiPushCallBack() {
@Override
public void onInstallCompleted(int taskId, Exception exception) {
if (exception != null) {
Toast.makeText(MainActivity.this, "Error push registration:" + exception.getLocalizedMessage(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "Succeeded push registration", Toast.LENGTH_LONG).show();
}
}
});
}
}, username, password);
}
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("PushTest", "This device is not supported.");
finish();
}
return false;
}
return true;
}
}
We are doing four things in the sample code:
- First, we check if the Google Play services are available with the
checkPlayServices()
method. For simplification, this tutorial implements a processing which only displays an error message and skips the initialization if the services are not available. Note that the FCM documentation recommends checking the availability of the services with theonCreate()
method andonResume()
method. - We process the Kii Cloud user login. We've created the user with the username
user1
and password123ABC
when we test the Android application in the previous step. If the user login fails, we display an error and skip the initialization. - We get an FCM device token with
FirebaseInstanceId.getInstance().getToken()
. This acquisition processing does not need to be asynchronous as it completes immediately. However, we need to do a null check on the return value as described in Null Check on the FCM Device Token below. The device in use has been automatically registered to Google's FCM server by the Firebase library. The registration result is kept as a device token that can be obtained with this method. - We associate the acquired device token with the currently logged-in user and register the pair to Kii Cloud by calling the
install()
method after thepushInstallation()
method. The user needs to be logged in at this timing. The above code initializes the push notification in the development environment. If the mobile app is a production version, setdevelopment
to false.
The checkPlayServices()
method uses the check processing as is in the GCM sample for the Google Play services. Be careful that it terminates the mobile app with the finish()
method.
The above sample code is for the Kii Cloud SDK. If you use the Thing-IF SDK, try running this code first and then move to the Modification for Thing-IF section which is a part of the steps for push notification implementation in Android apps with the Thing-IF SDK.
In a real application, we would implement this logic as follows:
Check the Google Play services availability when the application launches and returns.
Get and register a device token to Kii Cloud when a user logs in, a pseudo user is registered, a login with an access token completes, or at any timing after any of those events.
Null check on the FCM device token
The service implemented with the FCM library gets an FCM device token from the Firebase server. This processing is executed in parallel with the screen processing and finishes immediately. However, it might return a null because the token is not acquired from the Firebase server in time if the getToken()
method is called just after the mobile app starts.
In case FirebaseInstanceId.getInstance().getToken()
returns a null, add a processing to retry using a timer and so on as required. If you cannot play with the sample code because it gets a null, try using a handler called by clicking a button to adjust the timing to get a device token.
Adding the device token refresh handling code
We can handle the case when the FCM server refreshes the device token with the InstanceIDListenerService
.
In some occasions, the FCM server refreshes the device token. If this happens, the device needs to get a new token and register it to Kii Cloud.
The token refreshing can be detected as follows. We implement the logic as a new subclass of the FirebaseInstanceIdService
.
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
public static final String INTENT_PUSH_REGISTRATION_COMPLETED = "com.example.pushtest.COMPLETED";
public static final String PARAM_ERROR_MESSAGE = "ErrorMessage";
@Override
public void onTokenRefresh() {
String fcmToken = FirebaseInstanceId.getInstance().getToken();
String error = null;
try {
boolean development = true;
KiiUser.pushInstallation(development).install(fcmToken);
} catch (Exception e) {
error = e.getLocalizedMessage();
}
Intent registrationComplete = new Intent(INTENT_PUSH_REGISTRATION_COMPLETED);
registrationComplete.putExtra(PARAM_ERROR_MESSAGE, error);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
}
A device token is registered to Kii Cloud again for the user when the device token refreshment is detected by the onTokenRefresh()
method. The logic is the same as the one after the user login is completed. The push notification re-registration is executed accordingly.
When the execution is done, we broadcast the completion notice with the name com.example.pushtest.COMPLETED
. We set the error message as the parameter (null if the initialization is successful). We will later implement the handler for receiving this broadcast.
An exception is thrown and the catch block is executed if the pushInstallation()
method is called without any logged-in user. You can retain the logged-in status by means such as Login with the Auto-Saved Credentials in case the logged-in status is lost because of process restart.
Adding the initialization completion handler
We will implement BroadcastReceiver
in the MainActivity to receive the result of re-registration of the device token from MyFirebaseInstanceIDService
.
When the push notification initialization in the MyFirebaseInstanceIDService
is completed, this event will be broadcasted with the name com.example.pushtest.COMPLETED
. We will write the reception handler for catching this broadcast.
You can implement the BroadcastReceiver
anywhere. This time, we implement it in the MainActivity by adding a new mRegistrationBroadcastReceiver
in the MainActivity.onCreate()
method. We also add the field declaration and the onResume()
and onPause()
methods.
public class MainActivity extends AppCompatActivity {
private BroadcastReceiver mRegistrationBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
mRegistrationBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String errorMessage = intent.getStringExtra(MyFirebaseInstanceIDService.INTENT_PUSH_REGISTRATION_COMPLETED);
if (errorMessage != null) {
Toast.makeText(MainActivity.this, "Error push registration:" + errorMessage, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "Succeeded push registration", Toast.LENGTH_LONG).show();
}
}
};
......
}
@Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
new IntentFilter(MyFirebaseInstanceIDService.INTENT_PUSH_REGISTRATION_COMPLETED));
}
@Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
super.onPause();
}
}
We are doing the following things in the sample code:
- We save the
mRegistrationBroadcastReceiver
as a class field. - We add the BroadcastReceiver implementation in the
onCreate()
method. Here, we are finalizing the push notification initialization. In the previous step, we've registered the error message in theErrorMessage
field as the Intent parameter. We are extracting the message with thegetStringExtra()
method and display the message (or the completion message). - We register the BroadcastReceiver in the
onResume()
method. Since we are associating thecom.example.pushtest.COMPLETED
with themRegistrationBroadcastReceiver
, the completion notice sent by theMyFirebaseInstanceIDService
will be received by this class. - We are destroying the registered BroadcastReceiver in the
onPause()
method.
Adding the reception handler
When a device receives an FCM push message, a subclass of the FirebaseMessagingService
will be called.
We will create a new Class as follows:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map<String, String> payload = remoteMessage.getData();
Bundle bundle = new Bundle();
for (String key : payload.keySet()) {
bundle.putString(key, payload.get(key));
}
ReceivedMessage message = PushMessageBundleHelper.parse(bundle);
KiiUser sender = message.getSender();
PushMessageBundleHelper.MessageType type = message.pushMessageType();
switch (type) {
case PUSH_TO_APP:
PushToAppMessage appMsg = (PushToAppMessage)message;
Log.d(TAG, "PUSH_TO_APP Received");
break;
case PUSH_TO_USER:
PushToUserMessage userMsg = (PushToUserMessage)message;
Log.d(TAG, "PUSH_TO_USER Received");
break;
case DIRECT_PUSH:
DirectPushMessage directMsg = (DirectPushMessage)message;
Log.d(TAG, "DIRECT_PUSH Received");
break;
}
}
}
In the sample code, we are recording a message in the debug log based on the type of the push message. In this tutorial, we check if the push message is successfully received by checking the recorded log.
Kii Cloud supports three types of push notifications (PUSH_TO_APP, PUSH_TO_USER, and DIRECT_PUSH). In this tutorial, we will test the push notification by sending a DIRECT_PUSH message from the developer portal.
See Showing a message on the status bar if you want to show a message on the status bar when the push notification is received.
Enhancement for displaying notifications on the screen
If you want to show the result of the push notification reception directly on the user interface, you might need to enhance the application structure. See Showing a message on the status bar if you just want to reflect the result on the status bar.
In this tutorial, the MyFirebaseMessagingService
class processes push messages, but you cannot control the user interface directly in a service.
If you want to show the reception result on the screen when the mobile app is active, you can use the Broadcast mechanism as we do with the initialization completion notice. Implement the BroadcastReceiver
in the class that will update the user interface, and delegate the UI operation to this class by sending a Broadcast from the service reception logic.
Note that since the message reception logic is running as a Service, you might receive a push message when there is no active Activity.
Running the application
When you are done with the implementation, run the application.
After you launch the application, you should be able to see two messages toasted on the display; the first message is "Succeeded to login" right after the user login is completed, and the second message is "Succeeded push registration" right after the push notification initialization is completed.
If you see an error or no message shows up, check your code with the debugger. Also, double check if the AndroidManifest.xml is edited correctly.
This is all for the implementation. Let us move to the next step: Send Test Messages to check if the push notification really works.
<< Configure the Manifest File | Send Test Messages >> |