I want to write different NDEF messages inside a while() loop.
LAST EDIT: It seems that the microcontroller can't process data so fast, so my problem cannot be solved.
//ndef.connect();
ndef.writeNdefMessage(message);
//ndef.close();
My write() method, simplified, without all try/catch
So, at first loop it works correctly, but next ones don't. But after a number of loops it works again for one more time. This repeats.
stop = 0;
while(stop < 1000)
{
write();
stop++
}
write() is working correctly for one loop.
EDIT: I replaced while() with a timer:
new Timer().schedule(new TimerTask() {
#Override
public void run() {
write();
}
}, 2000);
But this is too slow... I need to write at least 5 times per second.
If I set timer period less than 2000 it doesn't work, works same as while()
EDIT2: I measured how fast a message is transmitted and received. It seems it takes about 55ms to send a message, and about 7ms to receive. This is what I want, but if I set my timer to repeat after 100ms, for example, I have this error from writeNDEFmessage() :
java.io.IOException: Tag is not ndef . So if I loop 10 times writeNDEFmessage() it works fine at first loop but I receive exception at the following 9.
EDIT3:
onNewIntent() :
#Override
protected void onNewIntent(Intent intent)
{
try {
if(intent.getAction().equals(NfcAdapter.ACTION_TAG_DISCOVERED) ||
intent.getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED)||
intent.getAction().equals(NfcAdapter.ACTION_TECH_DISCOVERED))
{
detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if(detectedTag != lastDetectedTag)
{
lastDetectedTag = detectedTag;
setIntent(intent);
}
}
} catch (Exception e)
{
Log.e("", "onIntent >>> "+e.getMessage());
}
}
This is assigned to a button:
public void testWrite()
{
final Timer timer = new Timer();
try {
ndef = Ndef.get(detectedTag);
ndef.connect();
} catch (IOException e) {
Log.e("", "Cannot connect");
e.printStackTrace();
}
timer.schedule(new TimerTask() {
#Override
public void run() {
transmit.writeTag(message), ndef)
}, 0, 200);
}
and writeTag():
public boolean writeTag(String str, Ndef ndef) {
try {
message = getNdefMessage(str);
}
catch (Exception e)
{
toast("Message error");
}
int size = message.toByteArray().length;
try {
if (ndef != null) {
if(!ndef.isConnected())
{
ndef.connect();
Log.e("", ""+ndef.toString());
}
if (!ndef.isWritable()) {
return false;
}
if (ndef.getMaxSize() < size) {
toast("Tag capacity is " + ndef.getMaxSize() + " bytes, message is " + size + " bytes.");
return false;
}
try{
ndef.writeNdefMessage(message);
}
catch(IOException e){
toast("error send");
Log.e("IOException", e + "-+-");
return false;
}
return true;
}
}
catch (Exception e) {
toast("Failed to write tag");
}
return false;
}
transmit is an object from Transmit class, in which writeTag() is defined
onCreate():
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
transmit = new Transmit(this);
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
textView.setText("");
detectedTag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
lastDetectedTag = detectedTag;
pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
new Intent(this, getClass()).
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
readTagFilters = new IntentFilter[]{tagDetected,filter2};
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
if (mNfcAdapter == null) {
// Stop here, we definitely need NFC
finish();
return;
}
if (!mNfcAdapter.isEnabled()) {
}
buttonListener(testButton);
}
From the Ndef docs for the close() method: "Disable I/O operations to the tag from this TagTechnology object, and release resources."
So I think when you call close() the internal TagTechnology is released. If you call connect on the same ndef it's a "stale" object. Try creating a new Ndef object each time by passing it the Tag object.
Edit: Or else just don't call close() until you're actually finished. You will still need to call connect() the first time. Additionally I'd always call isConnected() first to ensure the tag is present and connected.
Related
I'm new to android, and get the project that I need to know how fast the the NFC Tag from the phone read the tag with timer. It's been 4 days and I still try to figure it out.
So this is how it works in my mind. TAG > Detected (timer start) > Information From Tag Show Up > Timer Stop.
Is it possible ? I know it will get around 0.1 miliseconds. I already can make the timer start when the tag detected, but it dont stop.
Here the java code :
public class Read extends Activity {
NfcAdapter mAdapter;
Tag mTag;
PendingIntent mPI;
IntentFilter mFilter[];
String userData,yo;
boolean writeMode;
Context context;
TextView tvNFCContent, Timer,Low;
Button start, pause, reset, lap ;
long MillisecondTime, StartTime, TimeBuff, UpdateTime = 0L ;
Handler handler;
int Seconds, Minutes, MilliSeconds ;
ListView listView ;
String[] ListElements = new String[] { };
List<String> ListElementsArrayList ;
ArrayAdapter<String> adapter ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.read);
tvNFCContent = (TextView) findViewById(R.id.data);
Timer = (TextView)findViewById(R.id.timer);
handler = new Handler() ;
mAdapter = NfcAdapter.getDefaultAdapter(this);
mPI = PendingIntent.getActivity(getApplicationContext(), 0,
new Intent(this,getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0 );
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
mFilter = new IntentFilter[]{tagDetected,filter2};
mAdapter = NfcAdapter.getDefaultAdapter(this);
if (mAdapter == null) {
// Stop here, we definitely need NFC
Toast.makeText(this, "This device doesn't support NFC.", Toast.LENGTH_LONG).show();
finish();
}
readFromIntent(getIntent());
}
/******************************************************************************
**********************************Read From NFC Tag***************************
******************************************************************************/
private void readFromIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))
{
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] msgs = null;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
buildTagViews(msgs);
}
}
private void buildTagViews(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) return;
String text = "";
// String tagId = new String(msgs[0].getRecords()[0].getType());
byte[] payload = msgs[0].getRecords()[0].getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16"; // Get the Text Encoding
int languageCodeLength = payload[0] & 0063; // Get the Language Code, e.g. "en"
// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
try {
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
TimeBuff += MillisecondTime;
handler.removeCallbacks(runnable);
} catch (UnsupportedEncodingException e) {
Log.e("UnsupportedEncoding", e.toString());
}
tvNFCContent.setText("NFC Content: " + text);
}
NdefMessage[] getNdefMessage(Intent intent)
{
NdefMessage[] msgs = null;
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];
}
}
return msgs;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
setIntent(intent);
readFromIntent(intent);
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
StartTime = SystemClock.uptimeMillis();
handler.postDelayed(runnable, 0);
}
super.onNewIntent(intent);
if(intent.getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED))
{
Toast.makeText(getApplicationContext(),"Ndefdiscovered",Toast.LENGTH_SHORT).show();
}else if(intent.getAction().equals(NfcAdapter.ACTION_TAG_DISCOVERED))
{
mTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Toast.makeText(getApplicationContext(),"Smartcard detected",Toast.LENGTH_SHORT).show();
NdefMessage[] messages = getNdefMessage(intent);
if(messages == null)
{
Toast.makeText(getApplicationContext(),"There Is No Data",Toast.LENGTH_SHORT).show();
return;
}
byte[] payload = messages[0].getRecords()[0].getPayload();
userData = new String(payload);
}else
{
Toast.makeText(getApplicationContext(),"Undefined smartcard",Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mAdapter.enableForegroundDispatch(this, mPI, mFilter, null);
}
public Runnable runnable = new Runnable() {
public void run() {
MillisecondTime = SystemClock.uptimeMillis() - StartTime;
UpdateTime = TimeBuff + MillisecondTime;
Seconds = (int) (UpdateTime / 1000);
Minutes = Seconds / 60;
Seconds = Seconds % 60;
MilliSeconds = (int) (UpdateTime % 1000);
Timer.setText("" + Minutes + ":"
+ String.format("%02d", Seconds) + ":"
+ String.format("%03d", MilliSeconds));
handler.postDelayed(this, 0);
}
};
}
And here the code to stop the timer, but I dont know where to put to stop the timer right on time :
TimeBuff += MillisecondTime;
handler.removeCallbacks(runnable);
With that method of reading you don't need to time how long your App spends actually reading the NFC tags as the time will always be exactly zero.
This is because the Android OS has fully read the data from the tag before the result is passed to your App.
There are ways to actually read the tag yourself if you really want to time reading the tag.
Update:
As you have not told me the Type of card you are using it is not possible to write the code to measure the time you want.
Some Background:
All NFC card operations at the low level will do 1 to N number of transceive operations, with each transceive operation sends a byte Array of 1 to N bytes and the returns a byte Array of 0 to N bytes.
For the raw read time you would time the time taken to run the right number of transeive command to read the data.
Timing of higher level operations would also include the time taken to parse the N number of byte Arrays in to a NdefMessage as well as the transceive commands
The code:
So as I don't know the card type the best I can do is get a time it takes to connect to the card and read the data and parse it to an NdefMessage
This is the timing of any operation that can cause RF activity and block further execution of the code.
The code ignores the fact the Android OS has already read the card and has passed you the card data with zero time in your app spent reading the card, it re-reads the card to get the timings. Note that if you take the card away to fast it can generate exceptions and fail to time reading the card.
I could have written the code to use enableReaderMode which is more reliable especial when writing to the card and has many other benefits. But instead I used ForegroundDispatch as the example code was using ForegroundDispatch, so I followed suit.
PS I would not recommend using ForegroundDispatch
package com.test.foregrounddispatch;
import androidx.appcompat.app.AppCompatActivity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
NfcAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = NfcAdapter.getDefaultAdapter(this);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mAdapter.disableForegroundDispatch(this);
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter ndefDetected = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndefDetected.addDataType("*/*");
} catch (IntentFilter.MalformedMimeTypeException e) {}
IntentFilter techDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
IntentFilter[] nfcIntentFilter = new IntentFilter[]{ndefDetected,techDetected,tagDetected};
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
if(mAdapter!= null)
mAdapter.enableForegroundDispatch(this, pendingIntent, nfcIntentFilter, null);
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
// While with ForegroundDispatch the NDEF message has already been read
// And passed to us in the intent and thus the time the App spends "read" the NFC card is Zero
// We want to time time the read, so now we have been notified that a NDEF card is in range
// Try and read from it
// Get the Tag from the intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
long startTime = 0;
long endTime = 0;
try {
// Create an NDEF tag object
Ndef ndefTag = Ndef.get(tag);
// This is the I/O operation to read the card and format the result to an NDEF message
// Nothing is done with the result to not add any time to it, as we are timing this
startTime = System.currentTimeMillis();
ndefTag.connect();
ndefTag.getNdefMessage();
endTime = System.currentTimeMillis();
ndefTag.close();
} catch (Exception e) {
Log.e("NFC", e.toString());
}
Log.v("NFC", "Time to read in milliseconds is: " + (endTime - startTime));
}
}
}
For a short "Hello" plain text NDEF record on my phone it produces the log:-
V/NFC: Time to read in milliseconds is: 18
I want to read an NFC tag until the device moves away from the NFC tag. Then I want to do some activities. I have managed to do this using a while loop to read the tag and catching the InterruptedException. And I also want to update the UI while reading the tag inside the while loop. I couldn't find a way to update the UI when I'm in the while loop.
The data to update the UI comes from an onLocationChanged-listener.
public void onLocationChanged(Location location) {
if (location.hasSpeed()) {
/*double speed=location.getSpeed() * 3.6;;
while (1==1)
{*/
speed = location.getSpeed() * 3.6;
String units="km/h";
s= new SpannableString(String.format(Locale.ENGLISH, "%.0f %s", speed, units));
s.setSpan(new RelativeSizeSpan(0.45f), s.length()-units.length()-1, s.length(), 0);
updateUI();
}
}
public void updateUI(){
drivingMode=findViewById(R.id.txtDriving);
currentSpeed = findViewById(R.id.valSpeed);
if (currentSpeed!=null) {
currentSpeed.setText(s);
if (speed > 10) {
drivingMode.setText(R.string.msg_driving);
isDriving = true;
} else {
drivingMode.setText(R.string.msg_notDriving);
isDriving=false;
}
}
}
private void readFromNFC( Ndef ndef) {
try
{
ndef.connect();
NdefMessage ndefMessage = ndef.getNdefMessage();
ndef.close();
String message = new String(ndefMessage.getRecords()[0].getPayload());
// Log.d(TAG, "readFromNFC Before Pass: " + message);
//Toast.makeText(this, "Text" + message, Toast.LENGTH_LONG).show();
if (message.equals("in")) {
Toast.makeText(this.getApplicationContext(), R.string.message_nfc_holder_detected, Toast.LENGTH_LONG).show();
if (isDialogshowing) {
dialog.dismiss();
isEmergency=false;
}
while (1 == 1) {
ndef.connect();
ndefMessage = ndef.getNdefMessage();
message = new String(ndefMessage.getRecords()[0].getPayload());
//Log.d(TAG, "readFromNFCPassed: " + message);
TimeUnit.SECONDS.sleep(1);
ndef.close();
updateUI();
}
} else {
Toast.makeText(this.getApplicationContext(), R.string.message_nfc_holder_error, Toast.LENGTH_LONG).show();
ndef.close();
}
} catch (IOException | FormatException | InterruptedException e ) {
e.printStackTrace();
Toast.makeText(this.getApplicationContext(), R.string.message_nfc_holder_detached, Toast.LENGTH_LONG).show();
if(isDriving) {
activateEmergency();
}
else
{
if (isDialogshowing) {
dialog.dismiss();
dialog.dismiss();
isDialogshowing = false;
}
}
}
}
In order to continuously read the UI thread without blocking the main UI thread, you could use an AsyncTask. This also allows you to publish status updates to the UI (thread) using an onProgressUpdate() callback.
You AsyncTask could look something like this:
private class ProcessNFCTask extends AsyncTask<Ndef, NdefMessage, Void> {
protected Void doInBackground(Ndef... tag) {
// This happens on the worker thread!
// TODO: content of readFromNFC(Ndef ndef);
// Instead of calling updateUI(), you would call publishProgress(message).
// You can use whatever represents your progress instead of "message".
// Instead of doing your finalization stuff (what you currently have
// in the catch clause) here, you would want to do that in
// onPostExecute() instead.
}
protected void onProgressUpdate(NdefMessage... progress) {
// This happens on the UI thread!
// You may also use the progress status information provided in
// "progress" here. This is what you pass to publishProgress(...).
updateUI();
}
protected void onPostExecute(Void result) {
// This happens on the UI thread!
// TODO: Whatever you want to do when you are finished reading, e.g.:
if (isDriving) {
activateEmergency();
} else {
if (isDialogshowing) {
dialog.dismiss();
dialog.dismiss();
isDialogshowing = false;
}
}
}
}
I'm trying to implement a way to listen to a client's connection event on the smartphone hotspot. I see that android.net.wifi.WIFI_HOTSPOT_CLIENTS_CHANGED is no longer avaible. How can i do this? I think that this is possible because the smartphone notify me when i client make a connection to the smartphone hotspot.
You can't use the Intent Action...You have to use a custom method, i'l suggest you create a background thread that checks/reads the I.P table (/proc/net/arp) constantly and update you...here's a snippet I've used.
Read i.p list table
public ArrayList<String> getConnectedDevices() {
ArrayList<String> arrayList = new ArrayList();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"));
while (true) {
String readLine = bufferedReader.readLine();
if (readLine == null) {
break;
}
String[] split = readLine.split(" +");
if (split != null && split.length >= 4) {
arrayList.add(split[0]);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return arrayList;
}
Create runnable to check
class CheckHotSpotConnection implements Runnable {
private CheckHotSpotConnection() {
}
public void run() {
int i = 0;
while (discoverClient()) {
i = getConnectedDevices().size();
if (i > 1) {
//client discovered
//disable client discovery to end thread
} else {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Start Thread
new Thread(new CheckHotSpotConnection()).start();
I have an app that captures NFC tags. The problem i have had in the past is that users hover over the tag in an unsteady manner causeing the NFC adapter to trigger twice.
I have done a few things to counter this.
manifest:
<activity
android:name=".NfcActivity"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:noHistory="true"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation">
<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>
This sets the NFC capturing Activity to be the only instance in the stack and there to be no history. I've overridden all the config changes that can stop and relaunch this activity, the latter can lead to intent data being redelivered to the activity, making it look like a duplicate scan.
In the Activity itself i have overridden onNewIntent to do nothing but show a bad scan screen. I also understand that onNewIntent should mirror onCreate from a functionality standpoint, but because previous versions of the app have sent 2 scans to the next Activity, i just want the NFC capturing code in one place, onCreate.
In onCreate i do further tests to counter against hovering over a tag and creating a bad scan.
I check the launched form history flag in the intent. Android can kill an app when low on memory and relaunch later REDELIVERING the original intent. This can cause what seems like a duplicate scan.
In onCreate i check that the user STILL has the phone connected to the tag. This proves the user is not hovering over the tag with ndefTag.connect();
The app seems to work fine but on one particular phone (Samsung Galaxy Young 2), if the user places the phone on the tag for say a few seconds the the NFC adapter seems to fire a few times in a row.
When this happens the original scan is cancelled. The reason for this is oncreate processes the tag but when a subsequent scan happens(by hovering, by accident), onPause -> onNewIntent runs. So the Activity jumps out of onCreate and stops processing the tag. onNewIntent shows a failed scan screen and launches the menu screen.
The above isn't too much of a problem as all that happens is the user must re-scan the tag.
What i would like to happen is:
When onCreate runs, the tag is processed no matter what, even if onNewintent executes. Is there a way maybe to intercept the intent before is reaches onNewintent and onPause?
Maybe there is a global flag i can use, that can be checked first to say that onCreate is still running and onNewIntent shouldn't, or more importantly onPause isn't called making onCreate stop running.
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.ndeftools.Message;
import org.ndeftools.Record;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.os.Vibrator;
import android.util.Log;
import android.widget.Toast;
public class NfcActivity extends Activity {
private static final String TAG = NfcActivity.class.getName();
protected NfcAdapter nfcAdapter;
protected PendingIntent nfcPendingIntent;
Handler handler;
Runnable runnable;
Handler failHandler;
Runnable failRunnable;
Parcelable[] messages;
Intent i;
Tag tag;
String tagId;
boolean nfcConnected;
ProgressDialog progressDialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.nfcactivitylayout);
Log.e(TAG, "oncreate");
nfcConnected = false;
// initialize NFC
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
nfcPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
tag = null;
tagId = null;
i = getIntent();
if ((i.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
//check to see if Android has previously killed the app and relaunched it from History
//and delivered the original intent.
//if it has do not process and launch the menu screen
Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
processPayloadIntent.setAction("QRCODE_ACTION");
processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(processPayloadIntent);
}else{
tag = i.getParcelableExtra(NfcAdapter.EXTRA_TAG);
tagId = bytesToHexString(tag.getId());
Log.e(TAG, "tagID = " + tagId);
Log.e(TAG, "oncreate intent action = " + i.getAction());
//The activity has captured tag data, prove the user is not hovering over the tag and is doing a good scan
//hovering can trigger the adapter twice
AsyncNfcConnect asnc = new AsyncNfcConnect();
try {
asnc.execute().get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.e(TAG, "nfcConnected!!!!!!!!!!!!!!!!!!!!!!!!! = " + nfcConnected);
if(nfcConnected == true){
int buildVersionSdk = Build.VERSION.SDK_INT;
int buildVersionCodes = Build.VERSION_CODES.GINGERBREAD;
Log.e(TAG, "buildVersionSdk = " + buildVersionSdk
+ "buildVersionCodes = " + buildVersionCodes);
int themeVersion;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {
themeVersion = 2;
} else {
themeVersion = 1;
}
try{
progressDialog = new ProgressDialog(this, themeVersion);
progressDialog.setTitle("NFC Tag Scanned");
progressDialog.setMessage("Processing tag...");
progressDialog.setIndeterminate(true);
progressDialog.show();
}catch(Exception e){ }
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(i.getAction()) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(i.getAction())) {
if(NfcScannerApplication.isCanScanNfcTag()){
messages = i.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (messages != null) {
//setContentView(R.layout.successfulnfc);
NfcScannerApplication.startNfcTimer();
//Toast.makeText(this, "NFC timer set", Toast.LENGTH_LONG).show();
Log.e(TAG, "Found " + messages.length + " NDEF messages"); // is almost always just one
vibrate(); // signal found messages :-)
initHandler();
handler.postDelayed(runnable, 2000);
}else{
Toast.makeText(this, "Data on tag was not correct", Toast.LENGTH_LONG).show();
try{
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}else{
try{
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
} else {
Toast.makeText(this, "Tag not recognized correctly", Toast.LENGTH_LONG).show();
try{
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}else{
try{
Toast.makeText(this, "Phone wasn't connected to Tag", Toast.LENGTH_LONG).show();
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}//end of NFC connect test
}//end of launched from history check
}//end of onCreate
#Override
protected void onStart() {
super.onStart();
Log.e(TAG, "onStart");
}
#Override
protected void onStop() {
super.onStop();
Log.e(TAG, "onStop");
}
#Override
public void onNewIntent(Intent intent) {
Log.e(TAG, "onNewIntent");
Toast.makeText(this, "Bad scan!!!", Toast.LENGTH_LONG).show();
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}//end of onNewIntent
public void enableForegroundMode() {
Log.e(TAG, "enableForegroundMode");
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); // filter for all
IntentFilter[] writeTagFilters = new IntentFilter[] {tagDetected};
nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null);
}
public void disableForegroundMode() {
Log.e(TAG, "disableForegroundMode");
nfcAdapter.disableForegroundDispatch(this);
}
#Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume");
enableForegroundMode();
}
#Override
protected void onPause() {
Log.e(TAG, "onPause");
super.onPause();
disableForegroundMode();
if(handler != null){
handler.removeCallbacks(runnable);
}
}
private void vibrate() {
Log.e(TAG, "vibrate");
Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE) ;
vibe.vibrate(500);
}
public void initHandler(){
handler = new Handler();
runnable = new Runnable() {
public void run() {
processTag();
}
private void processTag() {
Log.e(TAG, "about to process tag");
try{
progressDialog.dismiss();
}catch(Exception e){
//do nothing
}
// parse to records
for (int i = 0; i < messages.length; i++) {
try {
List<Record> records = new Message((NdefMessage)messages[i]);
Log.e(TAG, "Found " + records.size() + " records in message " + i);
for(int k = 0; k < records.size(); k++) {
Log.e(TAG, " Record #" + k + " is of class " + records.get(k).getClass().getSimpleName());
Record record = records.get(k);
NdefRecord ndefRecord = record.getNdefRecord();
byte[] arr = ndefRecord.getPayload();
String payload = new String(arr);
if(payload.length() > 0){
payload = payload.substring(3, payload.length());
Log.e(TAG, "payload = " + payload);
String[] splitPayload = payload.split(",");
String tagType = splitPayload[0];
String tagCompany = splitPayload[1];
String tagClientID = splitPayload[2];
String tagClientName = splitPayload[3];
if(! tagClientID.equalsIgnoreCase("0") && tagClientID.length() > 0){
handler.post(new Runnable(){
public void run() {
setContentView(R.layout.successfulnfc);
}
});
Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
processPayloadIntent.putExtra("payload", payload);
processPayloadIntent.putExtra("tagid", tagId);
processPayloadIntent.setAction("NFC");
processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//processPayloadIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(processPayloadIntent);
finish();
overridePendingTransition(0, R.anim.activity_animation_zoom_in);
}else{
Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}else{
Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}
} catch (Exception e) {
Log.e(TAG, "Problem parsing message", e);
}
}
}
};
}
public void initFailHandler(){
failHandler = new Handler();
failRunnable = new Runnable() {
public void run() {
returnToMainMenu();
}
private void returnToMainMenu() {
//Log.e(TAG, "about to return to main menu");
try{
progressDialog.dismiss();
}catch(Exception e){
//do nothing
}
Toast.makeText(NfcActivity.this, "Please check your scanning technique.\nPlease do not hover over tag or swipe...", Toast.LENGTH_LONG).show();
failHandler.post(new Runnable(){
public void run() {
setContentView(R.layout.nfcfail);
}
});
//onBackPressed();
Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
processPayloadIntent.setAction("QRCODE_ACTION");
processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(processPayloadIntent);
finish();
//overridePendingTransition(0, R.anim.activity_animation_zoom_in);
}
};
}
private String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("0x");
if (src == null || src.length <= 0) {
return null;
}
char[] buffer = new char[2];
for (int i = 0; i < src.length; i++) {
buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
System.out.println(buffer);
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
private class AsyncNfcConnect extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
NfcActivity.this.nfcConnected = false;
String result;
Ndef ndefTag = Ndef.get(tag);
try {
Log.e(TAG, "about to test connect()********************************************");
ndefTag.connect(); // this should already perform an IO operation and should therefore fail if there is no tag
Log.e(TAG, "Ndef.connect() connected!********************************************");
NdefMessage ndefMsg = ndefTag.getNdefMessage(); // this reads the current NDEF message from the tag and consequently causes an IO operation
NfcActivity.this.nfcConnected = true;
result = "OK";
return result;
} catch (Exception e) {
// there is no tag or communication with tag dropped
Log.e(TAG, "There a problem with connecting to the tag using Ndef.connect(");
NfcActivity.this.nfcConnected = false;
result = "NOTOK";
return result;
} finally {
try {
ndefTag.close();
} catch (Exception e) {
}
}
}
}//end of Async
}
You seem to insist on not handling NFC intents in onNewIntent(). I would suggest that onCreate() and onNewIntent() both call a common procedure for scanning the tag. In that way, both entry points would follow the same code path.
Your claim that the app "jumps out of onCreate" is probably just a figure of speech? What happens is that your tag scanning AsyncNfcConnect runs on a separate thread as a background task (as it should). That task is created in onCreate() and continues running after onCreate() has finished (you could add add a Log statement at the end of onCreate() to check). When the connection with the tag is lost somehow and the tag is rediscovered, onNewIntent() is called, as you observed.
In any case there is no way to prevent this from happening, so your app has to be able to handle it. To detect this and deal with it, you could set some flag inside your activity to indicate that your background task is running or has run. You could also store the information whether an exception has occurred and simply try scanning the tag again when it is rediscovered (this probably requires that you implement the suggestion I made above). If you want to make your app even more failure proof, you could also store the ID of the last scanned tag to positively identify it again when it is rediscovered after you have successfully scanned it (or not). When exceptions keep occurring with the same tag, you could indicate his after a certain number of times to the user (e.g. by suggesting that the device be positioned differently w.r.t. the tag).
I wrote a custom plugin to read blocks of data from an NfcA(i.e.non-ndef) tag. It seems to work fine , but only after the second scan. I am using Activity intent to derive the "NfcAdapter.EXTRA_TAG" to later use it for reading the values. I am also updating the Intents in onNewIntent(). OnNewIntent gets called after the second scan and after that I get result all the time.But in the first scan onNewIntent does not gets called, hence I end up using the Activity tag that does not have "NfcAdapter.EXTRA_TAG", hence I get null. Please see the my code below.
SE_NfcA.java(my native code for plugin)
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
String Result = "";
String TypeOfTalking = "";
if (action.contains("TalkToNFC"))
{
JSONObject arg_object = args.getJSONObject(0);
TypeOfTalking = arg_object.getString("type");
if(TypeOfTalking != "")
{
if (TypeOfTalking.contains("readBlock"))
{
if(TypeOfTalking.contains("#"))
{
try
{
String[] parts = TypeOfTalking.split("#");
int index = Integer.parseInt(parts[1]);
Result = Readblock(cordova.getActivity().getIntent(),(byte)index);
callbackContext.success(Result);
}
catch(Exception e)
{
callbackContext.error("Exception Reading "+ TypeOfTalking + "due to "+ e.toString());
return false;
}
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
return true;
}
#Override
public void onNewIntent(Intent intent) {
ShowAlert("onNewIntent called");
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
super.onNewIntent(intent);
getActivity().setIntent(intent);
savedTag = tagFromIntent;
savedIntent = intent;
}
#Override
public void onPause(boolean multitasking) {
Log.d(TAG, "onPause " + getActivity().getIntent());
super.onPause(multitasking);
if (multitasking) {
// nfc can't run in background
stopNfc();
}
}
#Override
public void onResume(boolean multitasking) {
Log.d(TAG, "onResume " + getActivity().getIntent());
super.onResume(multitasking);
startNfc();
}
public String Readblock(Intent Intent,byte block) throws IOException{
byte[] response = new byte[]{};
if(Intent != null)
{
Tag myTag = Intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if(savedTag != null)
myTag = savedTag;
if(myTag != null)
{
try{
Reader nTagReader = new Reader(myTag);
nTagReader.close();
nTagReader.connect();
nTagReader.SectorSelect(Sector.Sector0);
response = nTagReader.fast_read(block, block);
nTagReader.close();
return ConvertH(response);
}catch(Exception e){
ShowAlert(e.toString());
}
}
else
ShowAlert("myTag is null.");
}
return null;
}
private void createPendingIntent() {
if (pendingIntent == null) {
Activity activity = getActivity();
Intent intent = new Intent(activity, activity.getClass());
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(activity, 0, intent, 0);
}
}
private void startNfc() {
createPendingIntent(); // onResume can call startNfc before execute
getActivity().runOnUiThread(new Runnable() {
public void run() {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
if (nfcAdapter != null && !getActivity().isFinishing()) {
try {
nfcAdapter.enableForegroundDispatch(getActivity(), getPendingIntent(), getIntentFilters(), getTechLists());
if (p2pMessage != null) {
nfcAdapter.setNdefPushMessage(p2pMessage, getActivity());
}
} catch (IllegalStateException e) {
// issue 110 - user exits app with home button while nfc is initializing
Log.w(TAG, "Illegal State Exception starting NFC. Assuming application is terminating.");
}
}
}
});
}
private void stopNfc() {
Log.d(TAG, "stopNfc");
getActivity().runOnUiThread(new Runnable() {
public void run() {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
if (nfcAdapter != null) {
try {
nfcAdapter.disableForegroundDispatch(getActivity());
} catch (IllegalStateException e) {
// issue 125 - user exits app with back button while nfc
Log.w(TAG, "Illegal State Exception stopping NFC. Assuming application is terminating.");
}
}
}
});
}
private Activity getActivity() {
return this.cordova.getActivity();
}
private PendingIntent getPendingIntent() {
return pendingIntent;
}
private IntentFilter[] getIntentFilters() {
return intentFilters.toArray(new IntentFilter[intentFilters.size()]);
}
private String[][] getTechLists() {
//noinspection ToArrayCallWithZeroLengthArrayArgument
return techLists.toArray(new String[0][0]);
}
}
My index.js file
nfc.addTagDiscoveredListener(
function(nfcEvent){
console.log(nfcEvent.tag.id);
alert(nfcEvent.tag.id);
window.echo("readBlock#88");//call to plugin
},
function() {
alert("Listening for NFC tags.");
},
function() {
alert("NFC activation failed.");
}
);
SE_NfcA.js(plugin interface for interaction b/w index.js and SE_NfcA.java)
window.echo = function(natureOfTalk)
{
alert("Inside JS Interface, arg =" + natureOfTalk);
cordova.exec(function(result){alert("Result is : "+result);},
function(error){alert("Some Error happened : "+ error);},
"SE_NfcA","TalkToNFC",[{"type": natureOfTalk}]);
};
I guess I have messed up with the Intents/Activity Life-Cycle, please help. TIA!
I found a tweak/hack and made it to work.
Before making any call to read or write, I made one dummy Initialize call.
window.echo("Initialize");
window.echo("readBlock#88");//call to plugin to read.
And in the native code of the plugin, on receiving the "Initialize" token I made a startNFC() call.
else if(TypeOfTalking.equalsIgnoreCase("Initialize"))
{
startNfc();
}