NFC read not working - android

I am newbie in developing android application. Currently I am trying to develop my own tag reader which can read MiFare Ultralight tag.
BUt I failed to read the tag as NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) always return false. Could somebody help me out?
NfcReader.java
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mText = (TextView) findViewById(R.id.text);
mText.setText("Scan a tag");
mAdapter = NfcAdapter.getDefaultAdapter();
// Create a generic PendingIntent that will be deliver to this activity. The NFC stack
// will fill in the intent with the details of the discovered tag before delivering to
// this activity.
mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
// Setup an intent filter for all MIME based dispatches
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
mFilters = new IntentFilter[] {
ndef,
};
// Setup a tech list for all NfcF tags
mTechLists = new String[][] { new String[] { NfcF.class.getName() } };
Intent intent = getIntent();
getNdefMessages(intent);
}
public void getNdefMessages(Intent intent) {
// Parse the intent
NdefMessage[] msgs = null;
String action = intent.getAction();
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
else {
// Unknown tag type
byte[] empty = new byte[] {};
NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
NdefMessage msg = new NdefMessage(new NdefRecord[] {record});
msgs = new NdefMessage[] {msg};
}
}
else {
//Log.e(TAG, "Unknown intent " + intent);
finish();
}
}
#Override
public void onResume() {
super.onResume();
mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
#Override
public void onNewIntent(Intent intent) {
Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
getNdefMessages(intent);
}
#Override
public void onPause() {
super.onPause();
//mAdapter.disableForegroundDispatch(this);
throw new RuntimeException("onPause not implemented to fix build");
}
Manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nfc.reader"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.NFC" />
<uses-sdk android:minSdkVersion="10"/>
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name="NfcReader"
android:theme="#android:style/Theme.NoTitleBar">
<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.TECH_DISCOVERED"/>
<data android:mimeType="text/plain" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/nfctech" />
</activity>
</application>
</manifest>

When a tag with an NDEF message is detected, android looks for any Application with an Activity with intent-filter for action "android.nfc.action.NDEF_DISCOVERED". If two or more applications can handle this action, the one with the most precise filter receives the intent. When applications with the same filter are found, is the user who chooses one. So in your AndroidManifest.xml you have to use this action for detecting NDEF formatted tags.
If you want to read NDEF messages don't worry about which type of tag you are reading (Ultralight, Felica, Topaz,...). Android uses android.nfc.tech.Ndef for dispatch NDEF formatted tags. So for getting all NdefMessages when you activity is on foreground, you can use this snippet:
//Called in onCreate()
private void nfcConfig() {
mAdapter = NfcAdapter.getDefaultAdapter(this);
pendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
techListsArray = new String[][]{new String[]{
Ndef.class.getName()
}};
}
#Override
public void onResume() {
super.onResume();
mAdapter.enableForegroundDispatch(this, pendingIntent, null, techListsArray);
}
#Override
public void onPause() {
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
#Override
public void onNewIntent(Intent intent) {
getNdefMessages(intent);
}
If you want to read an Ultralight tag ( or another technology), it should be because you need the raw data from the tag. It must not be executed from the main application thread. For example:
private void nfcConfig(){
...
//Mifare Ultralight filter
techListsArray = new String[][]{new String[]{
NfcA.class.getName(),
MifareUltralight.class.getName()
}};
...
}
#Override
public void onNewIntent(Intent intent) {
getRawData(intent);
}
private void getRawData(Intent intent){
final Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
MifareUltralight ulTag = MifareUltralight.getTag();
new ReadMFULTask(ulTag).execute();
}
private static class ReadMFULTask extends AsyncTask<Void, Void, Void> {
private Tag mTag;
public ReadMFULTask(Tag tag){
mTag = tag;
}
#Override
protected Void doInBackground(Void... arg0) {
MifareUltralight ulTag = MifareUltralight.get(mTag);
try {
ulTag.connect();
//Read all pages from an Ul tag
for(int i=0; i < 16; i=i+4){
byte[] data = ulTag.readPages(i);
String dataString = HexUtilities.getHexString(data);
Log.d("RAW DATA", dataString);
}
ulTag.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

In your code you are setting your intent filter based on ACTION_TECH_DISCOVERED, but then you try ndef.addDataType(/), which is the way you would go about setting up an intent filter for ACTION_NDEF_DISCOVERED. Instead you could simply do something like this:
IntentFilter ntech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
mFilters = new IntentFilter[]{ ntech };
make sure you have nfc_tech_list.xml set up properly:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>

Related

Not Reading Nfc 4K Card details in Android

I want to read NFC 4K card details, Its not reading the data, Here is the Code I use,
AndroidManifest.xml
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<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" />
</activity>
nfc_tech_filter.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.NfcF</tech>
<tech>android.nfc.tech.NfcV</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NdefFormatable</tech>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>
MainActivity.java
public class MainActivity extends Activity implements NfcAdapter.ReaderCallback {
public static final String TAG = " --NFC-- " + MainActivity.class.getSimpleName();
private NfcAdapter nfcAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
showToast("No NFC found...");
} else {
showToast("NFC Enables Device...");
enableReaderMode();
}
}
#Override
public void onResume() {
super.onResume();
showToast("On Resume...");
enableReaderMode();
}
#Override
protected void onPause() {
super.onPause();
showToast("On Pause...");
disableReaderMode();
}
private void showToast(String message) {
Log.d(TAG, message);
try {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
showToast("Show Toast Exception : " + message);
}
}
private void enableReaderMode() {
showToast("Enabling reader mode...");
Activity activity = MainActivity.this;
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
if (nfcAdapter != null) {
nfcAdapter.enableReaderMode(activity, this, Integer.MAX_VALUE, null);
}
}
private void disableReaderMode() {
showToast("Disabling reader mode...");
Activity activity = MainActivity.this;
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
if (nfcAdapter != null)
nfcAdapter.disableReaderMode(activity);
}
#Override
public void onTagDiscovered(Tag tag) {
Log.d(TAG, "New tag discovered...");
}
#Override
protected void onNewIntent(Intent intent) {
Log.d(TAG, "New Intent...");
super.onNewIntent(intent);
}
}
Your are not handling your intent data in onNewIntent(). onNewIntent() will be call whenever you scan your NFC card.
Like below example :
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "New Intent...");
handleIntent(intent);
}
private void handleIntent(Intent intent) {
val bundle = intent.extras
if (bundle != null) {
Log.e(TAG,intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
}
}
Edited
You need to call this method from onResume() after adding adapter.
private fun setupForegroundDispatch(activity : Activity, adapter
: NfcAdapter) {
val intent = Intent(activity.applicationContext,
activity.javaClass)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
val pendingIntent =
PendingIntent.getActivity(activity.applicationContext, 0,
intent, 0)
// val filters = arrayOfNulls<IntentFilter>(1)
// val techList = arrayOf<Array<String>>()
val ndef = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
val filters = arrayOf(ndef)
// val techList = arrayOf<Array<String>>()
val techList = arrayOf(arrayOf(NfcV::class.java.name))
// Notice that this is the same filter as in our manifest.
filters[0] = IntentFilter()
filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED)
filters[0].addCategory(Intent.CATEGORY_DEFAULT)
try {
filters[0].addDataType(MIME_TEXT_PLAIN)
}
catch (e : IntentFilter.MalformedMimeTypeException) {
throw RuntimeException("Check your mime type.")
}
adapter.enableForegroundDispatch(activity, pendingIntent,
filters, techList)
}
Like below :
private void enableReaderMode() {
showToast("Enabling reader mode...");
Activity activity = MainActivity.this;
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
if (nfcAdapter != null) {
nfcAdapter.enableReaderMode(activity, this,
Integer.MAX_VALUE, null);
setupForegroundDispatch(this,nfcAdapter);
}
}
Follow this tutorial for more info
As you are using enableReaderMode you don't need the onNewIntent method.
Also the intent-filter in the manifest and nfc_tech_filter.xml might not be required (They are not need if you don't want your app to be started by an NFC Tag if not running, then you would handle the NFC Intent in onCreate)
What you have not done is setup enableReaderMode correctly.
#Override
protected void onResume() {
super.onResume();
if(nfcAdapter!= null && nfcAdapter.isEnabled) {
Bundle options = new Bundle();
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);
nfcAdapter.enableReaderMode(this,
this,
NfcAdapter.FLAG_READER_NFC_A |
NfcAdapter.FLAG_READER_NFC_B |
NfcAdapter.FLAG_READER_NFC_F |
NfcAdapter.FLAG_READER_NFC_V |
NfcAdapter.FLAG_READER_NFC_BARCODE |
NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
options);
}
}
Turning off NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS is optional if you want to generate your own notification of reading instead of the OS doing it.
Note that the newer NFC API of enableReaderMode gives you more control that the older enableForegroundDispatch and is much more reliable than enableForegroundDispatch when writing to NFC Tags and it does not have some of the Negative side effects of enableForegroundDispatch, so I would recommend to always use enableReaderMode unless you want to support really old API's
Also note that using Mifare 4K cards is not recommended as well as they are a proprietary format and not supported by all Android Hardware (not matter which API you use)

Why is my Android NFC scanner not working

I have read many posts related to NFC scanning on Android, however I am unable to make it work.
The onNewIntent function never gets fired by my app sample.
In my activity, I have the following code:
//The array lists to hold our messages
private ArrayList<String> messagesToSendArray = new ArrayList<>();
private ArrayList<String> messagesReceivedArray = new ArrayList<>();
//Text boxes to add and display our messages
private EditText txtBoxAddMessage;
private TextView txtReceivedMessages;
private TextView txtMessagesToSend;
private NfcAdapter mNfcAdapter;
public void addMessage(View view) {
String newMessage = txtBoxAddMessage.getText().toString();
messagesToSendArray.add(newMessage);
txtBoxAddMessage.setText(null);
updateTextViews();
Toast.makeText(this, "Added Message", Toast.LENGTH_LONG).show();
}
private void updateTextViews() {
txtMessagesToSend.setText("Messages To Send:\n");
//Populate Our list of messages we want to send
if(messagesToSendArray.size() > 0) {
for (int i = 0; i < messagesToSendArray.size(); i++) {
txtMessagesToSend.append(messagesToSendArray.get(i));
txtMessagesToSend.append("\n");
}
}
txtReceivedMessages.setText("Messages Received:\n");
//Populate our list of messages we have received
if (messagesReceivedArray.size() > 0) {
for (int i = 0; i < messagesReceivedArray.size(); i++) {
txtReceivedMessages.append(messagesReceivedArray.get(i));
txtReceivedMessages.append("\n");
}
}
}
//Save our Array Lists of Messages for if the user navigates away
#Override
public void onSaveInstanceState(#NonNull Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putStringArrayList("messagesToSend", messagesToSendArray);
savedInstanceState.putStringArrayList("lastMessagesReceived",messagesReceivedArray);
}
//Load our Array Lists of Messages for when the user navigates back
#Override
public void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
messagesToSendArray = savedInstanceState.getStringArrayList("messagesToSend");
messagesReceivedArray = savedInstanceState.getStringArrayList("lastMessagesReceived");
}
private PendingIntent mPendingIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtBoxAddMessage = (EditText) findViewById(R.id.txtBoxAddMessage);
txtMessagesToSend = (TextView) findViewById(R.id.txtMessageToSend);
txtReceivedMessages = (TextView) findViewById(R.id.txtMessagesReceived);
Button btnAddMessage = (Button) findViewById(R.id.buttonAddMessage);
btnAddMessage.setText("Add Message");
updateTextViews();
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter != null) {
//Handle some NFC initialization here
}
else {
Toast.makeText(this, "NFC not available on this device",
Toast.LENGTH_SHORT).show();
}
//Check if NFC is available on device
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter != null) {
//This will refer back to createNdefMessage for what it will send
mNfcAdapter.setNdefPushMessageCallback(this, this);
//This will be called if the message is sent successfully
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
this.onNewIntent(getIntent());
}
#Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
#Override
protected void onPause() {
super.onPause();
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
}
#Override
public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
//This will be called when another NFC capable device is detected.
if (messagesToSendArray.size() == 0) {
return null;
}
//We'll write the createRecords() method in just a moment
NdefRecord[] recordsToAttach = createRecords();
//When creating an NdefMessage we need to provide an NdefRecord[]
return new NdefMessage(recordsToAttach);
}
#Override
public void onNdefPushComplete(NfcEvent nfcEvent) {
//This is called when the system detects that our NdefMessage was
//Successfully sent.
messagesToSendArray.clear();
}
public NdefRecord[] createRecords() {
NdefRecord[] records = new NdefRecord[messagesToSendArray.size()];
for (int i = 0; i < messagesToSendArray.size(); i++){
byte[] payload = messagesToSendArray.get(i).
getBytes(StandardCharsets.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[i] = record;
}
return records;
}
#Override
protected void onNewIntent(Intent intent) {
Log.e("TAG", "onNewIntent: "+intent.getAction());
super.onNewIntent(intent);
}
This is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<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:theme="#style/AppTheme" >
<activity
android:launchMode="singleTask"
android:name="com.banalapps.nfcmesssenger.MainActivity"
android:label="#string/app_name" >
<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" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
I am aware that this is probably a duplicate, but any help would be greatly appreciated as I struggled for days trying to make this work.

NFC Tag caught by android's default Tag Viewer instead of app

I've followed differents tutorials and read some fix related to that issue and still, nothing seems to work... Whenever I scan a NFC tag, it opens the default android app called Tag Viewer telling me "New tag collected" and with the body "Empty tag" and nothing happens within my app. I can't get any log printed...
Here is my manifest:
<activity
android:name=".activityv2.ActivityHome"
android:label="Home"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<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_tag_filter" />
</activity>
Here is my Activity:
public class ActivityHome extends GenericActivity implements BackHandledFragment.BackHandlerInterface, OnFragmentListener {
private final String TAG = "ActivityHome";
public static final String MIME_TEXT_PLAIN = "text/plain";
#Bind(R.id.drawer_layout)
DrawerLayout mDrawerLayout;
#Bind(R.id.actionToolbar)
Toolbar toolbar;
#Bind(R.id.left_drawer_item)
LinearLayout mDrawerLinear;
#Bind(R.id.left_drawer_child)
ListView mDrawerListChild;
#Bind(R.id.profil_pic)
CircleImageView mProfilPic;
private NfcAdapter mNfcAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_v2_home);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(getResources().getString(R.string.app_name));
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
// enable ActionBar app icon to behave as action to toggle nav drawer
handleIntent(getIntent());
}
private void handleIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
String type = intent.getType();
if (MIME_TEXT_PLAIN.equals(type)) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
new NdefReaderTask().execute(tag);
} else {
Log.d(TAG, "Wrong mime type: " + type);
}
} else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
// In case we would still use the Tech Discovered Intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String[] techList = tag.getTechList();
String searchedTech = Ndef.class.getName();
for (String tech : techList) {
if (searchedTech.equals(tech)) {
new NdefReaderTask().execute(tag);
break;
}
}
}
}
#Override
protected void onNewIntent(Intent intent) {
/**
* This method gets called, when a new Intent gets associated with the current activity instance.
* Instead of creating a new activity, onNewIntent will be called. For more information have a look
* at the documentation.
*
* In our case this method gets called, when the user attaches a Tag to the device.
*/
handleIntent(intent);
}
private class NdefReaderTask extends AsyncTask<Tag, Void, String> {
#Override
protected String doInBackground(Tag... params) {
Tag tag = params[0];
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
// NDEF is not supported by this Tag.
return null;
}
NdefMessage ndefMessage = ndef.getCachedNdefMessage();
NdefRecord[] records = ndefMessage.getRecords();
for (NdefRecord ndefRecord : records) {
if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
try {
return readText(ndefRecord);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unsupported Encoding", e);
}
}
}
return null;
}
private String readText(NdefRecord record) throws UnsupportedEncodingException {
/*
* See NFC forum specification for "Text Record Type Definition" at 3.2.1
*
* http://www.nfc-forum.org/specs/
*
* bit_7 defines encoding
* bit_6 reserved for future use, must be 0
* bit_5..0 length of IANA language code
*/
byte[] payload = record.getPayload();
// Get the Text Encoding
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
// Get the Language Code
int languageCodeLength = payload[0] & 0063;
// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
// e.g. "en"
// Get the Text
return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
}
#Override
protected void onPostExecute(String result) {
if (result != null) {
Log.e(TAG, result);
}
}
}
#Override
protected void onPause() {
super.onPause();
if (mNfcAdapter != null)
stopForegroundDispatch(this, mNfcAdapter);
}
#Override
protected void onResume() {
super.onResume();
if (mNfcAdapter != null)
setupForegroundDispatch(this, mNfcAdapter);
}
#Override
protected void onDestroy() {
getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
super.onDestroy();
}
public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
IntentFilter[] filters = new IntentFilter[1];
String[][] techList = new String[][]{};
// Notice that this is the same filter as in our manifest.
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
try {
filters[0].addDataType(MIME_TEXT_PLAIN);
} catch (IntentFilter.MalformedMimeTypeException e) {
throw new RuntimeException("Check your mime type.");
}
adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
}
public static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) {
adapter.disableForegroundDispatch(activity);
}
}
Here is the XML config to catch them all:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.NfcF</tech>
<tech>android.nfc.tech.NfcV</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NdefFormatable</tech>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>
And the message I get when I go near a tag and the default tag viewer opens:
04-20 12:45:26.825 799-2511/? I/ActivityManager: START u0 {flg=0x10008000 cmp=com.android.nfc/.NfcRootActivity (has extras)} from uid 1027 on display 0
04-20 12:45:26.874 1952-24878/? D/NativeNfcTag: Starting background presence check
04-20 12:45:26.913 1952-4642/? W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by client; transfer 4, track 16000 Hz, output 48000 Hz
04-20 12:45:26.917 799-24452/? I/ActivityManager: START u0 {act=android.nfc.action.TECH_DISCOVERED cmp=com.google.android.tag/com.android.apps.tag.TagViewer (has extras)} from uid 1027 on display 0
04-20 12:45:26.947 799-1287/? I/ActivityManager: Start proc 24893:com.google.android.tag/u0a18 for activity com.google.android.tag/com.android.apps.tag.TagViewer
I foudn the solution from #tatianag :
In first time I read a tutorial about nfc and I use some part of the code. And there was some code that I had not adapted to the advice you gave me.
This is the old part of my code
IntentFilter[] filters = new IntentFilter[1];
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_TECH_DISCOVERED);
try {
filters[0].addDataType(MIME_TEXT_PLAIN);
} catch (MalformedMimeTypeException e) {
Log.e("App","Wrong mime")
}
adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
Wrong action, mime type. Many errors... I change for
final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
adapter.enableForegroundDispatch(activity, pendingIntent, null, null);
And it works perfectly! I hope it can help someone like you helped me by your explanations.
Thank you again for your time!
Just would like to add that the PendingIntent needs to be mutable. As of SDK 31, this needs to be declared explicitly:
val mutabilityFlag = if (Build.VERSION.SDK_INT >= 31) PendingIntent.FLAG_MUTABLE else 0
val pendingIntent = PendingIntent.getActivity(context, 0, intent, mutabilityFlag)

Android NDEF message with two activities and wrong intent content

I'm new to NFC with Android and I'm trying to make a kind of messaging app with NFC.
I have a first activity that sends the content of an EditText view to the other phone when beaming and displays the incoming message on a TextView on the other phone. This works fine.
I have another activity which is used to add a contact to the contacts register, it should work as the following:
A wants to add B as a contact,
A goes to the AddContactActivity, enters the name of the contact in the EditText view,
then B touches A's phone (on the same activity) and sends their identifier (public key, for further encryption).
My problem is that even though the code concerning the sending via NFC is basically the same between the two activities, when I beam on the second activity (AddContactActivity), the action of the intent sent is ACTION_MAIN instead of ACTION_NDEF_DISCOVERED, which has the effect of opening the first activity and thus not going through the right treatment.
Here is the code of the MainActivity:
public class MainActivity extends Activity {
private TextView mTextView;
private EditText mEdit;
NfcAdapter nfcAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
mTextView = (TextView)findViewById(R.id.retour);
mEdit = (EditText)findViewById(R.id.editText);
nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
#Override public NdefMessage createNdefMessage(NfcEvent event) {
String stringOut = mEdit.getText().toString();
byte[] bytesOut = stringOut.getBytes();
NdefRecord ndefRecordOut = new NdefRecord(
NdefRecord.TNF_MIME_MEDIA,
"text/plain".getBytes(),
new byte[] {},
bytesOut);
NdefMessage ndefMessageout = new NdefMessage(ndefRecordOut);
return ndefMessageout;
}
}, this);
checkAndProcessBeamIntent(intent);
}
#Override
public void onResume() {
super.onResume();
Intent intent = getIntent();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("text/plain");
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
IntentFilter[] intentFiltersArray = new IntentFilter[] {ndef, };
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}
private void checkAndProcessBeamIntent(Intent intent) {
String action = intent.getAction();
if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)){
Parcelable[] parcelables =
intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage inNdefMessage = (NdefMessage)parcelables[0];
NdefRecord[] inNdefRecords = inNdefMessage.getRecords();
NdefRecord NdefRecord_0 = inNdefRecords[0];
String inMsg = new String(NdefRecord_0.getPayload());
mTextView.setText(inMsg);
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(MainActivity.this,
intent.getAction().toString(),
Toast.LENGTH_LONG).show();
checkAndProcessBeamIntent(intent);
}
#Override
public void onPause() {
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public void generateKeys(){
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
try {
kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
.setAlias("Keys")
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=test1"))
.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
kpg.generateKeyPair();
}
public void goToAddContact(View view) {
Intent intent = new Intent(this, AddContactActivity.class);
intent.setAction("NewActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
}
Here is the code of the AddContactActivity:
public class AddContactActivity extends Activity{
NfcAdapter nfcAdapter;
EditText editText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_contact);
editText = (EditText)findViewById(R.id.editText);
nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
Intent intent = getIntent();
nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
#Override public NdefMessage createNdefMessage(NfcEvent event) {
String stringOut = getMyPublicKey();
byte[] bytesOut = stringOut.getBytes();
NdefRecord ndefRecordOut = new NdefRecord(
NdefRecord.TNF_MIME_MEDIA,
"text/plain".getBytes(),
new byte[] {},
bytesOut);
NdefMessage ndefMessageout = new NdefMessage(ndefRecordOut);
return ndefMessageout;
}
}, this);
checkAndProcessBeamIntent(intent);
}
#Override
public void onResume() {
super.onResume();
Intent intent = getIntent();
Toast.makeText(AddContactActivity.this,
"onResume : "+intent.getAction().toString(),
Toast.LENGTH_LONG).show();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("text/plain");
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
IntentFilter[] intentFiltersArray = new IntentFilter[] {ndef, };
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}
public void addContactDataBase(String publicKey){
SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
sqLiteHelper.addUser(new User(editText.getText().toString(), publicKey));
}
public void checkUserInDataBase(String publicKey){
SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
User u = sqLiteHelper.getUser(publicKey);
Toast.makeText(AddContactActivity.this,
""+u.getName(),
Toast.LENGTH_LONG).show();
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(AddContactActivity.this,
"OnNewIntent : "+intent.getAction().toString(),
Toast.LENGTH_LONG).show();
checkAndProcessBeamIntent(intent);
}
#Override
public void onPause() {
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
}
private void checkAndProcessBeamIntent(Intent intent) {
String action = intent.getAction();
if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)){
Toast.makeText(AddContactActivity.this,
"COUCOU",
Toast.LENGTH_LONG).show();
Parcelable[] parcelables =
intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage inNdefMessage = (NdefMessage)parcelables[0];
NdefRecord[] inNdefRecords = inNdefMessage.getRecords();
NdefRecord NdefRecord_0 = inNdefRecords[0];
String inMsg = new String(NdefRecord_0.getPayload());
addContactDataBase(inMsg);
checkUserInDataBase(inMsg);
}
}
public String getMyPublicKey(){
KeyStore ks = null;
RSAPublicKey publicKey = null;
try {
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry)ks.getEntry("Keys", null);
publicKey = (RSAPublicKey) keyEntry.getCertificate().getPublicKey();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
}
return publicKey.toString();
}
}
And here is the manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bsauzet.testnfc" >
<uses-permission android:name="android.permission.NFC" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<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>
<activity
android:name=".AddContactActivity"
android:label="#string/title_activity_add_contact"
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>
</activity>
</application>
</manifest>
Check your appCompat libraries. Corruptness of appCompat libraries may cause wrong intent exceptions. Especially when you move your project from another PC or workspace (and/or IDE).
Getting an intent with ACTION_MAIN instead of ACTION_NDEF_DISCOVERED upon receiving an NFC event is a typical indication that an NDEF message containing an Android Application Record (AAR) was received and that the datatype of the first record of the NDEF message was not matched by any intent filter.
Your CreateNdefMessageCallback.createNdefMessage() method clearly does not add an AAR to the NDEF message. Consequently, the only reason why you would still receive an NDEF message containing an AAR would be that the createNdefMessage() raises an unhandled exception. In that case, the NFC stack will automatically generate an NDEF message containing a Play Store link and an AAR.
The most likely place that could cause createNdefmessage() to raise an unhandled exception is getMyPublicKey() (as that's the only part that differs between MainActivity and AddContactActivity).
So we can track the problem down to this part of your code:
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry)ks.getEntry("Keys", null);
publicKey = (RSAPublicKey) keyEntry.getCertificate().getPublicKey();
This code either
raises an unhandled runtime exception (e.g. if ks, keyEntry, or keyEntry.getCertificate() is null, or if ks.getEntry("Keys", null) cannot be cast to a KeyStore.PrivateKeyEntry), or
raises any of the handled exceptions, which causes publicKey to be null which results in an unhandled NullPointerException upon trying to invoke the toString() (in return publicKey.toString();).

NFC tag with text data and package name not working properly

I am developing small android application in which i wanted to use NFC functionality. I did this in following ways
On writer side :
boolean addAAR = true;
String uniqueId = "qrfid://nilkash";
byte[] uriField = uniqueId.getBytes(Charset.forName("US-ASCII"));
System.arraycopy(uriField, 0, payload, 0, uriField.length);
NdefRecord rtdUriRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], uriField);
if(addAAR)
{
return new NdefMessage(new NdefRecord[] {
rtdUriRecord, NdefRecord.createApplicationRecord("com.example.androidnfcurlreader")
});
And On receiver side I am using this in manifest file
<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" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/com.example.androidnfcurlreader" />
</intent-filter>
According to my few things are working fine like when i read tag it launch my application but few things are going wrong when i read tag it launch my application but not reading data from it. once app is opened I have to again read tag for getting data. I check for this so when application launch it shows intent action as main instead of NDEF_DISCOVERED. while application is open and if I read tag it shows me data and at that time action is NDEF_DISCOVERED. But what I want when i read tag it opens my application and mush show me data as well.
What to do? How to solve this problem? Am I doing something wrong? Please help. Thank you .
//Reading of data from tag is like this
// inside oncreate activity
handleIntent(intent);
// inside onresume
setupForegroundDispatch(activity, mNfcAdapter);
// inside on pause
stopForegroundDispatch(activity, mNfcAdapter);
// on new intent
handleIntent(intent);
private void handleIntent(Intent intent)
{
String action = intent.getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
String type = intent.getType();
if (MIME_TEXT_PLAIN.equals(type)) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
sendData(ndefmessage(tag));
} else {
Log.d(TAG, "Wrong mime type: " + type);
}
} else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
// In case we would still use the Tech Discovered Intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String[] techList = tag.getTechList();
String searchedTech = Ndef.class.getName();
for (String tech : techList) {
if (searchedTech.equals(tech)) {
//new NdefReaderTask().execute(tag);
ndefmessage(tag);
break;
}
}
}
}
private void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
IntentFilter[] filters = new IntentFilter[1];
String[][] techList = new String[][]{};
// Notice that this is the same filter as in our manifest.
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
try {
filters[0].addDataType(MIME_TEXT_PLAIN);
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("Check your mime type.");
}
adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
}
private static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter)
{
adapter.disableForegroundDispatch(activity);
}
private String ndefmessage(Tag tag)
{
Tag tag1 = tag;
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
// NDEF is not supported by this Tag.
return null;
}
NdefMessage ndefMessage = ndef.getCachedNdefMessage();
NdefRecord[] records = ndefMessage.getRecords();
for (NdefRecord ndefRecord : records) {
if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
try {
return readText1(ndefRecord);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unsupported Encoding", e);
}
}
}
return null;
}
private String readText1(NdefRecord record) throws UnsupportedEncodingException
{
byte[] payload = record.getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
int languageCodeLength = payload[0] & 0063;
return new String(payload, 0, payload.length, textEncoding);
}
private void sendData(String result)
{
eventlistsetter.getRfidListener().handleEvent(0, result);
}
}

Categories

Resources