Android / NFC: Get Tag in onCreate() without new Intent
Asked Answered
P

2

2

I am working on an NFC-application. To start my app, I am using a NDEF-tag with an AAR NDEF Record inside.

This works fine. But now I want to read the tag content with the app directly. How can I do this?

(It already works, when I remove the tag from the phone and touch it again, but I want to eliminate this step.)

Update: Some more details Ok, to make it more clear, here are some parts of my current code.

private NfcAdapter nfcAdapter;
private static final int PENDING_INTENT_TECH_DISCOVERED = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_l);
    text_output = (TextView) findViewById(R.id.textView2);

    NfcAdapter adapter = ((NfcManager) getSystemService(Context.NFC_SERVICE))
            .getDefaultAdapter();       
}

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

    nfcAdapter = ((NfcManager) this.getSystemService(Context.NFC_SERVICE))
            .getDefaultAdapter();

    PendingIntent pi = createPendingResult(PENDING_INTENT_TECH_DISCOVERED,
            new Intent(), 0);

    nfcAdapter.enableForegroundDispatch(this, pi,
            new IntentFilter[] { new IntentFilter(
                    NfcAdapter.ACTION_TECH_DISCOVERED) }, new String[][] {
                    new String[] { "android.nfc.tech.NdefFormatable" },
                    new String[] { "android.nfc.tech.Ndef" } });
} 

@Override
public void onPause() {
    super.onPause();
    nfcAdapter.disableForegroundDispatch(this);
}

@Override
protected void onActivityResult(int requestCode, int resultCode,
        final Intent data) {
    switch (requestCode) {
    case PENDING_INTENT_TECH_DISCOVERED:
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                doTagOperation(data);
            }
        };
        new Thread(runnable).start();

        break;
    }
}

private void doTagOperation(Intent data) {

    try {           
        String action = data.getAction();
        if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {

            Tag tag = data.getParcelableExtra(NfcAdapter.EXTRA_TAG);                                
            Ndef ndefTag = Ndef.get(tag);
        }
    } catch (Exception ex) {
                ...
    }
}

To start the app I use an AAR-NDEF-Record on the tag, which can be created in this way: NdefRecord ndr = NdefRecord.createApplicationRecord("com.example.something");

So what I want is: Touch the tag when the app is not started makes the app starting (works already) and then it should directly read the tag without the need of moving the phone away from the tag and touch it again.

Penmanship answered 16/1, 2014 at 15:48 Comment(1)
It would be helpful if you posted some of your current code, perhaps the onCreate() method that you say works already. ThanksCarbazole
S
4

If you want to receive the NDEF discovery intent upon the start of your activity, you need to add an intent filter that triggers on the tag's NDEF message to your application manifest:

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

I guess you already have the above, so you will need to add an NDEF intent filter:

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

You can use either a URI based trigger:

        <data android:scheme="http" android:host="mroland.at" android:pathPrefix="/test" />

Or a MIME based scheme:

        <data android:mimeType="application/vnd.mroland.test" />

Or if you want to receive the NDEF discovered intent for a tag that contains only an AAR (I have to admit that I never tested this so I only assume that this should work):

        <data android:scheme="vnd.android.nfc" android:host="ext"
              android:pathPrefix="/android.com:pkg" />

But you have to use one of them. (At least on most devices you have to. Some devices/Android versions will accept the intent filter even without a data element.)

    </intent-filter>
</activity>

You can then get the intent in onCreate(), onStart(), onResume() or wherever you want to read it using the getIntent() method.

Note that if you don't add an intent filter for NDEF_DISCOVERED to your manifest, an NDEF message with an AAR for your application will cause the intent action MAIN with the category LAUNCHER to be passed to your first activity that declares such an intent filter. In that case you will not receive the NDEF message. So if you want to receive the NDEF message you have to to declare an intent filter for NDEF_DISCOVERED. In that case, you can choose which activity is started by the AAR by setting the NDEF_DISCOVERED intent filter for that specific activity.

Sloven answered 17/1, 2014 at 13:5 Comment(7)
Does that mean, that there is no solution with AAR?Penmanship
You mean with an AAR alone? Typically you would use the AAR as the last record of an NDEF message and use some other records before it. So with the above intent filter you would trigger upon the first record in that message (the one that also contains the AAR as its last record). The AAR would still assure that the NDEF discovered intent is only passed to your application. If you really want the AAR to be the only record in your message see the updated answer (though this typically does not make much sense as you would not pass any data in that case).Sloven
FYI: It works with the pure AAR-Solution. Thanks a lot Dr. Roland! :)Penmanship
I also tried to do AAR as an additional record (after another ndef record) now. But this does not make my application start automatically (even when specified in the mainfest). The TNF of my first record is "Unknown" (0x05) (because it's just binary data). Is this a problem (and maybe the reason why it does not start)?Penmanship
Intent filters only work for the first record in an NDEF message. Never use TNF unknown for any record (it's meaning is not that the record payload is binary data but that the record itself is not parsable/interpretable for any reason.) Use an NFC Forum external type name (TNF=external + Type name following NFC Forum's RTD specification) instead. Then you can set your intent filter to triggwer for that record type name.Sloven
OK, nice to know. But to ask this in general: you said that an AAR-record can be placed at the end of multiple ndef-messages (even without an intent-filter) because Android reads always the full tag. Why does it not work in that case?Penmanship
To be honest, I'm not sure. I would have expected, that the AAR is still processed and that your defualt activity (the one that filters intent action MAIN) is started. If that's not the case, there might be a bug in the Android NFC stack that causes record parsing to be interrupted upon records flagged as "unknown" (AFAIK NFC Forum requires that parsers simply skip these records). The above intent filter (the one specifically for the AAR), however, always requires the AAR to be the first record.Sloven
C
-2

You need to use the onNewIntent() method as discussed in the documentation. The onCreate() method should contain the setup code that you currently have in onResume(). This will cause the app to work in all cases.

From the Android documentation (http://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc.html):

public void onPause() {
    super.onPause();
    mAdapter.disableForegroundDispatch(this);
}

public void onResume() {
    super.onResume();
    mAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
}

public void onNewIntent(Intent intent) {
    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    //do something with tagFromIntent
}
Carbazole answered 16/1, 2014 at 16:14 Comment(1)
-1 The foreground dispatch approach from the OP works just like your approach. Instead of the onNewIntent method the onActivityResult method will be called due to the use af a pending result (createPendingResult).Sloven

© 2022 - 2024 — McMap. All rights reserved.