要点:
以下示例是:
1:入参和读取到的返回结果:都采用Android与js交互的方式处理
2:读取卡片等的 某一扇区(该扇区带有秘钥)的信息并解析,扇区和秘钥都可以在前端手动入参
3:如果想要获取所有扇区的信息,可以放开 processIntent 方法中的一些注释(我保留了获取所有扇区信息的代码)稍加修改测试即可。
//主干代码
package com.xxxx.xxx;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.os.Build;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.greencloud.gcsunmi.utils.ICallbackUtil;
import com.greencloud.gcsunmi.utils.ToHexStringUtils;
public class MainActivity extends Activity {
WebView mWebView;
NfcAdapter nfcAdapter;
PendingIntent mPendingIntent;
//读卡 扇区+秘钥
String Sector = "";
String Key = "";
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
mWebView.getSettings().setDefaultTextEncodingName("utf-8");// 设置编码
mWebView.getSettings().setJavaScriptEnabled(true);// 支持js
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.setWebViewClient(new WebViewClientDemo());//添加一个页面相应监听类
mWebView.getSettings().setDomStorageEnabled(true);//支持HTML5中的一些控件标签
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mWebView.getSettings().setMediaPlaybackRequiresUserGesture(false);//支持前端音频自动播放
}
// 载入包含js的html
mWebView.loadData("", "text/html", null);
mWebView.loadUrl("file:///android_asset/www/index.html");
//nfc功能初始化
nfccheck();
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
/**
* 注册
*/
class WebViewClientDemo extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 当打开新链接时,使用当前的 WebView,不会使用系统其他浏览器
view.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
/**
* 注册JavascriptInterface,其中"lee"的名字随便取,如果你用"lee",那么在html中只要用 lee.方法名()
* 即可调用MyJavascriptInterface里的同名方法,参数也要一致
*/
mWebView.addJavascriptInterface(new JsObject(), "lee");
}
}
public void initViews() {
mWebView = (WebView) findViewById(R.id.wv_view);
}
/**
* js调用的安卓方法
*/
class JsObject {
ICallbackUtil callback = new ICallbackUtil();
//读卡接口
//sector扇区 key秘钥
@JavascriptInterface
public void funSunmiReadCard(final String sector, final String key) {
Sector = sector;
Key = key;
}
}
/**
* 安卓调用js的方法 输出结果
*
* @param me
*/
//读卡
public void forJSrecard(final String me) {
mWebView.post(new Runnable() {
@Override
public void run() {
mWebView.loadUrl("javascript:recardresult(" + me + ")");
}
});
}
/**
* 相关服务配置:
*/
private void nfccheck() {
// 获取默认的NFC控制器
nfcAdapter = NfcAdapter.getDefaultAdapter(MainActivity.this);
if (nfcAdapter == null) {
forJSrecard("'设备不支持NFC!'");
finish();
return;
}
if (!nfcAdapter.isEnabled()) {
forJSrecard("'请在系统设置中先启用NFC功能!'");
finish();
return;
}
}
//启动前台调度系统
@Override
protected void onResume() {
super.onResume();
nfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
//关闭前台调度系统
@Override
protected void onPause() {
super.onPause();
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(this);
}
}
//onNewIntent回调方法
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
// 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
if (!Key.equals("") && Key != null) {
processIntent(intent, Integer.valueOf(Sector), Key);
}
}
}
/**
* 读卡
* int sectorIndex 指定扇区
* String authKey 对应的秘钥
* //注意,如果扇区传0,秘钥只传字符串"ID",说明只是想获取物理卡号,返回给前端物理卡号即可
*/
private void processIntent(Intent intent, int sectorIndex, String authKey) {
//取出封装在intent中的TAG
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
byte[] bytesId = tagFromIntent.getId();// 获取id数组
String idinfo = ToHexStringUtils.ByteArrayToHexString(bytesId);
for (String tech : tagFromIntent.getTechList()) {
}
boolean auth = false;
//如果密钥只传了"ID",返回给前端会员卡的物理ID即可
if (authKey != null && authKey.equals("ID")) {
forJSrecard("'" + idinfo + "'");
} else {
//如果密钥只传了"NID",秘钥读卡,后台秘钥写死即可
//读取TAG
MifareClassic mfc = MifareClassic.get(tagFromIntent);
try {
String metaInfo = "";
String msg = "";
mfc.connect();
int type = mfc.getType();//获取TAG的类型
int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
String typeS = "";
switch (type) {
case MifareClassic.TYPE_CLASSIC:
typeS = "TYPE_CLASSIC";
break;
case MifareClassic.TYPE_PLUS:
typeS = "TYPE_PLUS";
break;
case MifareClassic.TYPE_PRO:
typeS = "TYPE_PRO";
break;
case MifareClassic.TYPE_UNKNOWN:
typeS = "TYPE_UNKNOWN";
break;
}
// metaInfo += "卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共"
// + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n"+"Id标签是"+info+"\n+"+"\n";
// for (int j = 0; j < sectorCount; j++) {
//解析扇区秘钥信息
auth = mfc.authenticateSectorWithKeyA(sectorIndex, ToHexStringUtils.hexStringToByte("568G789F334"));
int bCount;
int bIndex;
if (auth) {
// metaInfo += "Sector " + j + ":验证成功\n";
// 读取扇区中的块
bCount = mfc.getBlockCountInSector(sectorIndex);
bIndex = mfc.sectorToBlock(sectorIndex);
for (int i = 0; i < 3; i++) {
byte[] data = mfc.readBlock(bIndex);
metaInfo += ToHexStringUtils.toStringHex2(ToHexStringUtils.bytesToHexString(data));
bIndex++;
}
} else {
// metaInfo += "sector:" + sectorIndex + ":验证失败\n";
metaInfo += "";
}
// }
forJSrecard("'" + metaInfo + "'");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
AndroidMainfest.xml文件配置
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/logo_cy"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<activity android:name=".MainActivity"
android:hardwareAccelerated="true">
<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.TAG_DISCOVERED" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
JS前端交互代码
<body>
<a id="sn">读卡</a>
<div id="snback"></div>
<script type="text/javascript">
document.querySelector('#sn').addEventListener('click', function () {
javascript: lee.funSunmiReadCard(sector,key);
return false;
}, false);
function recardresult(str) {
document.querySelector("#snback").innerHTML = str
}
getidresult("ddd")
</script>
</body>
Android如果要读取前端代码,需要在与java目录,同等级目录下新增 assets 文件夹(注意,文件夹命名必须是这个名称,安卓规定的)
Mainactivity页面的代码
// 载入包含js的html
mWebView.loadData("", "text/html", null);
mWebView.loadUrl("file:///android_asset/www/index.html");
就是为了读取js等前端页面(这里我又在assets文件夹下新增了一个www的文件夹)