Search code examples
androidandroid-intentnfcintentfiltermifare

Can't invoke NFC onNewIntent() method


i'm trying to read the ID of Mifare Classic 1k card using foreground dispatch. As i can see from my logs, i can enable foreground dispatch, but can not invoke onNewIntent() method. Any suggestions would be appreciated.

MainActivity.java

...
@Override
   protected void onResume() {
      setupForegroundDispatch(this, mAdapter);
      super.onResume();
} 

public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
    final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
    intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    System.out.println("Setup FGD.");  // i can see this output.

    final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);

    IntentFilter[] filters = new IntentFilter[1];
    String[][] techList = new String[][]{};

    // Notice that this is the same filter as in our manifest.
    filters[0] = new IntentFilter();
    filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
    filters[0].addCategory(Intent.CATEGORY_DEFAULT);
    try {
        filters[0].addDataType(MIME_TEXT_PLAIN);
    } catch (MalformedMimeTypeException e) {
        throw new RuntimeException("Check your mime type.");
    }

    adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
    System.out.println("Enabled FGD.");  // and this one.
}

protected void onNewIntent(Intent intent) { 
    System.out.println("Intent."); but i cannot see this one,
    handleIntent(intent);
}

private void handleIntent(Intent intent) {      
    System.out.println("Handle.");  // and this one.
    String action = intent.getAction();
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
        System.out.println("NDEF discovered.");
         ....

AndroidManifest

....
 <activity
        android:name=".MainActivity"
        android:label="@string/app_name" >

        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <intent-filter>
            <action android:name="android.nfc.action.TECH_DISCOVERED" />
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <action android:name="android.nfc.action.TAG_DISCOVERED" />
         </intent-filter>

        <meta-data
                android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/nfc_tech_filter" />
    </activity>
....

Solution

  • If you want to be able to detect every MIFARE Classic tag (and not just those that contain a Text record (or a MIME type record with MIME type text/plain), you should adapt your foreground dispatch to detect the specific tag technology rather than a specific NDEF record type:

    public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
        final Intent pendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity, activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
    
        IntentFilter[] filters = new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED) };
        String[][] techList = new String[][] { new String[] { MifareClassic.class.getName() } };
    
        adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
    }
    

    If you also want to get the UID of MIFARE Classic tags on devices with Broadcom's NFC chipset (those devices are unable to detect MIFARE Classic as MIFARE Classic due to licensing issues of NXP's proprietary technology), you could instead filter for all NfcA tags (MIFARE Classic will be detected as NfcA on all devices, so you don't need to filter for both NfcA and MifareClassic):

        String[][] techList = new String[][] { new String[] { NfcA.class.getName() } };
    

    Last, the intent filter in your manifest does not match the intent filter in your code! Your foreground dispatch's equivalent intent filter would be:

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    

    The manifest equivalent of the foreground dispatch I showed above would be:

    <intent-filter>
        <action android:name="android.nfc.action.TECH_DISCOVERED" />
    </intent-filter>
    <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
               android:resource="@xml/nfc_tech_filter" />
    

    With nfc_tech_filter.xml containing:

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.MifareClassic</tech>
        </tech-list>
    </resources>
    

    Or (in case of matching for NfcA):

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.NfcA</tech>
        </tech-list>
    </resources>