So let's say I wanna make an app that scans an nfc tag when the user taps the "scan tag" button.
Currently I am having a few issues with it even though I spent the last week trying to figure it out on my own😅
The main issue that I am facing is that the app will scan nfc tags in the background (or at least looks like it) and will open a new instance of my app but with the name of "Nfc Service" (which is not what I named my app).
The other issue that I am facing is that, in the code, when I try to call my "readTag()" mehtod, it only works if it opens that "Nfc Service" thing I said before, otherwise it doesn't read the tag, but only breaks. From my understanding it looks like the readTag() method is running in the background rather than when I tell it to.
TL;DR: I don't want my app to scan nfc tags in the background but only on a button press.
Help would be massively appreciated especially because my deadline it tomorrow 😅 T-T
Here is some of my code (I can't show everything unfortunately):
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ips.software.iasset_ips">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.IAssetIPS">
<activity android:name=".WebViews.AssetInfoWebView"></activity>
<activity android:name=".Activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
</application>
</manifest>
xml/nfc_tech_filter:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
</tech-list>
</resources>
MainActivity: (just the parts relevant to the question)
// package + all the imports
// ...
public class MainActivity extends AppCompatActivity {
private Button scanButton;
NfcAdapter adapter;
Tag tag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
configureScanButton();
}
private void configureScanButton() {
scanButton= findViewById(R.id.scanButton);
scanButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
readTag();
}
});
}
public void readTag() {
adapter = NfcAdapter.getDefaultAdapter(this); // initialize the nfc reader
checkNfcSupport(); // check if device supports nfc, if not close and Toast.show
tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); // read the tag
decodeSerialNumberFromTag(); // can't show you this function, but it's only decoding, it shouldn't be the cause of the problem
Toast.makeText(this, "Serial Number: " + serialNumber, Toast.LENGTH_LONG).show();
}
}
So yeah i think that's all, it would also be nice if I could call the readTag() method whenever I want to scan a new tag.
I talk too much, thank you in advance :D
The only way to start the interaction of you App to an NFC Tag is via the NFC Service, but if you program it properly it is possible to achieve the your desired behaviour.
First off, the manifest Intent filter are really only about telling the NFC Service to start/restart your App when a Tag of the right type comes in to range.
There is an interaction with the "launchMode" of your App as detailed in https://stackoverflow.com/a/64834600/2373819
The way to handle an NFC tag when your App is running is to use enableForegroundDispatch
or use the better enableReaderMode
APIs which tell the NFC Service to pass you the Tag object in different ways when a Tag comes in to range.
An example of the better enableReaderMode
API for reading and writing is at https://stackoverflow.com/a/64921434/2373819 (You can ignore the writing bit)
It is not the normal workflow to do something with a Tag only when you press a button as a Tag might or might not be in range, what you are actually doing in your current readTag
method when you press the button is just processing the Intent
that was stored when the activity was started no actually I/O for reading is being done to the Tag. The Tag UID and any NDEF message stored on the Tag will have been cached in the Tag Object/Intent when it came in to range.
To do the similar with enableReaderMode
in the onTagDiscovered
method you would store the Tag Object is a global variable in the Activity class and then process it when you press the button with checking the Tag object is not null
i.e. user has pressed the button with not Tag has every been in range.
If want to read other non cached data when you press the button you need to handle Tag out or range errors and other I/O errors as you would if your were processing the Tag immediately when it came in to range, just you are more likely to have them.
I would say it is always good practise when using NFC in your App to enableForegroundDispatch
or use the better enableReaderMode
to as the NFC service to send you the Tag Object even if you don't want to do anything with it at that point in time because it will prevent the NFC service launching another App to handle it or even itself displaying a basic information screen over the top of your App about the Tag that came in to range. e.g. have an empty method in onNewIntent
or onTagDiscovered