Search code examples
androidxamarin.androidbroadcastreceiver

How do I use EventListener in BroadcastReceiver with custom Action in Xamarin.Android?


I'm trying to launch the TickEvent when BroadcastReceiver gets a custom message.

When intent.Action == Intent.ActionTimeTick, for example (instead of GRID_STARTED), it works fine. I commented out the working code, so you can experiment easily.

Seems like I'm missing an important thing about actions. Could you help me out, please?

using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V7.App;
using Android.Util;
using Android.Views;
using Android.Widget;

namespace TestApp
{
    [Activity(Label = "BroadcastUpdateFormExample", MainLauncher = true)]
    public class MainActivity : Activity
    {
        int count = 1;
        TextView txtNote;
        BroadcastMessageCatcher catcher;
        static readonly string TAG = "MainActivity";

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.activity_main);

            txtNote = FindViewById<TextView>(Resource.Id.textView1);

            Button button = FindViewById<Button>(Resource.Id.button1);

            button.Click += delegate
            {
                button.Text = string.Format("{0} clicks!", count++);
                BroadcastStarted();
            };

            catcher = new BroadcastMessageCatcher();
            catcher.TickEvent += delegate
            {
                Log.Debug(TAG, "before listener");
                txtNote.Text = "Tick";
                Log.Debug(TAG, "after listener");
            };
        }

        protected override void OnResume()
        {
            base.OnResume();
            // RegisterReceiver (catcher, new IntentFilter (Intent.ActionTimeTick));
            IntentFilter filter = new IntentFilter(BroadcastMessageCatcher.GRID_STARTED);
            RegisterReceiver(catcher, filter);
        }

        protected override void OnPause()
        {
            base.OnPause();
            UnregisterReceiver(catcher);
        }

        private void BroadcastStarted()
        {
            // Broadcast message to the view
            // Source: https://forums.xamarin.com/discussion/1147/updating-activity-using-a-background-service
            Intent BroadcastIntent = new Intent(this, typeof(BroadcastMessageCatcher));
            BroadcastIntent.SetAction(BroadcastMessageCatcher.GRID_STARTED);
            //BroadcastIntent.AddCategory(Intent.CategoryDefault);
            Log.Debug(TAG, "Broadcast started");
            SendBroadcast(BroadcastIntent);
        }
    }



    [BroadcastReceiver]
    public class BroadcastMessageCatcher : BroadcastReceiver
    {
        static readonly string TAG = "MainActivity";
        public static readonly string GRID_STARTED = "GRID_STARTED";
        public override void OnReceive(Context context, Intent intent)
        {
            // if (intent.Action == Intent.ActionTimeTick) 
            if (intent.Action == GRID_STARTED)
            {
                Log.Debug(TAG, "before event");
                // Here I get the error:
                TickEvent();
                Log.Debug(TAG, "after event");
            }
        }

        public delegate void TickEventHandler();
        public event TickEventHandler TickEvent;
    }
}

I'm getting the 'System.NullReferenceException: 'Object reference not set to an instance of an object.'' error on TickEvent(); inside the BroadcastMessageCatcher (marked in comments).


Solution

  • I understood actions wrong. Mostly because I didn't know that intent which initiates activities and intent which initiates actions are two completely different things.

    So if it helps anybody, I post the working code.

    Mainifest:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
              android:versionCode="1" 
              android:versionName="1.0" 
              package="com.companyname.testapp">
      <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
      <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity
         android:name=".MainActivity">
         <intent-filter>
             <action android:name="com.companyname.testapp.MY_CUSTOM_ACTION"></action>
             <category android:name="android.intent.category.DEFAULT"></category>
         </intent-filter>
        </activity>
      </application>
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    </manifest>
    

    Activity:

    using Android.App;
    using Android.Content;
    using Android.OS;
    using Android.Util;
    using Android.Widget;
    
    namespace TestApp
    {
        [Activity(Label = "BroadcastUpdateFormExample", MainLauncher = true)]
        public class MainActivity : Activity
        {
            int count = 1;
            TextView txtNote;
            BroadcastMessageCatcher catcher;
            static readonly string TAG = "FCMActivity";
    
            protected override void OnCreate(Bundle bundle)
            {
                base.OnCreate(bundle);
                // Set our view from the "main" layout resource
                SetContentView(Resource.Layout.activity_main);
    
                txtNote = FindViewById<TextView>(Resource.Id.textView1);
    
                Button button = FindViewById<Button>(Resource.Id.button1);
    
                button.Click += delegate
                {
                    button.Text = string.Format("{0} clicks!", count++);
                    BroadcastStarted();
                };
    
                catcher = new BroadcastMessageCatcher();
                catcher.TickEvent += delegate
                {
                    Log.Debug(TAG, "before listener");
                    txtNote.Text = "Tick";
                    Log.Debug(TAG, "after listener");
                };
            }
    
            protected override void OnResume()
            {
                base.OnResume();
                IntentFilter filter = new IntentFilter("com.companyname.testapp.MY_CUSTOM_ACTION");
                RegisterReceiver(catcher, filter);
            }
    
            protected override void OnPause()
            {
                base.OnPause();
                UnregisterReceiver(catcher);
            }
    
            private void BroadcastStarted()
            {
                // Broadcast message to the view
    
                Intent BroadcastIntent = new Intent();
                BroadcastIntent.SetAction("com.companyname.testapp.MY_CUSTOM_ACTION");
                //BroadcastIntent.AddCategory(Intent.CategoryDefault);
                Log.Debug(TAG, "Broadcast started");
                SendBroadcast(BroadcastIntent);
            }
        }
    
    
    
        [BroadcastReceiver]
        public class BroadcastMessageCatcher : BroadcastReceiver
        {
            private readonly string TAG = "MainActivity";
    
            public override void OnReceive(Context context, Intent intent)
            {
                if (intent.Action == "com.companyname.testapp.MY_CUSTOM_ACTION")
                {
                    Log.Debug(TAG, "before event");
                    TickEvent();
                    Log.Debug(TAG, "after event");
                }
            }
    
            public delegate void TickEventHandler();
            public event TickEventHandler TickEvent;
        }
    }