uni-app NFC读取卡Id 扇门块的16进制数组 数据解析

uni-app NFC读取功能大纲

这里的功能只是安卓APP的,后续可能更新多微信小程序之类的,目前 IOS系统的可能不太考虑做

网上NFC的资料是真的少之又少,基本上10个9个一模一样的都是使用

android.nfc.extra.NDEF_MESSAGES

直接读取其中的数据,并没有实际的读取 扇区和扇区块中的byte数组信息,只能自己摸索
虽然都是东拼西凑出来的,但是起码解决了想读取扇区信息的需求------ 目前没解决如何读取密码块的密码
下面是我需要想要的效果
在这里插入图片描述

读取扇区的js文件

nfcServices.js

class NFCServices {

	readyWriteData = false; //开启写
	readyRead = false; //开启读
	noNFC = false;
	callBack = null;
	readyData = ''; //准备写入的数据
	static techListsArray = [
		['android.nfc.tech.IsoDep'],
		['android.nfc.tech.NfcA'],
		['android.nfc.tech.NfcB'],
		['android.nfc.tech.NfcF'],
		['android.nfc.tech.Nfcf'],
		['android.nfc.tech.NfcV'],
		['android.nfc.tech.NdefFormatable'],
		['android.nfc.tech.MifareClassic'],
		['android.nfc.tech.MifareUltralight']
	];
	// 要写入的数据

	static package_TECH_DISCOVERED = 'android.nfc.action.TECH_DISCOVERED';
	static READ_SUCCESS = '1' //读取成功
	static READ_FAIL = '2' //读取失败
	static READING = '3' //正在读取
	static WRITING = '4' //正在写入
	static WRITE_SUCCESS = '5' //写入成功
	static WRITE_FAIL = '6' //写入失败
	static DEVICE_NO_NFC = '7' //设备不支持NFC
	static DEVICE_NFC_NOT_ENABLE = '8' //设备未开启NFC
	static DEVICE_NFC_NOT_ENABLE = '9' //设备未开启NFC
	static INIT = '10' //初始化NFC
	static INIT_ERROR = '11' //初始化NFC失败

	constructor(callBack, continuousRead) {
		//是否连续读取NFC数据
		this.isContinuousRead = continuousRead !== undefined ? continuousRead : true

		this.callBack = callBack
		this.RuntimeMainActivity = plus.android.runtimeMainActivity();
		this.Intent = plus.android.importClass('android.content.Intent');
		this.PendingIntent = plus.android.importClass('android.app.PendingIntent');
		this.IntentFilter = plus.android.importClass('android.content.IntentFilter');
		this.NfcAdapter = plus.android.importClass('android.nfc.NfcAdapter');
		this.NdefRecord = plus.android.importClass("android.nfc.NdefRecord");
		this.NdefMessage = plus.android.importClass("android.nfc.NdefMessage");
		this.Ndef = plus.android.importClass('android.nfc.tech.Ndef');
		this.NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');
		this.MifareClassic = plus.android.importClass('android.nfc.tech.MifareClassic');
		this.nfcA = plus.android.importClass('android.nfc.tech.NfcA')
		// this.Parcelable = plus.android.importClass("android.os.Parcelable");
	}
	listenNFCStatus() {
		if (uni.getSystemInfoSync().platform !== "android") {
			return
		}
		this.callBack(NFCServices.INIT, '监听NFC状态!')
		try {

			let nfcAdapter = this.NfcAdapter.getDefaultAdapter(this.RuntimeMainActivity);

			if (nfcAdapter == null) {
				this.callBack(NFCServices.DEVICE_NO_NFC, '设备不支持NFC!')
				this.noNFC = true;
				return;
			}

			if (!nfcAdapter.isEnabled()) {
				this.callBack(NFCServices.DEVICE_NFC_NOT_ENABLE, '请在系统设置中先启用NFC功能!')
				this.noNFC = true;
				return;
			} else {
				this.noNFC = false;
			}

			let intent = new this.Intent(this.RuntimeMainActivity, this.RuntimeMainActivity.getClass());
			intent.addFlags(this.Intent.FLAG_ACTIVITY_SINGLE_TOP);
			let pendingIntent = this.PendingIntent.getActivity(this.RuntimeMainActivity, 0, intent, 0);
			let ndef = new this.IntentFilter("android.nfc.action.TECH_DISCOVERED");
			ndef.addDataType("*/*");
			let intentFiltersArray = [ndef];
			const that = this
			plus.globalEvent.addEventListener('newintent', () => {
				console.log('newintent running');
				// 轮询调用 NFC
				that.nfcRuning()
				// setTimeout(, 1000);
			});

			plus.globalEvent.addEventListener('pause', (e) => {
				console.log('pause running');
				if (nfcAdapter) {
					//关闭前台调度系统 恢复默认状态
					nfcAdapter.disableForegroundDispatch(this.RuntimeMainActivity);
				}
			});
			plus.globalEvent.addEventListener('resume', (e) => {
				if (nfcAdapter) {
					//开启前台调度系统 优于所有其他NFC
					nfcAdapter.enableForegroundDispatch(this.RuntimeMainActivity, pendingIntent,
						intentFiltersArray,
						NFCServices.techListsArray);
				}
			});

			nfcAdapter.enableForegroundDispatch(this.RuntimeMainActivity, pendingIntent, intentFiltersArray,
				NFCServices.techListsArray);
		} catch (e) {
			this.callBack(NFCServices.INIT_ERROR, '初始化NFC失败!', e)

		}
	}
	nfcRuning() { //

		let intent = this.RuntimeMainActivity.getIntent();
		if (NFCServices.package_TECH_DISCOVERED == intent.getAction()) {
			if (this.readyWriteData) {
				this.write(intent);
				this.readyWriteData = false;
			} else if (this.isContinuousRead || this.readyRead) {
				this.read(intent);
				this.readyRead = false;
			}

		}
	}
	write(intent) { //写代码
		try {
			this.callBack(NFCServices.WRITING, '请勿移开标签 正在写入...')
			let textBytes = plus.android.invoke(this.readyData, "getBytes");
			// image/jpeg text/plain  
			let textRecord = new this.NdefRecord(this.NdefRecord.TNF_MIME_MEDIA,
				plus.android.invoke("text/plain", "getBytes"),
				plus.android.invoke("", "getBytes"), textBytes);
			let message = new this.NdefMessage([textRecord]);

			let tag = intent.getParcelableExtra(this.NfcAdapter.EXTRA_TAG);
			let ndef = this.Ndef.get(tag);
			if (ndef != null) {
				// 待写入的数据长度
				let size = message.toByteArray().length;
				ndef.connect();
				if (!ndef.isWritable()) {
					this.callBack(NFCServices.WRITE_FAIL, 'tag不允许写入!')

					return;
				}
				if (ndef.getMaxSize() < size) {
					this.callBack(NFCServices.WRITE_FAIL, '文件大小超出容量!')
					return;
				}
				ndef.writeNdefMessage(message); //写入数据
				this.callBack(NFCServices.WRITE_SUCCESS, '写入数据成功!', this.readyData)
				return;
			} else {
				let format = this.NdefFormatable.get(tag);
				if (format != null) {
					try {
						format.connect();
						format.format(message);
						this.callBack(NFCServices.WRITE_SUCCESS, '格式化tag并且写入数据成功!', this.readyData)

						return;
					} catch (e) {
						this.callBack(NFCServices.WRITE_FAIL, '格式化tag失败!')
						return;
					}
				} else {
					this.callBack(NFCServices.WRITE_FAIL, 'Tag不支持NDEF!')
					return;
				}
			}
		} catch (e) {
			this.callBack(NFCServices.WRITE_FAIL, '写入失败!', e)
		}

	}
	read(intent) { // 读代码
		this.callBack(NFCServices.READING, '请勿移开标签正在读取数据!')
		// NFC id
		let bytesId = intent.getByteArrayExtra(this.NfcAdapter.EXTRA_ID);
		let nfc_id = this.byteArrayToHexString(bytesId);
		console.log('nfc_id:', nfc_id);
		
		// 读取扇区
		let tag = intent.getParcelableExtra(this.NfcAdapter.EXTRA_TAG);
		
		// let fa = this.nfcA.get(tag)
		// 	console.log('实例获取---',fa)
		// fa.connect()
		let miclass = this.MifareClassic.get(tag)
		miclass.connect()
		let count = miclass.getSectorCount()
		console.log('====:',count,this.HexStringToByteArray('FFFFFFFFFFFF'))
		let shanQ = []
		let num = 0
		for (let i=0; i<count; i++) {
			if (miclass.authenticateSectorWithKeyA(i,this.HexStringToByteArray('FFFFFFFFFFFF'))) {
				console.log(11111)
			}
			if (miclass.authenticateSectorWithKeyB(i,this.HexStringToByteArray('FFFFFFFFFFFF'))) {
				console.log(222222)
			}
			let arr=[]
			for (let o = 0; o < 4; o++) {
				num = num + 1
			    let content = miclass.readBlock(num-1)
			    console.log(`当前${i}扇区---第${num-1}`+content)
			    arr.push(this.Bytes2Str(content))
			}
			console.log('数据体---',arr)
			shanQ.push(arr)
		}
		console.log('转换好的数据',shanQ)
		// let blockCount = miclass.getBlockCountInSector()
		// for (let i=0; i<blockCount; i++) {
		//     // 获取要读取的块号
		//     let blockIndex = miclass.sectorToBlock(i)
		//     // 读取块数据
		// 	let data = miclass.readBlock(blockIndex);
		// 	console.log('循环扇区获取的:----'+blockIndex+'扇区:',data)
		// }
		
		// 卡内信息
		let rawmsgs = intent.getParcelableArrayExtra("android.nfc.extra.NDEF_MESSAGES");
		//let rawmsgs = intent.getParcelableArrayExtra();

		console.log("数据" + rawmsgs)
		if (rawmsgs != null && rawmsgs.length > 0) {
			let records = rawmsgs[0].getRecords();
			console.log("数据22222" + records)
			let result = records[0].getPayload();
			console.log("数据33333" + result)
			let data = plus.android.newObject("java.lang.String", result);

			this.callBack(NFCServices.READ_SUCCESS, '读取数据成功!', data)
		} else {
			this.callBack(NFCServices.READ_FAIL, '没有读取到数据!')
		}
	}
	//Key处理函数
	
	HexStringToByteArray (instr) {
	    var hexA = new Array();
	    var pos = 0;
	    var len = instr.length/2;
	    for(var i=0; i<len; i++)
	    {
	        var s = instr.substr(pos, 2);
	        var v = parseInt(s, 16);
	        if(v>=128)
	            v=v-256;
	        hexA.push(v);
	        pos += 2;
	    }
	    return hexA;
	}
	
	// 格式化 byte[]数组 如[117,-117,65....] 这类的byte数组转换成  16进制[75,84,A3....]这类型的数据
	Bytes2Str(arrBytes){
		var str = "";
		for (var i = 0; i < arrBytes.length; i++) {
			var tmp;
			var num = arrBytes[i];
			if (num < 0) {
				//此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
				tmp = (255 + num + 1).toString(16);
			} else {
				tmp = num.toString(16);
			}
			if (tmp.length == 1) {
				tmp = "0" + tmp;
			}
			if(i>0){
				str += ""+tmp; // 可以修改 : 成你需要的间隔符号,如果不需要间隔符号那就空着
			}else{
				str += tmp;
			}
		}
		return str.toLocaleUpperCase();
	}
	str2UTF8(str){
		var bytes = new Array(); 
		var len,c;
		len = str.length;
		for(var i = 0; i < len; i++){
			c = str.charCodeAt(i);
			if(c >= 0x010000 && c <= 0x10FFFF){
				bytes.push(((c >> 18) & 0x07) | 0xF0);
				bytes.push(((c >> 12) & 0x3F) | 0x80);
				bytes.push(((c >> 6) & 0x3F) | 0x80);
				bytes.push((c & 0x3F) | 0x80);
			}else if(c >= 0x000800 && c <= 0x00FFFF){
				bytes.push(((c >> 12) & 0x0F) | 0xE0);
				bytes.push(((c >> 6) & 0x3F) | 0x80);
				bytes.push((c & 0x3F) | 0x80);
			}else if(c >= 0x000080 && c <= 0x0007FF){
				bytes.push(((c >> 6) & 0x1F) | 0xC0);
				bytes.push((c & 0x3F) | 0x80);
			}else{
				bytes.push(c & 0xFF);
			}
		}
		return bytes;
	}
	// 将字节数组转换为字符串  
	byteArrayToHexString(inarray) {
		let i, j, inn;
		let hex = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
		let out = "";

		for (j = 0; j < inarray.length; ++j) {
			inn = inarray[j] & 0xff;
			i = (inn >>> 4) & 0x0f;
			out += hex[i];
			i = inn & 0x0f;
			out += hex[i];
		}
		return out;
	}
	// 更改写状态
	writeData(data) {
		if (this.noNFC) {
			this.callBack(NFCServices.WRITE_FAIL, '请检查设备是否支持并开启 NFC 功能!')

			return;
		}
		this.readyData = data
		// 轮询条件
		this.readyWriteData = true;
		this.callBack(NFCServices.WRITING, '请将NFC标签靠近!')
	}

	readData() { // 更改读状态
		if (this.noNFC) {
			this.callBack(NFCServices.READ_FAIL, '请检查设备是否支持并开启 NFC 功能!')

			return;
		}
		// 轮询条件
		this.readyRead = true;
		this.callBack(NFCServices.READING, '请将NFC标签靠近!')

	}
	// 设置数据
	destroy() {
		plus.globalEvent.removeEventListener('newintent', false);
		plus.globalEvent.removeEventListener('pause', false);
		plus.globalEvent.removeEventListener('resume', false);
	}
}
export default NFCServices


nfc.vue

<template>
	<view class="nfc">
		<textarea class="txtarea" disabled :value="readData" placeholder="这是读取到的nfc的数据" />
		<button class="btn" @tap="read()">读取NFC</button>
		<textarea class="txtarea" v-model="writeTxt" maxlength="124" placeholder="这是输入写入的nfc的数据" />
		<button class="btn" @tap="write()">写入NFC</button>
	</view>
</template>

<script>
	// nfc 输入最大数字长度为124位,文字为41位
	import NFCServices from '@/nfc/nfcServices.js'

	let nfcServices;
	export default {
		onLoad() {
			nfcServices = new NFCServices(this.NFCServicesCallBack)
			nfcServices.listenNFCStatus()
		},
		onShow() {

		},
		onUnload() {
			nfcServices.destroy()
		},
		data() {
			return {
				writeTxt: '',
				readData: ''
			}
		},
		methods: {
			NFCServicesCallBack(status, tip, data) {
				switch (status) {
					case NFCServices.READ_SUCCESS:
						this.readData = data
						break;
					default:
						break;
				}
				console.log(tip, data);
				uni.showToast({
				    title: tip,
				    icon: 'none'
				});
			},
			read() {
				// 调用 js 文件里面的方法
				nfcServices.readData()
			},
			write() {
				// 调用 js 文件里面的方法
				nfcServices.writeData(this.writeTxt)

			},
		}
	}
</script>

<style lang="scss">
	.nfc {
		padding: 20rpx;

		.txt {
			height: 240rpx;
			line-height: 50rpx;
			font-size: 32rpx;
		}

		.txtarea {
			width: 100%;
			min-height: 240rpx;
			border: 1px solid #f0f0f0;
		}

		.btn {
			border: none;
			background-color: #1b85e9;
			color: #fff;
			margin: 20rpx 0;
		}
	}
</style>


最后得出的
在这里插入图片描述

打包时候记得把这个给勾上噢
在这里插入图片描述

// ============= 2024-2-21 更新 区块编辑(就差卡模拟了) ====================//

最近又需要做一个手机发卡的功能(仅限于安卓),如果是按照上面读写方法是完全没办法做到针对某一个块进行修改的,所以我苦心专研找到了这方面的方法,也实现了对卡的扇区块做数据编辑的方法了。(如果是学过安卓原生实际上很简单)。

相关的代码就不无偿分享了 ๑乛◡乛๑
在这里插入图片描述

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值