I'm detecting NFC tags for my game (made in Unity). In the game the player detects NFC tag and it shows him/her something (button, text, ...). And I would like to make that thing disappear if the player is no longer next to the tag.
The problem is that even if I remove the NFC tag from the reach of the NFC reader (put the tag away from the phone) it returns the same _mActivity as if the tag was still there.
So my question is if there is a way how to remove the once read activity? or is there better way to detect if the tag is really there?
my code:
void FixedUpdate()
{
string newTagOutput = DetectNFCTag();
...
}
string DetectNFCTag()
{
try
{
// Create new NFC Android object
AndroidJavaObject _mActivity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject _mIntent = _mActivity.Call<AndroidJavaObject>("getIntent");
string _sAction = _mIntent.Call<String>("getAction");
if (_sAction == "android.nfc.action.NDEF_DISCOVERED")
{
AndroidJavaObject[] rawMsg = _mIntent.Call<AndroidJavaObject[]>("getParcelableArrayExtra", "android.nfc.extra.NDEF_MESSAGES");
AndroidJavaObject[] records = rawMsg[0].Call<AndroidJavaObject[]>("getRecords");
byte[] payLoad = records[0].Call<byte[]>("getPayload");
string result = System.Text.Encoding.Default.GetString(payLoad);
result = result.Remove(0, 3); // removes information about language "en..."
return result;
}
}
catch (Exception ex) {...}
return null;
}
Android manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="preferExternal"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<uses-sdk android:minSdkVersion="21"/>
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true"/>
<application
android:theme="@style/UnityThemeSelector"
android:icon="@drawable/app_icon"
android:label="@string/app_name">
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:label="@string/app_name"
android:launchMode="singleTask">
<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.NDEF_DISCOVERED" />
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" android:value="true" />
</activity>
</application>
There is no direct way to detect when a NFC Tag is removed out of range in Android (there is an indirect way).
There are a number of different ways to interact with a NFC Tag in Android but the way you seem to be doing it is the most basic.
With all methods the Android NFC system service detects the Tag and by default it will fully reads any NDEF data off the Tag.
At this point the once the Android NFC system service has read all the NDEF data off the Tag then the Tag can be moved out of range with no affect on the process as all NDef data has been extracted and stored.
The Android NFC system service then decides who to send this Ndef data to and then starts the appropriate Android Activity and passes it this static NDef data (there is no link to the Tag with the Ndef Data and no I/O operations will be performed to the Tag when you look at this static data).
Now how to do it the indirect way.
(Sorry not a Unity expert)
Instead of asking Android for the preparsed Ndef data from the Intent you can ask for a Tag
object which is a direct link to the Nfc Tag.
This is in the getParcelableArrayExtra
of android.nfc.extra.TAG
From this generic Tag object you can convert it to an NDef Tag object.
In Java terms this is Ndef.get(Tag)
and will be non null if the Tag is of Ndef Type.
From there you can call getNdefMessage
on this Ndef Tag object and this will cause an I/O operation to happen to the Tag.
If the Tag is out of range this I/O operation will fail with an Exception (TagLostException
or IOException
or FormatException
) which can be caught.
Thus to detect if a Tag has been moved out of range you need to keep doing an I/O operation in a loop until you get an Exception which will mean the Tag is out of range to read.
Note you should never do Tag I/O operations on the main UI thread, especially if you are doing them in a loop as the App will freeze.
I would at the Android level use enableReaderMode
instead of the Intent
based method as you automatic get the Tag
object in a new thread for this.
Sorry I don't know how to do enableReaderMode
in Unity.