IntentFilter on NFC - Xamarin - C#

2.9k views Asked by At

The context is: Framework 4.5, Xamarin.Android v5.0

I want to use the NFC technology to implement shortcuts for my application users. I want the users to scan an NFC tag so they just have to put a value to a predefined scheme.

I have put some arguments in my NFC message and I do that when I write the message on my NFC tag:

    var tag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
    var ndef = Ndef.Get(tag);
            NdefRecord external = NdefRecord.CreateExternal(applicationPackageName(), "letypetype", Encoding.ASCII.GetBytes("param"));
            NdefRecord appRecord = NdefRecord.CreateApplicationRecord(applicationPackageName());
            NdefMessage ndefMessage = new NdefMessage(external, appRecord);
    if (ndef != null)
    {
        ndef.Connect();
        ndef.WriteNdefMessage(ndefMessage);
    }

Then, I want to use it on my application, so I have put it in AndroidManifest.xml this:

<uses-feature android:name="android.hardware.nfc" android:required="true" />

And on my main activity I have the following intent filter:

     [IntentFilter(new[] { NfcAdapter.ActionNdefDiscovered },
      Categories = new[] { Intent.CategoryDefault },
      DataScheme = "vnd.android.nfc", DataPathPrefix = "letypetype",
     DataHost = "ext")]
        public class Activity1 : Activity
       { ...

And I try to handle my parameter in this activity with the override method OnResume:

    protected override void OnResume()
    {
        base.OnResume();
        if (NfcAdapter.ActionNdefDiscovered.Equals(this.Intent.Action))
        {
            IParcelable[] rawMsgs = this.Intent.GetParcelableArrayExtra(NfcAdapter.ExtraNdefMessages);
            if (rawMsgs != null)
            {
                NdefMessage[] msgs = new NdefMessage[rawMsgs.Length];
                for (int i = 0; i < rawMsgs.Length; i++)
                {
                    msgs[i] = (NdefMessage)rawMsgs[i];
                }
            }
        }
    }

But there is no way to get it back. So I'm pretty sure I do something wrong but I don't know what.

2

There are 2 answers

0
Majkl On

If I well understand the question (if not correct me please), the problem is reading the data from the tag? Try the first simple reading through EnableForegroundDispatch and OnNewIntent and then customize for your needs.

private NfcAdapter mNfcAdapter;

In Activity OnCreate

  mNfcAdapter = NfcAdapter.GetDefaultAdapter(this);

In Activity OnResume

if (mNfcAdapter != null)
    {
      var tagDetected = new IntentFilter(NfcAdapter.ActionTagDiscovered);//or try other Action type
      var filters = new[] { tagDetected };
      var intent = new Intent(this, GetType()).AddFlags(ActivityFlags.SingleTop);
      var pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
      mNfcAdapter.EnableForegroundDispatch(this, pendingIntent, filters, null);
    }

And overriede

    protected override void OnNewIntent(Intent intent)
{
  object obj = intent.GetParcelableExtra(NfcAdapter.ExtraTag);
  if (obj != null && obj is Tag)
  {
    Tag t = (Tag)obj;
    byte[] id = t.GetId();
    string[] techList = t.GetTechList();
    int con = t.DescribeContents();
    string objName = t.ToString();
  }
}

do not forget for in OnPause

if (mNfcAdapter != null) mNfcAdapter.DisableForegroundDispatch(this);

and OnDestroy

if (mNfcAdapter != null)
    {
      mNfcAdapter.Dispose();
      mNfcAdapter = null;
    }
0
Michael Roland On

The problem that you are facing is a result of your wrong intent filter for the external record. What currently happens in your case is that the intent filter

[IntentFilter(new[] { NfcAdapter.ActionNdefDiscovered },
      Categories = new[] { Intent.CategoryDefault },
      DataScheme = "vnd.android.nfc",
      DataPathPrefix = "letypetype",
      DataHost = "ext")]

does not match the external record that you created using

NdefRecord external = NdefRecord.CreateExternal(
            applicationPackageName(),
            "letypetype",
            Encoding.ASCII.GetBytes("param"));

Instead, your activity is launched due to the Android Application Record (AAR). As theres was no matching NFC intent filter Android does not know that your activity supports NFC and, consequently, does not pass the tag (and its NDEF messages) to your activity.

In order to receive the NDEF messages/tag handle in your activity, you would therefore need to update the intent filter to match the external record. If the package name of the app is "com.example", then your intent filter would need to look like:

[IntentFilter(new[] { NfcAdapter.ActionNdefDiscovered },
      Categories = new[] { Intent.CategoryDefault },
      DataScheme = "vnd.android.nfc",
      DataPathPrefix = "/com.example:letypetype",
      DataHost = "ext")]

Note that the domain field and a leading slash need to be included into the DataPathPrefix attribute.

Also note that (while this usually works) a Java/Android package name is not a valid domain name according to the NFC Forum's specification of the external type. Instead, you should use a real domain name (e.g. "example.com").

And finally: don't forget to request the NFC permission in the manifest:

<uses-permission android:name="android.permission.NFC" />