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.


31 comments :

  1. After editing the android manifest file, I created a MyFirebaseMessagingService.java under src/packagename/. When i copied the codes from https://github.com/firebase/quickstart-android/blob/master/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/MyFirebaseMessagingService.java
    i get the following errors
    - The import com.google.firebase.messaging cannot be resolved
    - FirebaseMessagingService cannot be resolved to a type
    - RemoteMessage cannot be resolved to a type
    - The constructor Intent(MyFirebaseMessagingService, Class) is undefined
    - The method getActivity(Context, int, Intent, int) in the type PendingIntent is not applicable for the arguments - (MyFirebaseMessagingService, int, Intent, int)
    - The constructor NotificationCompat.Builder(MyFirebaseMessagingService) is undefined
    - The method getSystemService(String) is undefined for the type MyFirebaseMessagingService
    How should i solve these problems in MyFirebaseMessagingService.java

    ReplyDelete
    Replies
    1. It probably means you haven't imported the respective libraries from GitHub into your workspace (I see messaging, iid, support-v4 classes) or you haven't attach firebase-messaging to your Android app project in project Properties > Android > Library section (down).

      Delete
    2. Thanks! the MyFirebaseMessagingService.java is working now. However after making changes to Mainactivity.java.
      When i run the app and click the log token button. I get the following errors
      - failed to resolve REGISTER intent, falling back.
      - Both Google Play Services and legacy GSF package are missing
      - InstanceID Token: null

      How should i fix this?

      Delete
    3. I would make sure to use the latest Google Play Services and Firebase packages (10.0.0 as of now, 10.0.1 should be just a minor fix, soon to be available) and test it on a device that does have Google Play Services installed.

      From https://firebase.google.com/docs/cloud-messaging/android/client
      "FCM clients require devices running Android 2.3 or higher that also have the Google Play Store app installed, or an emulator running Android 2.3 with Google APIs. Note that you are not limited to deploying your Android apps through Google Play Store."

      Delete
    4. How to install google play services on the emulator? I am using android 4.1.2

      Delete
    5. You need to start practicing your google.com and stackoverflow.com search skills :-) As it says in the earlier message "or an emulator running Android 2.3 (or higher) with Google APIs".

      This is an article from Intel going through creating an AVD with Google APIs(as they've helped providing images optimized for working on Windows and Intel CPUs), the screenshots are from a Mac, but the programs are the same really (run "ANDROID_SDK\SDK Manager.exe" and "ANDROID_SDK\AVD Manager.exe").

      You will need to install Intel HAXM either from SDK Manager (download from "Extras\Intel X86 Emulator Accelerator" then go and install ANDROID_SDK\extras\intel") or from Intel's website, it's linked in the article.

      Now Available: Android SDK x86 System Image with Google APIs
      https://software.intel.com/en-us/blogs/2014/03/06/now-available-android-sdk-x86-system-image-with-google-apis

      Delete
  2. Why is my adblogcat not showing anything?

    ReplyDelete
  3. Hi! thanks for the information. However when i run the app and click on the log token, the token is generated. I copied the token and pasted into the firebase console registration token. However, after sending the message my android emulator is not receiving any notifications. How should i fix it?

    ReplyDelete
    Replies
    1. Have a look at the firebase/quickstart-android GitHub project mentioned in the post at the end, see the two services at the end of AndroidManifest.xml, you need to implement something along those lines. https://github.com/firebase/quickstart-android/tree/master/messaging/app/src/main

      They're also mentioned in the official documentation (linked in the article before the Testing section): https://firebase.google.com/docs/cloud-messaging/android/client#manifest

      You may be right to ask "why didn't you fully implement it here so we can see?" Because it was meant to tackle just the missing part for Eclipse, libraries and generated parts of the manifest, the rest of it should be just as documented and provided by Google.

      PS: Sorry, your comment went into Spam for some reason and could not see it until now.

      Delete
  4. I created the app as per your instructions but I get this error in my logcat. Any idea whi this might be happening W/System.err(20824): remove failed: ENOENT (No such file or directory) : /data/user/0/com.example.ap/shared_prefs/com.google.android.gms.appid.xml.bak

    ReplyDelete
    Replies
    1. By the look of it tries to delete a shared preferences backup file that doesn't exist. You didnd't provide a stacktrace so I can't decompile a class to look at it and see what it does, but I would assume maybe you're running this on an emulator without external storage, or missing permission to write on external storage, not sure what's going on. I would suggest opening a new thread on StackOverflow with more details about it. I don't think this is a problem related to the libraries themselves or the directions suggested in this post.

      Delete
  5. Hey, thanks for this tutorial. I tried it with IntelliJ, not using gradle.

    without calling FirebaseApp.initializeApp i get the error:
    Default FirebaseApp is not initialized in this process

    when calling FirebaseApp.initializeApp the following occurs:
    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/android/gms/R$string;
    at com.google.android.gms.common.internal.zzam.(Unknown Source)
    at com.google.firebase.FirebaseOptions.fromResource(Unknown Source)
    at com.google.firebase.FirebaseApp.initializeApp(Unknown Source)

    what's going wrong here?

    ReplyDelete
    Replies
    1. com.google.android.gms.R$string is in google-play-services-basement.

      If you go to dandar3/android-google-firebase-README project on GitHub (linked in article above) you can see all the libraries you need grouped by Firebase library.

      I would suggest using Eclise to download an entire set easily (see the video explaining how to download an entire project set), then import in IntelliJ all projects from the Eclipse workspace.

      Delete
    2. thanks for pushing me to the right direction, it's working now, i had to add the corresponding projects in my project.properties as android.library.reference and add the jars to my project libs folder

      Delete
    3. Glad to hear you got it working, well done :-)

      Delete
  6. Hi Dan, Thanks for your information! Really very useful integration. Anywhere github you pushed this code? i need to setup in sencha project, so if i can able to see how you added in sample project then its very useful to me. Thanks b vinoth

    ReplyDelete
  7. Hi Dan, Few errors i m getting, please can you help this AndroidManifest.xml:86: error: Error: No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version')

    ReplyDelete
  8. Dear Dan, Please can you helpthis, i m getting error in AndroidManifest.xml:86: error: Error: No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version').

    ReplyDelete
    Replies
    1. That is part of the google-play-services-basement project (see its res\values\values.xml)
      https://github.com/dandar3/android-google-play-services-README

      Delete
    2. Thanks for reply! Thanks lot! I have added those files in lib folder
      android-support-annotations.jar
      android-support-compat.jar
      android-support-core-ui.jar
      android-support-core-utils.jar
      android-support-fragment.jar
      android-support-media-compat.jar
      android-support-v4.jar
      google-firebase-common.jar
      google-firebase-iid.jar
      google-firebase-messaging.jar
      google-play-services-basement.jar
      google-play-services-tasks.jar

      But still getting error R.java file also deleted and getting reference i m getting error in AndroidManifest.xml:86: error: Error: No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version').

      Delete
    3. if you don't mine can you share any sample integration source ?

      Delete
    4. You need to bring those entire projects into your workspace, whatever IDE you are using - you need the JARs as well as the resources from those projects (res folders). You can't just lump the XML resources into your project as they would overwrite, each library project as it's own res\values\values.xml that would simply overwrite each other. The way to do it is to keep them or import them as separate library projects and add them as library references, see this screenshot as an example (your app project > Properties > Android page > Libraries section). https://developers.helpshift.com/static/books/android/eclipse-helpshiftsdk-reference.png

      Delete
    5. I'll see if I can make a short video or a post with screenshots to show you how to add the libraries as dependencies to your app for Eclipse, they seem to be harder to find nowadays with Google removing those older references from their documentation.

      Delete
  9. Hi Dan,
    Can you please help me
    i just want to integrate FCM into eclipse using Cordova

    i am facing this error:
    java.lang.ClassNotFoundException: Didn't find class "com.google.firebase.messaging.FirebaseMessaging" on path: DexPathList[[zip file "/data/app/io.cordova.hellocordova-2/base.apk"],nativeLibraryDirectories=[/data/app/io.cordova.hellocordova-2/lib/arm64, /vendor/lib64, /system/lib64]]
    03-29 18:29:15.053: E/AndroidRuntime(27398): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)

    Please help me

    ReplyDelete
    Replies
    1. I'm sorry I haven't used these with Apache Cordoba, but if I were to guess from the error message com.google.firebase.messaging.FirebaseMessaging is missing from your APK - it can be found in google-firebase-messaging.jar meaning, so I would think somehow google-firebase-messaging project is not set as a dependency / library for your project...

      Delete
  10. Hi
    Two simple questions:
    1. Is the Support-v4.jar required?
    2. The guide of firebase stated the prerequisite is Android 4.0. Will it got crash if running on Android 3.x ?

    ReplyDelete
    Replies
    1. Sorry I missed your questions.

      1. Yes, google-firebase-messaging depends on google-play-services-basement and that depends on android-support-v4. You can see that in the link below and at the bottom there are a couple of videos showing you how to download them all down easily using Team Project Sets.
      https://github.com/dandar3/android-google-firebase-README

      2. It probably will crash on older APIs, newer versions use minSdkVersion="14", see their AndroidManifest.xml.
      Version 10.0.1 is the last one to support API 9 (again, you can check their AndroidManifest.xml)
      https://github.com/dandar3/android-google-play-services-README/tree/10.0.1

      Also see the release notes:
      https://developers.google.com/android/guides/releases#november_2016_-_version_100

      "Android version 2.3.x (Gingerbread) Deprecation

      Google Play services 10.0.x is the final release that includes full support for Android version 2.3.x (Gingerbread). Apps developed using future SDK releases after 10.0.x will not be able to connect to Google Play services on Android Gingerbread devices. To learn more about your options, including building multiple APKs to extend your app's support for Android Gingerbread, see the Android Developers Blog."

      Delete
  11. Hey Dan,

    have you ever done a release build with firebase messaging libs included? I have massive problems with proguard when obfuscating. It throws a lot of Warnings like

    Warning: com.google.android.gms.internal.zzw$zza: can't find superclass or interface org.apache.http.client.methods.HttpEntityEnclosingRequestBase
    Warning: com.google.android.gms.internal.zzac: can't find referenced class android.net.http.AndroidHttpClient
    Warning: com.google.android.gms.internal.zzac: can't find referenced class android.net.http.AndroidHttpClient

    Ant build can not be finished with: Please correct the above warnings first.

    ReplyDelete
    Replies
    1. I created an issue on GitHub and I started to answer it there - it will only get you over the first one reported, but we can take it further there. https://github.com/dandar3/android-google-firebase-README/issues/4

      Delete
  12. Hi Dan, thank you for your amazing work, I just want to know that are you going to port firebase-database to eclipse soon?

    ReplyDelete
    Replies
    1. Thank you, Linh, you're quite welcome. I certainly can look into it, keep an eye on android-google-firebase-README next few days.

      Delete