Search code examples
androidxamarinnfc

NFCTagDiscovered Intent not fired when phone is already in contact with tag and then listening is invoked


I'm working on an NFC based app developed in Xamarin for Android. The app has a 'Connect' button and the NFC scanning process starts only when that button is tapped. With proper intents configured, whenever an NFC tag is detected, HandleNewIntent() method gets called and NFC read procedure is followed.

    internal void HandleNewIntent(Intent intent)
    {
        try
        {
            if (intent.Action == NfcAdapter.ActionTagDiscovered || intent.Action == NfcAdapter.ActionNdefDiscovered)
            {
                _currentTag = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
                if (_currentTag != null)
                {
                    Task.Run(() => { ReadDataFromTag(_currentTag); });
                }
            }
        }
        catch (Exception ex)
        {
            //Display error
        }
    }

In normal cases this works fine. However, if the phone is kept in contact with the NFC tag and then the 'Connect' button is tapped on, then the TagDiscovered intent never gets fired. User has to take the phone away, bring it back in contact with the NFC tag and then only the event gets fired. I observed the same behaviour with generic NFC apps on play store, and on 2 different Android phones.

Looks like Android keeps the NFC of phone tied up when in contact with NFC tag because of which the intents are not detected. Is there anything to be done to release such links (if any) before initiating new NFC connection?


Solution

  • All NFC is handled by the System NFC service and System App and is normally triggered by a Tag coming in to range and then an Intent being delivered to the right app immediately, therefore the system NFC service has already delivered the Intent to another App before you App has had the button pressed.

    You don't show all the code that you do to setup your Apps' NFC configuration but as you are talking about Intents you are probably using the old and unreliable enableForegroundDispatch API which has a number of issues.

    While it is more normal to enable foreground NFC detection as soon as you App is resumed and then process the Tag on immediate detection, it is possible to store the Tag object until a button is pressed.

    I suggest that you use the better and newer enableReaderMode API that has less of a problem because it is a more direct interface to the NFC hardware and thus you use case seems to work using the code below.

    public class MainActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback{
    
        private NfcAdapter mNfcAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void Button(View view) {
    
            Log.v("TAG", "DetailedScoresActivity:onTagDiscovered:Start");
    
            mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
    
            if(mNfcAdapter!= null) {
                Bundle options = new Bundle();
                options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);
    
                mNfcAdapter.enableReaderMode(this,
                        this,
                        NfcAdapter.FLAG_READER_NFC_A |
                                NfcAdapter.FLAG_READER_NFC_B |
                                NfcAdapter.FLAG_READER_NFC_F |
                                NfcAdapter.FLAG_READER_NFC_V |
                                NfcAdapter.FLAG_READER_NFC_BARCODE |
                                NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK |
                                NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                        options);
            }
        }
    
        public void onTagDiscovered(Tag tag) {
    
            Log.v("TAG", "onTagDiscovered:Start");
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            if(mNfcAdapter!= null)
                mNfcAdapter.disableReaderMode(this);
        }
    }
    
    For me you can place a Tag against the phone and then start the App and as soon as the Button is clicked and `enableReaderMode` is enabled then `onTagDiscovered` is triggered with the Tag object.