I am new to NFC. I tried to connect to NFC and share the text data from a device to another device.
I install my application on both devices and in one device I open my application and start tapping the device to the another device in order to transmit data over Beam.
On the other device, my application opens due to the Beam interaction. However, the activity is started with the default MAIN intent action and not with TAG_DISCOVERED (or similar NFC intent). Every time it does the same thing.
Also, it's not calling the onNewIntent()
method. I tried to call onNewIntent
from onCreate
, but the intent action is still MAIN in that case. I expected to receive an NFC intent for the Beam interaction. So can you please tell me where did I go wrong?
In this code I am not sharing the data. I just need the tag first.
Manifest:
<activity android:name="com.framentos.hellonfc.MainActivity"
android:clearTaskOnLaunch="true"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
Java code:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ctx = this;
// startHandler();
_handler = new Handler();
Button btnWrite = (Button) findViewById(R.id.button);
message = (TextView) findViewById(R.id.nfcwriteTag);
btnWrite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
adapter = NfcAdapter.getDefaultAdapter(this);
if (adapter == null) {
message.setText("NFC is not supported on this device.");
}
if (adapter.isEnabled()) {
message.setText("NFC is Enabled on this device.");
} else {
message.setText("Please enable NFC to communicate.");
}
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(
NfcAdapter.ACTION_NDEF_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
IntentFilter tagTech = new IntentFilter(
NfcAdapter.ACTION_TECH_DISCOVERED);
tagTech.addCategory(Intent.CATEGORY_DEFAULT);
IntentFilter tagDetect = new IntentFilter(
NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetect.addCategory(Intent.CATEGORY_DEFAULT);
writeTagFilters = new IntentFilter[] { tagDetected, tagTech ,tagDetect};
// handleIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.i(getPackageName(), "on New Intent is called...!");
handleIntent(getIntent());
}
public void onPause() {
super.onPause();
WriteModeOff();
}
@Override
public void onResume() {
super.onResume();
WriteModeOn();
}
private void WriteModeOn() {
writeMode = true;
adapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters,
null);
}
private void WriteModeOff() {
writeMode = false;
adapter.disableForegroundDispatch(this);
}
On your Android device, open the Settings app. Select Connected devices. Tap on Connection preferences. You should see the NFC option.
Writing Data To An NFC Tag Using Your Android Device To do it, open the Settings app, tap on Bluetooth & device connection, select Connection preferences, and finally turn the toggle for NFC to the ON position.
Its possible to make Android device behave as an NFC Tag. Such a behaviour is called Card Emulation. Card emulation can be host-based(HCE) or secure-element based(CE). In HCE, an application running on the Android main processor responds to the reader.
IOS & Android devices can scan an NFC tag by tapping the physical tag to instantly pull up information on the device. The Scan app stores all information read on any tag (even non Array tags). The Scan app can also generate custom QR codes and barcodes to be shared and exported as JPG images.
From the code that you posted in your question, I assume that you did not register your application to send a specific NDEF message. In that case, if your app is open on one device, Android will automatically Beam an NDEF message containing a URI record with the Play Store link of your app and an Android Application Record (AAR) to the other device.
So your second device will receive the following NDEF message:
+---------------------------------------------------------------------------------------+
| WKT:URI | http://play.google.com/store/apps/details?id=your.package.name&feature=beam |
+---------------------------------------------------------------------------------------+
| EXT:android:com:pkg | your.package.name |
+---------------------------------------------------------------------------------------+
What happens now if your app is not already open on the second device is, that the Android Application Record (second record) will force your app to be started. However, looking at your manifest, you do not have an intent filter that matches the first record of this NDEF message (the Play Store URL). Consequently, Android thinks that you do not expect an NDEF message and uses the standard android.intent.action.MAIN
(with category android.intent.category.LAUNCHER
) to start your app (or rather the first activity of your app that has an intent filter for action MAIN
with category LAUNCHER
).
In order to receive an NFC intent together with the whole NDEF message in your app, you would need to define a proper intent filter that matches the first record in the above NDEF message:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http"
android:host="play.google.com"
android:pathPrefix="/store/apps/details?id=your.package.name" />
</intent-filter>
Now, Android will recognize that your app/activity is ready to receive an NFC intent and pass the NDEF_DISCOVERED
intent to your activity. Note that you still won't receive that intent through onNewIntent()
if your app is not already running. Instead you can get the intent that started your activity with the activity's getIntent()
method. E.g. in onCreate
/onStart
/onResume
, you could use
Intent intent = getIntent();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
NdefMessage ndefMessage = null;
Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if ((rawMessages != null) && (rawMessages.length > 0)) {
ndefMessage = (NdefMessage)rawMessages[0];
}
// TODO: do something with the received NDEF message
}
to get the intent and the NDEF message.
Regarding the intent filters you already have in your manifest:
NDEF_DISCOVERED
:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
An NDEF_DISCOVERED
intent filter without a <data ... />
tag will never trigger on many NFC devices. You should always define what specific data you expect to be present in the launching NDEF record.
TECH_DISCOVERED
:
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
You should only use this intent filter if you wan't you application to be started upon detection of a certain NFC tag technology (you define the specific technologies in the nfc_tech_filter.xml
file. For your Beam scenario, you would not use such an intent filter. Btw. the <category ...>
tag is not used for this intent filter.
TAG_DISCOVERED
:
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
This intent filter should normally not be used in the manifest. It is meant as a fallback that triggers if no other app could possibly handle a detected NFC tag. Using this can lead to bad user-experience (e.g. your app being started for tags it can't or does not actually want to handle). It is primarily available for backward compatibility with API level 9 (?).
In order to also catch the NFC intents if your activity is already started, you could register for the foreground dispatch like this (in the onResume
method):
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
That way, you will receive any NFC discovery events that occur while your activity is in the foreground in your activity's onNewIntent
method. They will be dispatched through an ACTION_TAG_DISCOVERED
intent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With