6

In Android 10 I noticed I get a Toast message from the OS stating "No supported application for this NFC tag" or "No supported app for this NFC tag" (depending on the device):

enter image description here

The weird thing is that I see the Toast while enableReaderMode is active in the foreground Activity. In all previous versions of Android, enableReaderMode would override the Android intent tag dispatch system. Is this a bug in Android 10?

I know enableForegroundDispatch also exists, and that API does seem to override the intent tag dispatch system even in Android 10. But I'd like to keep control over the NFC discovery sound which is only provided by enableReaderMode.

I also know that I can declare an intent-filter in my manifest to get rid of the Toast while continuing to use enableReaderMode, but that also has unintended side effects (e.g. my app could be launched while reading the NFC tag from the device home screen which I don't want).

Adam Johns
  • 35,397
  • 25
  • 123
  • 176
  • For me this happens randomly on Android 10 (i.e. sometimes reader mode gets activated, sometimes not). [This question](https://stackoverflow.com/q/61142449/5128464) looks related (and contains some insights)... – vlp May 15 '20 at 22:55

1 Answers1

2

Yes, it seems to be a bug as Android OS fires a new tagDiscovery event under the following conditions:

  • Use enableReaderMode instead of enableForegroundDispatch
  • Read or write a card
  • While the card is still in proximity call disableReaderMode.

Since this triggers an OS level event, it can pause focused activity, might show a toast screen or show related application select box.

To workaround the problem,

Workaround 1:

  • Try connecting the card in a loop until an IOException is fired. (which means card is not in proximity anymore)
  • Then call disableReaderMode

Cons: You may need to show a message to the user to remove the tag/card from the device proximity.

Workaround 2:

  • Use legacy enableForegroundDispatch / disableForegroundDispatch along with readerMode

Cons: Popup do not gets displayed, however tag discovery sound still gets triggered.

Both solutions do not need intent-filter to be defined.

Sample code that implements both workarounds is below.

import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Bundle;
import android.widget.Toast;

import java.io.IOException;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity
{
    private NfcAdapter nfcAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        //Assuming nfc adapter is present and active, such checks are ignored for code clarity,
        //production code must check for hardware nfc adapter existence
        nfcAdapter = NfcAdapter.getDefaultAdapter(this); //Get the default NFC adapter from OS

        //Additional initialization code
    }

    private void onTagDiscovered(Tag tag)
    {
        try
        {
            if (tag == null)
                return;

            //Assumption: We're using an NFC card that supports IsoDep
            IsoDep iso = IsoDep.get(tag);

            if (iso == null)
                return;

            iso.setTimeout(1000);
            iso.connect();

            //doCardReadWrite(iso);

            iso.close();

            //Workaround 1
            //Wait until the card has been removed from the range of NFC
            //Then finish the activity
            while(true)
            {
                try
                {
                    iso.connect();
                    Thread.sleep(100);
                    iso.close();
                }
                catch (IOException | InterruptedException e)
                {
                    //On this example, once we're done with the activity, we want to close it
                    //then onPause event will call disableReaderMode, at that moment we don't want a card to be in proximity
                    onCardRemoved();
                    break;
                }
            }
            //End of Workaround 1
        }
        catch (IOException e)
        {
            Toast.makeText(this, "Tag disconnected. Reason: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void onCardRemoved()
    {
        this.finish();
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        //Workaround 2
        //Legacy nfc reader activation method, just enabled it but we won't use it
        //(to fully support [e.g. OS version < v4.4.4] you must override onNewIntent(Intent intent) method as well)

        //create intent/tag filters to be used with enableForegroundDispatch
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
                0
        );

        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
        IntentFilter[] writeTagFilters = new IntentFilter[]{tagDetected};

        nfcAdapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null);
        //End of Workaround 2

        //ReaderMode activation
        Bundle options = new Bundle();
        options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 1000);//Presence check interval
        final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS;
        nfcAdapter.enableReaderMode(this, new NfcAdapter.ReaderCallback()
        {
            @Override
            public void onTagDiscovered(Tag tag)
            {
                MainActivity.this.onTagDiscovered(tag);
            }
        }, READER_FLAGS, options);
    }

    @Override
    protected void onPause()
    {
        nfcAdapter.disableReaderMode(this);

        //Workaround 2
        nfcAdapter.disableForegroundDispatch(this);

        super.onPause();
    }
}
vahapt
  • 1,685
  • 1
  • 21
  • 26