I am trying to update the URI written onto an NFC device. I am essentially appending some value from an edittext on the Android app’s screen.
However, whenever I move the NFC device too quickly – the previous URI value gets erased leaving the device in an unusable state. This happens very rarely but it does occur especially in cases where I do multiple NFC swipes within a few seconds (Same Android phone and same NFC device).
I have included the sample code below (I have intentionally kept the URI manipulation code intact as there is a possibility that I’m doing too many things in a short duration which may be partially causing the issue to occur more frequently).
MainActivity.java:
package my.package.name;
import static my.package.name.NfcData.URI_PATTERN;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private NfcAdapter nfcAdapter;
private TextView myTextView;
private final String LOG_TAG = MainActivity.class.getName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = findViewById(R.id.myTextView);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
}
@Override
protected void onResume() {
super.onResume();
if (nfcAdapter != null) {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
IntentFilter writeTagFilters[] = new IntentFilter[]{tagDetected};
nfcAdapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null);
}
}
@Override
protected void onPause() {
super.onPause();
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(this);
}
}
private void writeNfcDataToDevice(Intent intent, String myString) {
try {
Tag nfcTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
NfcData nfcData = new NfcData(intent);
if (URI_PATTERN.matcher(String.valueOf(nfcData.getUri())).matches()) {
String preExistingUrlStr = nfcData.getUri().toString();
Uri preExistingUri = Uri.parse(preExistingUrlStr);
Uri newUri = preExistingUri.buildUpon().clearQuery().appendQueryParameter(NfcData.MY_PARAM, myString).build();
NdefMessage uriNdefMessage = createUriNdefMessage(newUri);
Ndef ndef = Ndef.get(nfcTag);
ndef.connect();
ndef.writeNdefMessage(uriNdefMessage);
NdefMessage ndefMessage = ndef.getNdefMessage();
ndef.close();
Log.d(LOG_TAG, "NFC write successful !");
} else {
Log.d(LOG_TAG, "nSomething Went wrong. URI pattern does not match:" + nfcData.getUri());
}
}catch (Exception exception) {
Log.e(LOG_TAG, "nSomething Went wrong: " + exception.toString());
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null && (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction()))) {
writeNfcDataToDevice(intent, myTextView.getText().toString().trim());
}
}
public NdefMessage createUriNdefMessage(Uri uri) {
NdefRecord record = NdefRecord.createUri(uri);
NdefMessage msg = new NdefMessage(new NdefRecord[]{record});
return msg;
}
}
NfcData.java:
package my.package.name;
import android.content.Intent;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Parcelable;
import java.util.regex.Pattern;
public final class NfcData {
private final Uri mUri;
public static final String MY_PARAM = "xyz";
static Pattern URI_PATTERN = Pattern.compile("^https?://www.mywebsite.com/ax/([0-9A-Fa-f]{12})(?:\?.*)?$");
// both http and https protocol accepted
// domain name www.mywebsite.com
// string "/ax/"
// 12 compulsory characters that may be any digit from 0 to 9 or A through F or a through f (used to denote bluetooth address)
// Any text/characters beyond the above are optional
public Uri getUri() {
return mUri;
}
public NfcData(Intent intent) {
Uri uri = null;
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
for (Parcelable parcelable : rawMsgs) {
if (!(parcelable instanceof NdefMessage)) continue;
NdefMessage msg = (NdefMessage) parcelable;
for (NdefRecord record : msg.getRecords()) {
uri = record.toUri();
if(uri != null {
this.mUri = uri;
break;
}
}
}
}
}
}