Receiving an NFC intent from outside of app

ThomasNasbyThomasNasby USMember ✭✭
edited June 2016 in Xamarin.Android

I'm working on an app that needs to read NFC tags. I have the app working correctly IF it is already open and is currently showing on the phone. However, when I scan an NFC with the Data scheme and host for the app, while the app is closed, it opens the NFC inside the Google Play store. I have a native Android version of this app and it will open the tags even if the app has been killed so I know it can be done..but I can't for the life of me get it to work on the Xamarin Android.

Currently the manifest is mostly blank except for NFC permissions, however I tried placing the intent filters directly there as well to no avail.

namespace RF.Droid
{
    [Activity(Label = "RF", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    [IntentFilter(new[] { "android.nfc.action.NDEF_DISCOVERED" },
    Categories = new[] { "android.intent.category.DEFAULT" },
    DataScheme = "http", DataHost = "myapp.com")]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
    {

        // For foreground nfc handling.
        PendingIntent nfcPendingIntent;
        IntentFilter[] nfcIntentFiltersArray;
        private NfcAdapter nfcAdapter;
        Uri nfcOneTimeURI = null;

        String TAG = "MainActivity";

        public object NFCUtil { get; private set; }

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            prepareForegroundNFCHandling();

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());


            // If we received an NFC intent, handle it after the rest of the content
            // is attached.
            if (isNfcIntent(Intent))
            {
                HandleNFC(Intent, false);
            }
        }

        protected override void OnResume()
        {
            base.OnResume();
            if (nfcAdapter != null)
            {
                nfcAdapter.EnableForegroundDispatch(this, nfcPendingIntent, nfcIntentFiltersArray, null /*nfcTechListsArray*/);
            }
        }

        //protected override void OnPause()
        //{
        //    base.OnPause();
        //    if (nfcAdapter != null)
        //    {
        //        nfcAdapter.DisableForegroundDispatch(this);
        //    }
        //}

        protected override void OnNewIntent(Intent intent)
        {
            DisplayMessage("New Intent received");

            var tag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;

            if (tag == null)
            {
                return;
            }

            if (NfcAdapter.ExtraTag.Contains("nfc"))
            {
                HandleNFC(intent, true);
            }

        }



        protected void HandleNFC(Intent intent, Boolean inForeground)
        {
            NdefMessage[] msgs = null;
            IParcelable[] rawMsgs = intent.GetParcelableArrayExtra(NfcAdapter.ExtraNdefMessages);

            if (rawMsgs != null)
            {
                msgs = new NdefMessage[rawMsgs.Length];
                for(int i = 0; i < rawMsgs.Length; i++)
                {
                    msgs[i] = (NdefMessage)rawMsgs[i];
                }

                if (msgs != null)
                {
                    for (int i = 0; i < msgs.Length; i++)
                    {
                        NdefRecord[] records = msgs[i].GetRecords();
                        if (records != null)
                        {
                            for (int j = 0; j < records.Length; j++)
                            {
                                Log.Info("MainActivity", "Record found");
                                if(NdefRecord.TnfWellKnown == records[j].Tnf) // && Array.Equals(records[j].GetType(), NdefRecord.RtdUri))
                                {
                                    // If we're a URI type, parse it.

                                    String uri = ParseUriRecord(records[j]);
                                    DisplayMessage(uri);
                                    var myintent = new Intent(this, typeof(RoomDetails));

                                    var bundle = new Bundle();
                                    bundle.PutString("uri", uri);
                                    myintent.PutExtras(bundle);

                                    this.StartActivity(myintent);

                                }

                            }
                        }
                    }
                }
            }
            else
            {
                DisplayMessage("No message");
            }
        }

        private bool isNfcIntent(Intent i)
        {
            return (NfcAdapter.ActionNdefDiscovered.Equals(i.Action));
        }
        public void DisplayMessage(string message)
        {
            Log.Info(TAG, message);
        }

        public static String ParseUriRecord(NdefRecord record)
        {
            byte[] payload = record.GetPayload();
            return Encoding.ASCII.GetString(payload);
        }

        private void prepareForegroundNFCHandling() {

            var intent = new Intent(this, GetType()).AddFlags(ActivityFlags.ClearTop|ActivityFlags.SingleTop);
            nfcAdapter = NfcAdapter.GetDefaultAdapter(Application.Context);

            // When an NFC tag is detected, Android will use the PendingIntent to come back to this activity.
            // The OnNewIntent method will invoked by Android.
            nfcPendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
            // Create an intent filter for when an NFC tag is discovered.  When
            // the NFC tag is discovered, Android will u
            var tagDetected = new IntentFilter(NfcAdapter.ActionTagDiscovered);
            var ndefDetected = new IntentFilter(NfcAdapter.ActionNdefDiscovered);
            var techDetected = new IntentFilter(NfcAdapter.ActionTechDiscovered);
            var nfcIntentFiltersArrays = new[] { tagDetected, ndefDetected, techDetected };

        }
    }
Tagged:

Answers

  • ThomasNasbyThomasNasby USMember ✭✭
    edited August 2016

    I've rewritten the app a bit but I believe my issue was not creating the app using the SingleTop flag. You'll notice I had it set for intents when I run prepareForegroundNFCHandling(), but obviously that only runs once the app is open, hence why NFC will work inside the app but not outside.

    To set it to the app itself, add it as a LaunchMode flag so that it's generated inside the Manifest, ex:
    [Activity(Label = "AppName", Icon = "@drawable/app_icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, LaunchMode = LaunchMode.SingleTop)]

  • WimDeVriendtWimDeVriendt BEMember

    Hi, I am new with xamarin and nfc. Can we get your sample project to learn from ?

  • WimDeVriendtWimDeVriendt BEMember

    Hi, I am new with xamarin and nfc. Can we get your sample project to learn from ?

Sign In or Register to comment.