Xamarin NFC view is updated when trying to write a tag

I hope I can explain it correctly. Forgive me if I don't get it.

I have a Xamarin NFC app for Android that works. You go into the application, touch the NFC card, read the information on the card, then touch the button to switch to a new intention, and then in this view you can enter something, touch the card, and it should write the card.

What actually happens in the last view, when you click on a card, it updates the exact view and then clicks again and only then writes it to the NFC card.

So what I think I need to do is to stop detecting NFC in the view that should be written to the NFC card - it should only be in write mode, not read mode.

Any ideas would be much appreciated, thanks.

OnNewIntent is not called the first time the card is connected.

EDIT - Adding Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Nfc;
using Android.Nfc.Tech;
using Android.Preferences;
using Java.IO;
using System.Threading.Tasks;
using System.Net.Http;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace [removed]
{
[Activity(Label = "Update Card")]
public class NfcWriter : Activity
{
    public static readonly string PREFERENCE_FILENAME = "[removed]";
    public static readonly string CLIENTID = "CLIENTID";
    public static readonly string CLIENTUSERID = "CLIENTUSERID";

    public static readonly string EMAILADDRESS = "EMAILADDRESS";
    public static readonly string CARDID = "CARDID";
    public static readonly string USERID = "USERID";
    public static readonly string BALANCE = "BALANCE";

    public const string ViewApeMimeType     = "application/[removed]";//"application/[removed]";
    private bool _inWriteMode;
    private NfcAdapter _nfcAdapter;
    private TextView _textView;
    private Button _writeTagButton;
    private AutoCompleteTextView _textEmail;
    public static readonly string Tag = "NFC Writer";
    public static string uuid = "";

    private TextView _textCard;
    private EditText _textAmount;
    private static string cardID;
    private static double balance;
    private static double newBalance;

    private TextView _txtCard;
    private TextView _txtBalance;
    private TextView _txtProduct;
    private TextView _txtTotalPrice;

    private static string layout;

    private double amount = 0.0f;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        var c = (Context)this;

        // get the nfc device adapter
        _nfcAdapter = NfcAdapter.GetDefaultAdapter(this);

        var allxsSettings = c.GetSharedPreferences(PREFERENCE_FILENAME, FileCreationMode.Private);
        layout = allxsSettings.GetString("Layout", "Registration");


        if (layout == "Registration")
        {
            SetContentView(Resource.Layout.writer);
            if (Intent == null)
            {
                return;
            }

            _writeTagButton = FindViewById<Button>(Resource.Id.write_tag_button);
            _writeTagButton.Click += WriteTagButtonOnClick;

            _textView = FindViewById<TextView>(Resource.Id.text_view);

            _textEmail = FindViewById<AutoCompleteTextView>(Resource.Id.EmailID);
            _textEmail.Text = "";
        }

        if (layout == "CashLoad")
        {
            //_inWriteMode = true;
            SetContentView(Resource.Layout.LoadCash);
            if (Intent == null)
            {
                return;
            }

            _writeTagButton = FindViewById<Button>(Resource.Id.btnLoadCash);
            _writeTagButton.Click += WriteTagButtonOnClick;

            _textView = FindViewById<TextView>(Resource.Id.text_view);

            _textAmount = FindViewById<EditText>(Resource.Id.txtCash);
            _textCard = FindViewById<TextView>(Resource.Id.txtCardId);

            cardID = allxsSettings.GetString (CARDID, "");
            var tempBalance = allxsSettings.GetFloat(BALANCE, 0.0f);
            balance = Convert.ToDouble(tempBalance);

            _textCard.Text = cardID;
            _textAmount.Text = "";
        }

        if (layout == "Sale") {
            //_inWriteMode = true;
            SetContentView(Resource.Layout.Sale);
            if (Intent == null)
            {
                return;
            }

            _writeTagButton = FindViewById<Button>(Resource.Id.btnSale);
            _writeTagButton.Click += WriteTagButtonOnClick;

            _textView = FindViewById<TextView>(Resource.Id.text_view);

            _txtBalance= FindViewById<TextView>(Resource.Id.txtCash);
            _txtCard = FindViewById<TextView>(Resource.Id.txtCardId);
            _txtProduct=FindViewById<TextView>(Resource.Id.txtProduct);
            _txtTotalPrice=FindViewById<TextView>(Resource.Id.txtPrice);

            cardID = allxsSettings.GetString (CARDID, "");
            var tempBalance = allxsSettings.GetFloat (BALANCE, 0.0f);
            balance = Convert.ToDouble(tempBalance);

            _txtCard.Text = cardID;
            _txtBalance.Text = balance.ToString("0.00");

            Product productRow = null;

            decimal totalAmount = 0;
            _txtProduct.Text = "";

            foreach (var salesRow in SalesManager.GetSaless()) {
                productRow = ProductManager.GetProduct (salesRow.ProductID);

                totalAmount += Convert.ToDecimal (salesRow.TotalAmount);

                if (_txtProduct.Text.Length != 0)
                    _txtProduct.Text += "\n";

                _txtProduct.Text += salesRow.NoOfUnits + " x " + productRow.ProductName + " @ " + productRow.UnitPrice.ToString ("0.00");
            }

            _txtTotalPrice.Text = totalAmount.ToString("0.00");

            // determine the new card balance
            newBalance = balance - Convert.ToDouble (totalAmount);

            if (newBalance < 0) {
                // we don't have enough money to buy
                _txtTotalPrice.Text += " INSUFFICIENT FUNDS";

                toast ("INSUFFICIENT FUNDS - NO SALE");
            }
        }

    }

    /// <summary>
    /// This method is called when an NFC tag is discovered by the application.
    /// </summary>
    /// <param name="intent"></param>
    protected override void OnNewIntent(Intent intent)
    {
        _inWriteMode = true;

        if (_inWriteMode)
        {
            //_inWriteMode = false;

            var c = (Context)this;
            var allxsSettings = c.GetSharedPreferences(PREFERENCE_FILENAME, FileCreationMode.Private);

            var tag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
            if (tag == null)
            {
                return;
            }


            Card card = new Card ();
            if (layout == "Registration") {

                card.CardID = _textEmail.Text;
                card.Amount = 0.0f;
            } 
            if (layout == "CashLoad") {
                card.CardID = _textCard.Text;
                card.Amount = balance;
            }

            if (layout == "Sale") {

                // no write allowed if there is no money
                if (newBalance < 0) {
                    toast ("INSUFFICIENT FUNDS - NO SALE");
                    return;
                }

                var cardID = allxsSettings.GetString (CARDID, "");
                var tempBalance = allxsSettings.GetFloat (BALANCE, 0.0f);
                var balance = Convert.ToDouble(tempBalance);
                var clientUserId = allxsSettings.GetInt (CLIENTUSERID, 0);

                card.CardID = cardID;
                card.Amount = newBalance;
            }


            BinaryFormatter payload = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            payload.Serialize(ms, card);

            var mimeBytes = Encoding.ASCII.GetBytes(ViewApeMimeType);
            var cardRecord = new NdefRecord(NdefRecord.TnfMimeMedia, mimeBytes, new byte[0], ms.ToArray());
            var ndefMessage = new NdefMessage(new[] { cardRecord });

            bool isWritten = false;

            if (!TryAndWriteToTag(tag, ndefMessage))
            {
                // Maybe the write couldn't happen because the tag wasn't formatted?
                if (TryAndFormatTagWithMessage(tag, ndefMessage))
                    isWritten = true;

            }
            else
            {
                isWritten = true;
            }

            // if we did write to the card then we must update the server api (this is where the transaction should be stored locally and updated later)
            if (isWritten)
            {
                if (layout == "Registration")
                {
                    // this is a new card - we don't know who the user is at this time but send the card registration to the system
                    ApiCardRegistration(card.CardID);
                }

                if (layout == "CashLoad")
                {
                    // tell the api that we have loaded cash
                    ApiCardLoadCash(card.CardID, amount);
                }

                if (layout == "Sale")
                {
                    // we have written the sale amount to the card, now write to the server
                    ApiCardLoadProducts (card.CardID);

                }
            }


            ISharedPreferencesEditor prefEditor = allxsSettings.Edit();
            prefEditor.PutString("Layout", "");     
            prefEditor.Apply();
            prefEditor.Commit();

        }
    }




    protected override void OnPause()
    {
        base.OnPause();
        // App is paused, so no need to keep an eye out for NFC tags.
        if (_nfcAdapter != null)
            _nfcAdapter.DisableForegroundDispatch(this);
    }

    private void DisplayMessage(string message)
    {
        _textView.Text = message;
        //Log.Info(Tag, message);
    }

    /// <summary>
    /// Identify to Android that this activity wants to be notified when 
    /// an NFC tag is discovered. 
    /// </summary>
    private void EnableWriteMode()
    {
        _inWriteMode = true;

        // 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 filters = new[] { tagDetected };

        // When an NFC tag is detected, Android will use the PendingIntent to come back to this activity.
        // The OnNewIntent method will invoked by Android.


        var intent = new Intent(this, GetType()).AddFlags(ActivityFlags.SingleTop);
        var pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);

        if (_nfcAdapter == null)
        {
            var alert = new AlertDialog.Builder(this).Create();
            alert.SetMessage("NFC is not supported on this device.");
            alert.SetTitle("NFC Unavailable");
            alert.SetButton("OK", delegate
                {
                    _writeTagButton.Enabled = false;
                    _textView.Text = "NFC is not supported on this device.";
                });
            alert.Show();
        }
        else
            _nfcAdapter.EnableForegroundDispatch(this, pendingIntent, filters, null);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="tag"></param>
    /// <param name="ndefMessage"></param>
    /// <returns></returns>
    private bool TryAndFormatTagWithMessage(Tag tag, NdefMessage ndefMessage)
    {
        var format = NdefFormatable.Get(tag);
        if (format == null)
        {
            DisplayMessage("Tag does not appear to support NDEF format.");
        }
        else
        {
            try
            {
                format.Connect();

                format.Format(ndefMessage);
                DisplayMessage("Tag successfully written.");


                return true;
            }
            catch (System.IO.IOException ioex)
            {
                var msg = "There was an error trying to format the tag.";
                DisplayMessage(msg);
                //Log.Error(Tag, ioex, msg);
            }
        }
        return false;
    }


    private void WriteTagButtonOnClick(object sender, EventArgs eventArgs)
    {
        var view = (View)sender;
        if (view.Id == Resource.Id.write_tag_button)
        {
            DisplayMessage("Touch and hold the tag against the phone to write.");
            EnableWriteMode();
        }

        if (view.Id == Resource.Id.btnLoadCash)
        {

            if (_textAmount.Text.Trim().Length == 0)
                toast("Please enter Amount.");
            else
            {
                if (_textCard.Text.Trim().Length == 0)
                {
                    toast("Please enter Card Number");
                    return;
                }

                amount = 0;

                try
                {
                    amount = Convert.ToDouble(_textAmount.Text.Trim());
                }
                catch
                {
                    toast("Invalid Amount");
                    return;
                }

                if (cardID == "")
                    cardID = _textCard.Text;

                balance += amount;
                DisplayMessage("Touch and hold the tag against the phone to write.");
                EnableWriteMode();
            }




        }
        if (view.Id == Resource.Id.btnSale) {
            DisplayMessage("Touch and hold the tag against the phone to write.");
            EnableWriteMode();
        }
    }


    /// <summary>
    /// This method will try and write the specified message to the provided tag. 
    /// </summary>
    /// <param name="tag">The NFC tag that was detected.</param>
    /// <param name="ndefMessage">An NDEF message to write.</param>
    /// <returns>true if the tag was written to.</returns>
    private bool TryAndWriteToTag(Tag tag, NdefMessage ndefMessage)
    {

        // This object is used to get information about the NFC tag as 
        // well as perform operations on it.
        var ndef = Ndef.Get(tag);
        if (ndef != null)
        {
            ndef.Connect();

            // Once written to, a tag can be marked as read-only - check for this.
            if (!ndef.IsWritable)
            {
                DisplayMessage("Tag is read-only.");
            }

            // NFC tags can only store a small amount of data, this depends on the type of tag its.
            var size = ndefMessage.ToByteArray().Length;
            if (ndef.MaxSize < size)
            {
                DisplayMessage("Tag doesn't have enough space.");
            }

            ndef.WriteNdefMessage(ndefMessage);
            DisplayMessage("Succesfully wrote tag.");
            return true;
        }

        return false;
    }
+4
source share
1 answer

I solved the problem by changing the way the NfcWriter class starts. The code above was not a problem, since I never had to make any changes other than the way the intent was created.

I replaced "this" with "Application.ApplicationContext" and set the flag to "NewTask" in the calling code, which became this ...

Android.Content.Intent intent = new Intent(Application.ApplicationContext, typeof(NfcWriter));
intent.AddFlags(ActivityFlags.NewTask);

StartActivity(intent);

Hope this helps someone else.

I got this with some clever help on behalf of Xamarin's support, Brendan - who guided me to create NFC logs from interactions, and then study the logs to find out what the problem is.

, , NFC, - "startActivity " .

NFC, :

  • Android- , "adb"
  • "adb logcat -c", , .
  • , .
  • adb logcat

    • adb logcat -d -vtime > C:\Users\%USERNAME%\Desktop\nfc_logcat.txt

    • adb logcat -d -vtime -bradio > C:\Users\%USERNAME%\Desktop\nfc_radio.txt

    • adb logcat -d -vtime -bevents > C:\Users\%USERNAME%\Desktop\nfc_events.txt

Windows adb Xamarin: C:\Users\%USERNAME%\AppData\Local\Android\android-sdk\platform-tools\adb.exe

+1

Source: https://habr.com/ru/post/1540014/


All Articles