【发布时间】:2015-10-05 23:22:36
【问题描述】:
我需要从 NfcV (ISO 15693) 标签中读取数据,我已经尝试过 Chariotsolution 的 Phonegap-Nfc 插件,但似乎这个插件只能读取 TagId 和 Type off NfcV 标签,所以我决定我可以创建我自己的自定义插件,我首先找到了 echo Hello World 插件教程,它运行良好且没有问题,所以我寻找 Java NfcV 阅读器并找到了这个Java NfcV Reader。
据我目前了解的是,我需要调用 cordova.exec 函数并从 CordovaPlugin 扩展我的 Java 类,它正在工作。
由于我是一名 Web 开发人员,所以我不懂 Java,这对我来说有点困难。
这实际上看起来不错,但我尝试以 Hello World echo 示例的方式实现它,但没有按计划进行。
我现在有一个插件文件夹
Hello.java
package org.apache.cordova.plugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
/**
* This class echoes a string called from JavaScript.
*/
public class Hello extends CordovaPlugin
{
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("hello")) {
String message = args.getString(0);
this.hello(message, callbackContext);
return true;
}
return false;
}
private void hello(String message, CallbackContext callbackContext) {
if (message != null && message.length() > 0) {
callbackContext.success(message);
} else {
callbackContext.error("Expected one non-empty string argument.");
}
}
}
从上面的链接中读取NfcV.java
package org.apache.cordova.plugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import org.apache.http.util.ByteArrayBuffer;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.nfc.tech.NfcV;
import android.nfc.tech.TagTechnology;
//import android.os.Parcelable;
import android.util.Log;
/**
* @author uhahn
*
*/
public class ReadNfcV extends CordovaPlugin implements TagTechnology {
protected NfcV mynfcv;
// protected Tag mytag; // can be retrieved through mynfcv
private final String TAG=this.getClass().getName();
protected final int maxtries=3;
protected boolean isTainted=true; // Tag info already read?
protected byte[] mysysinfo=null; // NfcV SystemInformation - or generated
protected byte[] myuserdata=null; // buffer user content
protected boolean[] blocktainted; // true when block is to be uploaded to tag
protected byte[] blocklocked; // 0 means writable
protected byte afi=0;
public byte nBlocks=0;
public byte blocksize=0;
public byte[] Id;
public byte[] UID; // becomes valid when a real tag is contacted
public byte DSFID = -1;
public int maxtrans=0; // tag dependent max transceive length
public byte lastErrorFlags=-1; // re-set by each transceive
public byte lastErrorCode=-1; // re-set by each transceive
public byte manuByte=0;
public static final byte BYTE_IDSTART=(byte)0xe0;
public static final byte MANU_TAGSYS=0x04;
public static final HashMap<Byte,String> manuMap = new HashMap<Byte, String>();
static{
manuMap.put(MANU_TAGSYS, "TagSys");
}
/**
* read new NfcV Tag from NFC device
*/
public ReadNfcV(Tag t) {
UID = t.getId(); // sysinfo holds the UID in lsb order - Id will be filled lateron from sysinfo!
// Log.d(TAG,"getId: "+toHex(t.getId()));
mynfcv=NfcV.get(t);
try {
mynfcv.connect();
mysysinfo=getSystemInformation();
// explore Nfcv properties..
//initfields(); // done by getSys..
maxtrans=mynfcv.getMaxTransceiveLength();
DSFID=mynfcv.getDsfId();
Log.d(TAG,nBlocks + " x " + blocksize + " bytes");
blocklocked=new byte[nBlocks]; // init the lock shadow
getMultiSecStatus(0, nBlocks); // and fill from tag
blocktainted=new boolean[nBlocks];
taintblock(0,nBlocks);
// Log.d(TAG,"maxtrans "+maxtrans);
// init space for userdata ?
myuserdata= new byte[nBlocks*blocksize];
} catch (IOException e) {
// TODO Auto-generated catch block
lastErrorFlags=-1;
Log.d(TAG, "MyNfcV failed: "+e.getMessage());
e.printStackTrace();
}
}
/**
* recreate NfcV Tag from log
* @param sysinfo: the logged system info only
*/
public ReadNfcV(String sysinfo){
int startat=0;
sysinfo.toLowerCase(); // ignore case
if(sysinfo.startsWith("0x")){ // lets believe in HEX
startat=2;
}
mysysinfo=hexStringToByteArray(sysinfo.substring(startat));
initfields();
// init space for userdata TODO limit size?
//myuserdata= new byte[nBlocks*blocksize];
isTainted=false;
// TODO fake Tag? mytag = Tag.CREATOR.createFromParcel(???);
}
/**
* recreate NfcV Tag from log
* @param sysinfo: the logged system info
* @param userdata: the logged userdata
*/
public ReadNfcV(String sysinfo, String userdata){
this(sysinfo);
// TODO fake userdata
int startat=0;
userdata.toLowerCase(); // ignore case
if(userdata.startsWith("0x")){ // lets believe in HEX
startat=2;
}
myuserdata=hexStringToByteArray(userdata.substring(startat));
}
/**
* parse system information byte array into attributes
* with respect to the flags found
* DSFID
* AFI
* memsize values (block count and length)
*/
private void initfields(){
byte[] read=mysysinfo;
if((null!=read)&&(12<read.length)&&(0==read[0])){// no error
char flags=(char)read[1]; //s.charAt(1);
// String s=new String(read);
//s.substring(2, 9).compareTo(Id.toString()) // the same?
//set the Id from mysysinfo
int pos=2;
boolean forwardId=false; // the Id field is in lsb order
if(BYTE_IDSTART==read[pos]){
forwardId=true;
manuByte=read[pos+1];
}else if(BYTE_IDSTART==read[pos+7]){
manuByte=read[pos+6];
forwardId=false;
}else
Log.e(TAG,"Id start byte not found where expected");
if(null==Id){ // dont overwrite, if given
Id=new byte[8];
for(int i=0;i<8;i++)
// TODO decide if Id to be reversed (Zebra needs msb order, that is Id[7] changes between tags)
Id[i]=(forwardId? read[pos+i] : read[pos + 7 - i]); //reverse?!
Log.d(TAG,"Id from sysinfo (reversed): "+toHex(Id));
}
pos=10; // start after flags, Infoflags and Id TODO: change if transceive should eat up the error byte
if(0<(flags&0x1)){ // DSFID valid
pos++; // already implemented
}
if(0<(flags&0x2)){ // AFI valid
afi=(byte)read[pos++];//s.charAt(pos++);
}
if(0<(flags&0x4)){ // memsize valid
nBlocks=(byte)(read[pos++]+1);//(s.charAt(pos++)+1);
blocksize=(byte)(read[pos++]+1); //((s.charAt(pos++)&0x1f)+1);
}
}
}
/**
* @return the stored afi byte
*/
public byte getAFI(){
if(isTainted){ // system info not read yet
getSystemInformation(); // fill in the fields
}
return afi;
}
public byte getDsfId(){
// return mynfcv.getDsfId(); // avoid re-reading
return DSFID;
}
public int getblocksize(){
return (int)blocksize;
}
public int getnBlocks(){
return (int)nBlocks;
}
public byte[] getSystemInformation(){
if(isTainted){ // dont reread
mysysinfo=transceive((byte)0x2b);
isTainted=false; // remember: we have read it and found it valid
if(0==lastErrorFlags){// no error
isTainted=false; // remember: we have read it and found it valid
initfields(); // analyze
}}
return mysysinfo;
}
/**
* overload method transceive
* @return resulting array (or error?)
*/
protected byte[] transceive(byte cmd){
return transceive(cmd, -1, -1, null);
}
protected byte[] transceive(byte cmd, int m){
return transceive(cmd, m, -1, null);
}
protected byte[] transceive(byte cmd, int m ,int n){
return transceive(cmd, m, n, null);
}
/**
* prepare and run the command according to NfcV specification
* @param cmd command byte
* @param m command length
* @param n
* @param in input data
* @return
*/
protected byte[] transceive(byte cmd,int m, int n, byte[] in){
byte[] command;
byte[] res="transceive failed message".getBytes();
ByteArrayBuffer bab = new ByteArrayBuffer(128);
// flags: bit x=adressed,
bab.append(0x00);
bab.append(cmd); // cmd byte
// 8 byte UID - or unaddressed
// bab.append(mytag.getId(), 0, 8);
// block Nr
if(-1!=m)bab.append(m);
if(-1!=n)bab.append(n);
if(null!=in)bab.append(in, 0, in.length);
command=bab.toByteArray();
Log.d(TAG,"transceive cmd: "+toHex(command));
// Log.d(TAG,"transceive cmd length: "+command.length);
// TODO background!
try {
if(!mynfcv.isConnected()) return res;
for(int t=maxtries;t>0;t++){ // retry reading
res=mynfcv.transceive(command);
if(0==res[0]) break;
}
}
catch (TagLostException e){ //TODO roll back user action
Log.e(TAG, "Tag lost "+e.getMessage());
try {
mynfcv.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return e.getMessage().getBytes();
}
catch (IOException e) {
Log.d(TAG, "transceive IOEx: "+e.getMessage()+toHex(res));
// e.printStackTrace();
return e.getMessage().getBytes();
}
finally{
Log.d(TAG,"getResponseFlags: "+mynfcv.getResponseFlags());
lastErrorFlags=res[0];
Log.d(TAG,"Flagbyte: "+String.format("%2x", lastErrorFlags));
if(0!=lastErrorFlags){
lastErrorCode=res[1];
Log.d(TAG,"ErrorCodebyte: "+String.format("%2x", lastErrorCode));
}
}
if(0==mynfcv.getResponseFlags())
return (res);
else
// return new String("response Flags not 0").getBytes();
return res;
}
public void taintblock(int i, int n){
for(int j=0;j<n;j++)
setblocktaint(j,true);
}
public void taintblock(int i){
setblocktaint(i,true);
}
protected void setblocktaint(int i, boolean b){
blocktainted[i]=b;
}
/* (non-Javadoc)
* @see android.nfc.tech.TagTechnology#getTag()
*
*/
@Override
public Tag getTag() {
// TODO Auto-generated method stub
//return mytag;
return mynfcv.getTag();
}
/* (non-Javadoc)
* @see android.nfc.tech.TagTechnology#close()
*/
@Override
public void close() throws IOException {
try {
mynfcv.close();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "close failed: "+e.getMessage());
e.printStackTrace();
}
}
/* (non-Javadoc)
* @see android.nfc.tech.TagTechnology#connect()
*/
@Override
public void connect() throws IOException {
try {
mynfcv.connect();
} catch (IOException e) {
lastErrorFlags=-1; // TODO discriminate error states
Log.d(TAG,"connect failed: "+e.getMessage());
e.printStackTrace();
}
}
/* (non-Javadoc)
* @see android.nfc.tech.TagTechnology#isConnected()
*/
@Override
public boolean isConnected() {
// TODO Auto-generated method stub
// mynfcv.getDsfId();
return mynfcv.isConnected(); // better?
}
public byte[] readSingleBlock(int i){
byte[] read=transceive((byte)0x20,i);
setblocktaint(i,false); // remember we read this block
if(0!=lastErrorFlags)return read; // TODO not so ignorant..
byte[] res=new byte[read.length-1]; // drop the (0) flag byte TODO: in transceive?
for (int l = 0; l < read.length-1; l++) {
res[l]=read[l+1];
myuserdata[i*blocksize+l]=res[l]; // sort block into our buffer
}
return res;
}
/**
*
* @param i starting block number
* @param j block count
* @return block content concatenated
*/
public byte[] readMultipleBlocks(int i,int j){
if(0==blocksize){
Log.e(TAG,"readMult w/o initfields?");
getSystemInformation(); // system info was not read yet
}
byte[] read = transceive((byte)0x23,i,j);
if(0!=read[0])return read; // error flag set: TODO left as exercise..
byte[] res=new byte[read.length-1]; // drop the (0) flag byte
for (int l = 0; l < read.length-1; l++) {
res[l]=read[l+1];
myuserdata[i*blocksize+l]=res[l]; // sort block into our buffer
}
if(res.length<j*blocksize) return read; // da fehlt was
for (int k = i; k < j; k++) { // all blocks we read
setblocktaint(k, false); // untaint blocks we read
// @TODO reverting block order should be done on demand - or under user control (done again in DDMData)
// reverse(res,k*blocksize,blocksize); // swap string positions
}
return res;
}
public byte[] getMultiSecStatus(int i,int n){
byte[] read = transceive((byte)0x2c,i,n-1);
Log.d(TAG,"secstatus "+toHex(read));
if(0!=read[0])return read;
int startat=1; // TODO transceive will skip the error field soon
for(int j=0;j<nBlocks;j++)
blocklocked[j]=read[startat+i+j];
return read;
}
/**
* move anywhere to utils
* @param s
* @return
*/
public static String toHex(byte[] in){
String text=String.format("0x");
for (byte element : in) {
text=text.concat(String.format("%02x", element));
}
return text;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
我的 hello.js 文件
var hello = {
world: function(str, callback) {
cordova.exec(callback, function(err) {
callback('Nothing to hello.');
}, "Hello", "hello", [str]);
}
}
var ReadNfcV = {
read: function (str, callback) {
cordova.exec(callback, function (err) {
callback('Nothing to hello.');
}, "Hello", "hello", [str]);
}
}
module.exports = hello;
module.exports = ReadNfcV;
还有我的 plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
id="org.apache.cordova.plugin"
version="0.1.0">
<js-module src="hello.js" name="hello">
<clobbers target="hello" />
</js-module>
<!-- Android -->
<platform name="android">
<source-file src="Hello.java" target-dir="src/org/apache/cordova/plugin" />
<source-file src="ReadNfcV.java" target-dir="src/org/apache/cordova/plugin" />
<config-file target="res/xml/config.xml" parent="/*">
<feature name="Hello" >
<param name="android-package" value="org.apache.cordova.plugin.Hello"/>
</feature>
</config-file>
</platform>
</plugin>
我能够部署应用程序,因此我可以进行一些测试,我的问题是我真的不明白如何通过 javascript 从我的应用程序中调用 ReadNfcV.java 文件中的 ReadNfc 类。我只是做了与教程中相同的操作,但现在 hello.World 函数不再是一个函数,所以我想我在 hello.js 文件中做错了。如果有人可以帮助并解释我如何通过 javascript 调用我的 java 类,然后将 java 类的结果返回到我的 javascript,我将不胜感激。我花了 2 天时间寻找一个已经存在的插件,但除了 phonegap-nfc 插件外,没有找到关于该主题的任何内容。
克里斯托弗
更新第一天
我将 tech.NfcV 添加到导入列表中
import android.nfc.tech.NfcV;
按照建议更改了执行功能
@Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
Log.d(TAG, "execute " + action);
if (!getNfcStatus().equals(STATUS_NFC_OK)) {
callbackContext.error(getNfcStatus());
return true; // short circuit
}
createPendingIntent();
if (action.equals(REGISTER_DEFAULT_TAG)) {
addTechList(new String[]{NfcV.class.getName()}); //changed this form Mifare to NfcV
registerDefaultTag(callbackContext);
} else if (action.equalsIgnoreCase(INIT)) {
init(callbackContext);
} else {
// invalid action
return false;
}
return true;
}
问题似乎是我现在返回了无效的操作,所以这里出了点问题
我将 registerDefault 函数更改为
private void registerDefaultTag(CallbackContext callbackContext) {
addTechFilter();
callbackContext.success();
}
我将解析消息功能更改为
void parseMessage() {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, "parseMessage " + getIntent());
Intent intent = getIntent();
String action = intent.getAction();
Log.d(TAG, "action " + action);
if (action == null) {
return;
}
if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) || action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)){
Tag tagFromIntent = (Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
NfcV mfc = NfcV.get(tagFromIntent);
fireTagEvent(tag);
}
/*if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) {
Ndef ndef = Ndef.get(tag);
fireNdefEvent(NDEF_MIME, ndef, messages);
} else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) {
for (String tagTech : tag.getTechList()) {
Log.d(TAG, tagTech);
if (tagTech.equals(NdefFormatable.class.getName())) {
fireNdefFormatableEvent(tag);
} else if (tagTech.equals(Ndef.class.getName())) { //
Ndef ndef = Ndef.get(tag);
fireNdefEvent(NDEF, ndef, messages);
}
}
}
if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) {
fireTagEvent(tag);
}*/
setIntent(new Intent());
}
});
}
所以目前我在点击开始监听并在 Javascript atm 中调用 Taglisteners 时立即收到错误无效操作,我使用所有 4 个不同的监听器来查看是否有任何工作。看来我需要为 NfcV 编写一个新的 fireEvent 函数,因为现有的函数不起作用
更新 2
我已成功编译插件并部署应用程序,但没有任何反应,我没有返回 Tag 对象
解析消息函数
void parseMessage() {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
Log.d(TAG, "parseMessage " + getIntent());
Intent intent = getIntent();
String action = intent.getAction();
Log.d(TAG, "action " + action);
if (action == null) {
return;
}
if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) || action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)){
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if(tag != null){
byte[] id = tag.getId();
// set up read command buffer
byte blockNo = 0; // block address
byte[] readCmd = new byte[3 + id.length];
readCmd[0] = 0x20; // set "address" flag (only send command to this tag)
readCmd[1] = 0x20; // ISO 15693 Single Block Read command byte
System.arraycopy(id, 0, readCmd, 2, id.length); // copy ID
readCmd[2 + id.length] = blockNo; // 1 byte payload: block address
NfcV tech = NfcV.get(tag);
if (tech != null) {
// send read command
try {
tech.connect();
byte[] data = tech.transceive(readCmd);
fireTagEvent(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
tech.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
setIntent(new Intent());
}
}
});
}
我的fireTagEvent
private void fireTagEvent(byte[] data) {
String s2 = new String(data);
String command = MessageFormat.format(javaScriptEventTemplate, TAG_DEFAULT, s2);
Log.v(TAG, s2);
this.webView.sendJavascript(s2);
}
执行函数
@Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
Log.d(TAG, "execute " + action);
if (!getNfcStatus().equals(STATUS_NFC_OK)) {
callbackContext.error(getNfcStatus());
return true; // short circuit
}
createPendingIntent();
if (action.equals(REGISTER_DEFAULT_TAG)) {
addTechList(new String[]{NfcV.class.getName()});
registerDefaultTag(callbackContext);
} else if (action.equalsIgnoreCase(INIT)) {
init(callbackContext);
} else {
// invalid action
return false;
}
return true;
}
应用程序启动时“addTagDiscoveredListener”已注册,但我没有得到任何对象,所以要么未读取 nfcv 标签,要么我只是没有得到任何东西,不确定......
【问题讨论】:
-
嗨,我会在这个周末花一些时间来改进我的答案。您是否尝试过在 android studio 中导入应用并设置断点以查看标签是否可见? (并且在原始插件中是否可以看到...我想我的是,但我只能获得 ID)
-
原始插件正在读取标签,我正在获取标签的 id 和类型,问题是我需要存储在标签上的字节信息。不,我没有与 Visual Studio 合作,而用于混合开发的 Telerik appbuilder 并没有真正尝试设置断点,但没有发生任何事情,我必须承认,我不熟悉那种调试类型-.-。非常感谢非常感谢
-
嘿,quickfix,你有时间详细阐述你的方法,我尝试了一段时间没有成功
-
@QuickFix 很喜欢如果你能抽出时间来解释你的定制解决方案更多
标签: javascript java cordova cordova-plugins