Sunday, January 15, 2017

Eclipse: Integrating Firebase Cloud Messaging into your Android app

Following requests from some of you on the previous article on Firebase Analytics, today we're going to cover Firebase Cloud Messaging integration in your application developed with Eclipse. It is not much different from the Firebase Analytics integration, so here we go.


Firebase Analytics libraries


dandar3/android-google-firebase-README gives you an overview of the Firebase libraries available to use with your Eclipse project. Have a look at the two YouTube videos at the end covering the installation of the Subversive plugin and the steps to easily import entire project sets like dandar3/android-google-firebase-messaging in your workspace.

You will observe compile errors like Tag ... attribute name has invalid character '$' after importing libraries like android-google-firebase-iid or android-google-firebase-common. To resolve the issue you will have to manually replace ${applicationId} with your application id (Java package) in all AndroidManifest.xml files (use CTRL+H > File Search >  Replace...)






Importing google-services.json


Read the Firebase documentation and follow the Processing the JSON File information to manually generate your own values.xml

If you're having trouble with getting this right you could use Android Studio to import firebase/quickstart-android GitHub project, deploy your google-services.json into it and then copy the generated values.xml out.
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="default_web_client_id" translatable="false">{YOUR_CLIENT}/oauth_client/[first client_type 3]</string>
    <string name="gcm_defaultSenderId"   translatable="false">project_info/project_number</string>
    <string name="firebase_database_url" translatable="false">project_info/firebase_url</string>
    <string name="google_app_id"         translatable="false">{YOUR_CLIENT}/client_info/mobilesdk_app_id</string>
    <string name="google_api_key"        translatable="false">{YOUR_CLIENT}/services/api_key/current_key</string>
    <string name="google_storage_bucket" translatable="false">project_info/storage_bucket</string>
</resources>


AndroidManifest.xml changes


This is the undocumented part that can only be observed by looking at the AndroidManifest.xml generated by Google Services Gradle plugin (app\intermediates\manifests\full\debug\AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest>
    [...]

    <!-- Required permission for App measurement to run. -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission
        android:name="${applicationId}.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" /> 
 
    <application>
        [...]
 
        <receiver
            android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
            android:enabled="true"
            android:exported="false" >
        </receiver> 

        <receiver
            android:name="com.google.android.gms.measurement.AppMeasurementInstallReferrerReceiver"
            android:enabled="true"
            android:permission="android.permission.INSTALL_PACKAGES" >
            <intent-filter>
                <action android:name="com.android.vending.INSTALL_REFERRER" />
            </intent-filter>
        </receiver>

        <service
            android:name="com.google.android.gms.measurement.AppMeasurementService"
            android:enabled="true"
            android:exported="false" />

        <receiver
            android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <category android:name="${applicationId}" />
            </intent-filter>
        </receiver>

        <!--
            Internal (not exported) receiver used by the app to start its own exported services
            without risk of being spoofed.
          -->
        <receiver
            android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
            android:exported="false" /> 

        <!-- 
            FirebaseInstanceIdService performs security checks at runtime,
            no need for explicit permissions despite exported="true"
          -->
        <service
            android:name="com.google.firebase.iid.FirebaseInstanceIdService"
            android:exported="true" >
            <intent-filter android:priority="-500" >
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
        </service>

        <provider
            android:name="com.google.firebase.provider.FirebaseInitProvider"
            android:authorities="${applicationId}.firebaseinitprovider"
            android:exported="false"
            android:initOrder="100" />

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        </application>

</manifest>

On top of that you will have to add your own FirebaseMessagingService and FirebaseInstanceIdService implementations to handle messages and registration tokens as detailed in Firebase documentation > Set Up a Firebase Cloud Messaging App on Android.

You can find a working sample on GitHub in firebase/quickstart-android in the messaging module. I would recommend reading the module README.md, app/src/main/AndroidManifest.xml and the related classes in /app/src/main/java/com/google/firebase/quickstart/fcm/


Testing

 

If everything was set up correctly and you have implemented a similar logic to the classes mentioned in the above documentation and working sample, you can subscribe through the app to a topic and have messages sent to those subscribed that way (see Send Tipic Messages with Firebase Console) or you can obtain the registration token and send messages to that one device (see Send a Notification to an Android Device).


Here is a test using the sample app. Push Log Token button to get the registration token in logcat:
D/MainActivity( 1983): InstanceID Token: cNtIOWfDiJY:APA91bFat1GNW7yEAj5HdSzsQsPsn-CzN6KtxxzT0h41lo_w5Hg2p8_PJVAgY7hWx_2RrHSmq8Bc7emA_NDXqHEVfzj8hqnbucnJKSeMU48jS2DEdJLcM5eYha3KLIIRvwOie17voHJJ



which then you can use to send messages from Firebase Console to:
D/MyFirebaseMsgService( 1983): From: 348775509088
D/MyFirebaseMsgService( 1983): Message Notification Body: Hello from Firebase Console!
See sendNotification() and the note at the end of MyFirebaseMessagingService.java if you want to transform the message into a system notification.