0

I'm following the tutorial here to set up Firebase Cloud Messaging for Android in my Xamarin.Forms application. This is an app that currently uses Google Cloud Messaging (GCM). I believe I followed all of the steps in the tutorial. However, my OnTokenRefresh method or OnMessageReceived method never fires.

In my Droid project, I have the packages Xamarin.Firebase.Common, Xamarin.Firebase.Iid, Xamarin.Firebase.Messaging, Xamarin.GooglePlayServices.Base, Xamarin.GooglePlayServices.Basement, Xamarin.GooglePlayServices.Tasks. I also have about 70 other packages (I didn't create this app). I removed any packages related to GCM, and removed GCM-related code. My google-services.json is set to GoogleServicesJson build action.

When I tried adding Console.Out.WriteLine("MainActivity InstanceID token: " + FirebaseInstanceId.Instance.Token); to MainActivity's OnCreate method to try to force the app to give me a token, it crashes with the error:

Java.Lang.IllegalStateException

Default FirebaseApp is not initialized in this process com.ebelinski.fakename. Make sure to call FirebaseApp.initializeApp(Context) first.

I've tried a bunch of different things and nothing seems to work. Below is my code:

WinterFirebaseIIDService.cs

using System.Collections.Generic;
using Android.App;
using WindowsAzure.Messaging;
using Firebase.Iid;

namespace FakeName.Droid {

  [Service]
  [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
  public class WinterFirebaseIIDService: FirebaseInstanceIdService {

    NotificationHub hub;

    public override void OnTokenRefresh() {
      var refreshedToken = FirebaseInstanceId.Instance.Token;
      AppLog.Checkpoint("WinterFirebaseIIDService FCM token: " + refreshedToken);
      SendRegistrationToServer(refreshedToken);
    }

    void SendRegistrationToServer(string token) {
      // Register with Notification Hubs
      hub = new NotificationHub(AppConstants.NOTIFICATION_HUB_NAME,
                                AppConstants.NOTIFICATION_HUB_CONNECTION, this);

      var tags = new List<string>() { };
      var regID = hub.Register(token, tags.ToArray()).RegistrationId;

      AppLog.Checkpoint("WinterFirebaseIIDService Successful registration of ID " + regID);
    }

  }

}

WinterFirebaseMessagingService

using System.Linq;
using Android.App;
using Firebase.Messaging;

namespace FakeName.Droid {

  [Service]
  [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
  public class WinterFirebaseMessagingService: FirebaseMessagingService {

    public override void OnMessageReceived(RemoteMessage message) {
      AppLog.Checkpoint("WinterFirebaseMessagingService From: " + message.From);
      if (message.GetNotification() != null) {
        //These is how most messages will be received
        AppLog.Checkpoint("WinterFirebaseMessagingService Notification Message Body: " + message.GetNotification().Body);
        SendNotification(message.GetNotification().Body);
      }
      else {
        //Only used for debugging payloads sent from the Azure portal
        SendNotification(message.Data.Values.First());

      }

    }

    void SendNotification(string messageBody) {
    }

  }

}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ebelinski.fakename" android:versionName="1.1" android:versionCode="15">
    <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="28" />
    <application android:label="Fake Name">
        <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" 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>
    </application>
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

AndroidMaifest.xml (the version in /Droid/obj/Debug/android/)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ebelinski.fakename" android:versionName="1.1" android:versionCode="15">
  <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="28" />
  <permission android:name="com.ebelinski.fakename.permission.C2D_MESSAGE" />
  <uses-permission android:name="com.ebelinski.fakename.permission.C2D_MESSAGE" />
  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
  <uses-permission android:name="android.permission.GET_ACCOUNTS" />
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  <uses-feature android:name="android.hardware.location.gps" />
  <uses-feature android:name="android.hardware.location.network" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <application android:label="Fake Name" android:name="android.support.multidex.MultiDexApplication" android:allowBackup="true" android:icon="@drawable/icon" android:debuggable="true">
    <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" 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="com.ebelinski.fakename" />
      </intent-filter>
    </receiver>
    <activity android:configChanges="orientation|screenSize" android:icon="@drawable/icon" android:label="Fake Name" android:launchMode="singleTop" android:theme="@style/CustomTheme" android:name="md501b8064acb9363ced7fe7144b683c1bc.MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:configChanges="orientation|screenSize" android:label="Notification Activity" android:name="md501b8064acb9363ced7fe7144b683c1bc.NotificationActivity" />
    <service android:name="md501b8064acb9363ced7fe7144b683c1bc.BackgroundService">
      <intent-filter>
        <action android:name="FakeName.Droid.BackgroundService" />
      </intent-filter>
    </service>
    <service android:name="md501b8064acb9363ced7fe7144b683c1bc.WinterFirebaseIIDService">
      <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
      </intent-filter>
    </service>
    <service android:name="md501b8064acb9363ced7fe7144b683c1bc.WinterFirebaseMessagingService">
      <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
      </intent-filter>
    </service>
    <receiver android:enabled="true" android:exported="false" android:name="md51558244f76c53b6aeda52c8a337f2c37.PowerSaveModeBroadcastReceiver" />
    <provider android:name="mono.MonoRuntimeProvider" android:exported="false" android:initOrder="2147483647" android:authorities="com.ebelinski.fakename.mono.MonoRuntimeProvider.__mono_init__" />
    <!--suppress ExportedReceiver-->
    <receiver android:name="mono.android.Seppuku">
      <intent-filter>
        <action android:name="mono.android.intent.action.SEPPUKU" />
        <category android:name="mono.android.intent.category.SEPPUKU.com.ebelinski.fakename" />
      </intent-filter>
    </receiver>
    <provider android:authorities="com.ebelinski.fakename.firebaseinitprovider" android:name="com.google.firebase.provider.FirebaseInitProvider" android:exported="false" android:initOrder="100" />
    <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" />
        <category android:name="com.ebelinski.fakename" />
      </intent-filter>
    </receiver>
    <!-- Internal (not exported) receiver used by the app to start its own exported services
             without risk of being spoofed. -->
    <!-- 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>
    <!-- FirebaseMessagingService performs security checks at runtime,
             no need for explicit permissions despite exported="true" -->
    <service android:name="com.google.firebase.messaging.FirebaseMessagingService" android:exported="true">
      <intent-filter android:priority="-500">
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
      </intent-filter>
    </service>
    <activity android:name="com.google.android.gms.common.api.GoogleApiActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:exported="false" />
    <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
  </application>
  <permission android:name="com.ebelinski.fakename.permission.C2D_MESSAGE" android:protectionLevel="signature" />
  <uses-permission android:name="com.ebelinski.fakename.permission.C2D_MESSAGE" />
</manifest>

MainActivity.cs

using Android.App;
using Android.Content.PM;
using Android.OS;
using System.Globalization;
using Xamarin;
using FakeName.Infrastructure;
using FakeNameDroid.Infrastructure;
using Android.Content;
using System;
using FakeNameMessaging;
using FakeNameModels;
using Firebase.Iid;
using Firebase.Messaging;
using Firebase;

namespace FakeName.Droid
{
  [Activity (Label = "FakeName", LaunchMode = LaunchMode.SingleTop, Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, Theme = "@style/CustomTheme")]
  public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
  {

    // This will be set to true in the OnNewIntent method to notify the app should navigate to the notifications page after launching
    private bool navigateToNotifications = false;

    // Google API Project Number
    public static MainActivity instance;

    protected override void OnCreate (Bundle bundle)
    {
      AppLog.Checkpoint("MainActivity OnCreate");

      instance = this;
      base.OnCreate (bundle);

      AppLog.Checkpoint("MainActivity Initializing Xamarin Forms");
      global::Xamarin.Forms.Forms.Init (this, bundle);

      // Set up the Translation service based on the current culture
      Translate.Initialize (CultureInfo.CurrentCulture.TwoLetterISOLanguageName);

      SettingsPageModel.ShowManageNotificationsButton = true;


      try
      {
        string notificationData = null;
        if(bundle != null) {
          notificationData = bundle.GetString("notification");
        } else {
          notificationData = Intent.GetStringExtra("notification");
        }

        if (!string.IsNullOrEmpty(notificationData)) {
          AppLog.Checkpoint("MainActivity Notification Data Present in bundle");
          navigateToNotifications = true;
        } else {
          AppLog.Checkpoint("MainActivity Notification Data not present in bundle");
        }
      }
      catch (Exception ex)
      {
        AppLog.Checkpoint("MainActivity Exception getting notification data OnCreate");
        AppLog.CaptureException ("MainActivity CheckForNotificationData", ex);
      }

      FillIoCContainer();
      CreateNotificationChannel();

      if (navigateToNotifications) {
        LoadApplication (new App (NavigationDetailPage.Notifications));
        // Reset flag
        navigateToNotifications = false;
      } else {
        LoadApplication (new App ());
      }

      // NOTE: I added this even though it didn't say so in the tutorial...it doesn't work with or without it...
      FirebaseApp app = FirebaseApp.InitializeApp(Android.App.Application.Context);
    }

    protected override void OnResume ()
    {
      base.OnResume();

      AppLog.Checkpoint("MainActivity OnResume");

      // NOTE: This is where the app crashes. It crashes even if I move this line to OnCreate.
      Console.Out.WriteLine("MainActivity InstanceID token: " + FirebaseInstanceId.Instance.Token);

      if (navigateToNotifications) {
        AppLog.Checkpoint("MainActivity Navigating to notifications");
        GalaSoft.MvvmLight.Messaging.Messenger.Default.Send (new NavigationMessageEvent (NavigationDetailPage.Notifications));
        navigateToNotifications = false; // reset the value
      }
    }

    /* 
     * The intent that calls MainActivity from a notification is actually a different intent than the one we built and won't 
     * have the extra data we put in it. This method 'OnNewIntent' catches our intent that was tied to that notification so
     * we can get the extra data we put in it when the user click it.
     */
    protected override void OnNewIntent(Intent intent)
    {
      base.OnNewIntent(intent);
      AppLog.Checkpoint("MainActivity OnNewIntent called");

      try
      {
        var notificationData = intent.GetStringExtra("notification");
        if (!string.IsNullOrEmpty(notificationData)) {
          AppLog.Checkpoint("MainActivity Notification Data Present");
          navigateToNotifications = true;
        }
      }
      catch (Exception ex)
      {
        AppLog.Checkpoint("MainActivity Exception getting notification ");
        AppLog.CaptureException ("CheckForNotificationData", ex);
      }
    }

    private void CreateNotificationChannel() {
      if (Build.VERSION.SdkInt >= BuildVersionCodes.O) {
        string name = "Snow emergency notifications";
        string description = "This notification channel contains snow emergency notifications.";
        NotificationImportance importance = NotificationImportance.Max;
        string channelID = AppConstants.NOTIFICATION_CHANNEL_SNOW_EMERGENCIES_ID;

        NotificationChannel channel = new NotificationChannel(channelID, name, importance);
        channel.Description = description;

        NotificationManager notificationManager = (NotificationManager) GetSystemService(NotificationService);
        notificationManager.CreateNotificationChannel(channel);
      }
    }

    public void FillIoCContainer ()
    {
      AppLog.Checkpoint("MainActivity Filling IoC");
      IoCContainer.Instance.AddInstance<INetworkConnectionMonitor> (new NetworkConnectionMonitor ());
    }
  }
}
Eugene
  • 3,417
  • 5
  • 25
  • 49
  • Do you already add google-services.json ? – albilaga Dec 19 '18 at 20:56
  • Yes, I added google-services.json to my Droid project and set the build action for that file to GoogleServicesJson. – Eugene Dec 19 '18 at 20:57
  • what is the xamarin.firebase.messaging you used? is the latest one? – albilaga Dec 19 '18 at 21:06
  • Yep, all of my packages are up-to-date. My Xamarin.Firebase.Messaging package is 60.1142.1. – Eugene Dec 19 '18 at 21:12
  • Review your final manifest (the one generated in the builds OBJ dir or extract it from the final APK) and ensure things like ${applicationId} have been expanded... also review the `logcat` as firebase will output logging info and when/why things are initialized or not – SushiHangover Dec 19 '18 at 21:32
  • @SushiHangover I have updated my question with the manifest in `/Droid/obj/Debug/android/`. In LogCat I found this error: `Info (31569) / FirebaseInitProvider: FirebaseApp initialization unsuccessful`. But I couldn't find info as to why this is unsuccessful. – Eugene Dec 20 '18 at 00:34
  • 1
    @Eugene If you are getting "FirebaseApp initialization unsuccessful", the number one cause is your google-services.json does not have the correct build action assigned or a mismatch in the app id, see my answer here: https://stackoverflow.com/questions/42158239/getting-exception-using-firebase-in-xamarin-android/42159446#42159446 – SushiHangover Dec 20 '18 at 01:18
  • @SushiHangover You are right! I realized that the developer before me, who added the Android app to the Firebase project, set the application package name with a slight typo on it. Thank you! – Eugene Dec 20 '18 at 03:37

1 Answers1

1

As it turns out, the reason for this happening was because my application package name in my Android project and the one in my google-services.json file were very slightly different, when they really should have been identical.

Eugene
  • 3,417
  • 5
  • 25
  • 49
  • 1
    Thanks, i had same issue and the problem was package name was lower case while google-services.json had package name in capital letter – Esam Sherif Dec 08 '19 at 09:17