I am developing an app with NFC. I was working with an example in which sending and receiving data was carried by the same Activity. Now I need to move sending to the other one and for some reason receing doesn't work anymore.
Here is my mainifest:
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<activity
android:name=".Screens.LogIn.LogInActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
And the receiving activity:
public class LogInActivity extends AppCompatActivity {
public static final String TAG = LogInActivity.class.getSimpleName();
private String messagesToReceive= null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_log_in);
replaceFragment();
if (getIntent().getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) {
handleNfcIntent(getIntent());
}
}
private void replaceFragment() {
android.support.v4.app.FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
LogInFragment fragment = new LogInFragment();
ft.replace(R.id.fragmentFrame, fragment, LogInFragment.TAG);
ft.commit();
}
@Override
public void onResume() {
super.onResume();
handleNfcIntent(getIntent());
}
@Override
public void onNewIntent(Intent intent) {
handleNfcIntent(intent);
}
private void handleNfcIntent(Intent NfcIntent) {
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(NfcIntent.getAction())) {
Parcelable[] receivedArray =
NfcIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if(receivedArray != null) {
NdefMessage receivedMessage = (NdefMessage) receivedArray[0];
NdefRecord[] attachedRecords = receivedMessage.getRecords();
for (NdefRecord record:attachedRecords) {
String string = new String(record.getPayload());
if (string.equals(getPackageName())) { continue; }
messagesToReceive = string;
}
Toast.makeText(this, "Received " +
" Messages", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(this, "Received Blank Parcel", Toast.LENGTH_LONG).show();
}
}
}
The problem is that NfcIntent.getAction() always equals android.intent.action.MAIN even if the app opens when I send data with NFC. As you can see, there is action NDEF_DISCOVERED in the manifest.
This is what I send:
public NdefRecord[] createRecords() {
NdefRecord[] records = new NdefRecord[2];
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
byte[] payload = messagesToSend.
getBytes(Charset.forName("UTF-8"));
NdefRecord record = new NdefRecord(
NdefRecord.TNF_WELL_KNOWN, //Our 3-bit Type name format
NdefRecord.RTD_TEXT, //Description of our payload
new byte[0], //The optional id for our Record
payload); //Our payload for the Record
records[0] = record;
}
else {
byte[] payload = messagesToSend.//messagesToSendArray.get(i).
getBytes(Charset.forName("UTF-8"));
NdefRecord record = NdefRecord.createMime("text/plain",payload);
records[0] = record;
}
records[1] = NdefRecord.createApplicationRecord(getActivity().getPackageName());
return records;
}
@Override
public NdefMessage createNdefMessage(NfcEvent event) {
if (messagesToSend != null && !messagesToSend.equals("")) {
return null;
}
NdefRecord[] recordsToAttach = createRecords();
return new NdefMessage(recordsToAttach);
}
Receiving the intent action
MAINinstead ofNDEF_DISCOVEREDfor an NFC event is usually a clear indication that the sending side sent an NDEF message containing an Android Application Record and that you did not filter for the first record of that NDEF message.Since your receiving side has an
NDEF_DISCOVEREDintent filter, the problem is most likely located at the sending side. Looking at your code, this could have several reasons:The sending side creates NDEF messages in
createNdefMessage(). There, you test ifmessagesToSendis null or empty. If its neither null nor empty, you returnnullfrom the NDEF message creation callback:Since you did not reveal if
messagesToSendis null or empty, nobody can tell if you atually come to the point where you create your own NDEF message. However, its likely thatmessagesToSendis null/empty since returningnullfromcreateNdefMessage()would disable Beam completely.You did not reveal the remaining code of the sending side. Hence, it's impossible to tell if you actually registered the
createNdefMessage()callback. You should have a like likein
onCreate(). If that's not the case, Android will use a default NDEF message. The default NDEF message typically consists of a URI record and an AAR for your app. This would explain why the receiving side sees intent action MAIN, since your activity is not registered for receiving that specific URI record.On Android versions below Jelly Bean, the condition
evaluates to
trueand you, consequently, hit that branch of the if-statement. In this branch you create an NFC Forum Text record with an invalid structure. This may cause the receiving device to fail to decode the NDEF record as a valid Text record. As a result, the receiving device might not match this record to theNDEF_DISCOVEREDintent filter for typetext/plain.In order to fix this, you need to create a valid Text record. The Text RTD requires a payload that is encoded in the form (also see this post)
whereStatusequals to the lengthnof theLanguage Codeif theTextis UTF-8 encoded andLanguage Codeis an IANA language code (e.g. "en" for English). Consequently, the proper way to encode that record would be:When the method
createRecords()is called on linemessagesToSendis either null or empty (since you would have returnednullotherwise). IfmessagesToSendisnullspecifically and you hit the linein
createRecords(), then this will cause aNullPointerException(since you try to access a method of a null instance). As you never catch this exception, the callbackcreateNdefMessage()will fail with that same exception. In that case, Android will handle this exception and will automatically use the default NDEF message (consisting of a URI record and an AAR for your app). Since your receiving side does not filter for that URI record (the first record of the default NDEF message), your receiver will be statred solely by the AAR and you will receive intent action MAIN, since Android assumes that your activity is ready to receive that specific NDEF message.