UNPKG

vite-uni-dev-tool

Version:

vite-uni-dev-tool, debug, uni-app, 一处编写,到处调试

597 lines (523 loc) 17.1 kB
import { onLoad, onUnload } from '@dcloudio/uni-app'; import { ref } from 'vue'; import { getAndroidTechs, getReadWriteStandard, getSakHex, getSakType, parseAtqaAndroid, transformArrayBuffer, } from './utils'; const NfcAdapter: any = plus.android.importClass('android.nfc.NfcAdapter'); const Context = plus.android.importClass('android.content.Context'); const Tag = plus.android.importClass('android.nfc.Tag'); const Ndef: any = plus.android.importClass('android.nfc.tech.Ndef'); const NfcA: any = plus.android.importClass('android.nfc.tech.NfcA'); const NfcB: any = plus.android.importClass('android.nfc.tech.NfcB'); const NfcF: any = plus.android.importClass('android.nfc.tech.NfcF'); const NfcV: any = plus.android.importClass('android.nfc.tech.NfcV'); const NdefFormat = plus.android.importClass('android.nfc.tech.NdefFormat'); const NdefFormatable: any = plus.android.importClass( 'android.nfc.tech.NdefFormatable', ); const MifareClassic: any = plus.android.importClass( 'android.nfc.tech.MifareClassic', ); const MifareUltralight: any = plus.android.importClass( 'android.nfc.tech.MifareUltralight', ); const IsoDep: any = plus.android.importClass('android.nfc.tech.IsoDep'); const Intent: any = plus.android.importClass('android.content.Intent'); const PendingIntent: any = plus.android.importClass( 'android.app.PendingIntent', ); const IntentFilter: any = plus.android.importClass( 'android.content.IntentFilter', ); const Settings: any = plus.android.importClass('android.provider.Settings'); const NdefRecord: any = plus.android.importClass('android.nfc.NdefRecord'); const NdefMessage: any = plus.android.importClass('android.nfc.NdefMessage'); const INTENT_FLAGS_SINGLE_TOP = Intent.FLAG_ACTIVITY_SINGLE_TOP; const PENDING_INTENT_FLAGS = PendingIntent.FLAG_MUTABLE; const EXTRA_NDEF_MESSAGES = NfcAdapter.EXTRA_NDEF_MESSAGES; const EXTRA_ID = NfcAdapter.EXTRA_ID; const EXTRA_TAG = NfcAdapter.EXTRA_TAG; const TNF_MIME_MEDIA = NdefRecord.TNF_MIME_MEDIA; const ACTION_NFC_SETTINGS = Settings.ACTION_NFC_SETTINGS; const NFC_TECH_LISTS = [ ['android.nfc.tech.Ndef'], ['android.nfc.tech.NdefFormatable'], ['android.nfc.tech.IsoDep'], ['android.nfc.tech.NfcA'], ['android.nfc.tech.NfcB'], ['android.nfc.tech.NfcF'], ['android.nfc.tech.NfcV'], ['android.nfc.tech.MifareClassic'], ['android.nfc.tech.MifareUltralight'], ]; const NFC_INTENT_FILTERS = [ NfcAdapter.ACTION_NDEF_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TAG_DISCOVERED, ]; export default function useNFCAndroid( listener: (data: NFC.Result) => void, options: { timeout?: number; /** 检测连接状态 */ checkConnectedDelay?: number; } = { timeout: 0, checkConnectedDelay: 500, }, ) { /** 是否支持 nfc */ const isSupportNFC = ref<boolean | undefined>(false); /** 是否监听中 */ const isListening = ref<boolean>(false); /** 是否已连接 */ const isConnected = ref<boolean>(false); /** nfc 数据 */ const data = ref<NFC.Result>({ id: new ArrayBuffer(0), techs: [], byteArray: [], hexArray: [], hexNumberArray: [], hexString: '', }); /** ndef 写入方式比较特殊,需要特殊处理 */ let isNdef = ref(false); const mainActivity: any = plus.android.runtimeMainActivity(); let checkInterval: any = null; let nfcAdapter: NFC.Adapter | null = null; let nfcPendingIntent: any = null; let nfcTag: any = null; let nfcInstance: any = null; function checkConnected() { checkInterval = setInterval(() => { if (!nfcAdapter) { isConnected.value = false; clearInterval(checkInterval); return; } if (!nfcAdapter.isEnabled()) { isConnected.value = false; clearInterval(checkInterval); return; } if (!nfcInstance) { isConnected.value = false; clearInterval(checkInterval); return; } if (nfcInstance.isConnected()) { isConnected.value = true; } else { isConnected.value = false; clearInterval(checkInterval); } }, options.checkConnectedDelay ?? 500); } function innerListener() { try { const intent = mainActivity.getIntent(); if (!intent) { uni.showToast({ title: 'NFC 数据解析失败', icon: 'none' }); return; } // 获取id const id = intent.getByteArrayExtra(EXTRA_ID); const transform = transformArrayBuffer(id); let techs: NFC.NfcType[] = []; let maxTransceiveLength = 0; let sak = 0; let sakHex = ''; let sakType = undefined; let atqaInfo = { uidType: 0, storageSize: 0, manufacturer: 0, cardFeatures: 0, reserved: 0, }; let readWriteStandard: string[] = []; nfcTag = intent.getParcelableExtra(EXTRA_TAG); // 先获取 Tag 实例 if (nfcTag) { // 核心方法:通过 Tag.getTechList() 获取原生技术列表数组 const nativeTechList = nfcTag.getTechList(); // 转换为可直接使用的 string 数组(兼容原生返回格式) techs = Array.isArray(nativeTechList) ? nativeTechList : []; techs = getAndroidTechs(techs); isNdef.value = techs.includes('Ndef'); // 根据技术类型 获取实例 const type = techs[0]; switch (type) { case 'NFC-A': nfcInstance = NfcA.get(nfcTag); break; case 'NFC-B': nfcInstance = NfcB.get(nfcTag); break; case 'NFC-F': nfcInstance = NfcF.get(nfcTag); break; case 'NFC-V': nfcInstance = NfcV.get(nfcTag); break; case 'MIFARE Classic': nfcInstance = MifareClassic.get(nfcTag); break; case 'MIFARE Ultralight': nfcInstance = MifareUltralight.get(nfcTag); break; case 'Ndef': isNdef.value = true; nfcInstance = Ndef.get(nfcTag); break; case 'Ndef Formatable': isNdef.value = true; nfcInstance = NdefFormatable.get(nfcTag); break; case 'ISO-DEP': nfcInstance = IsoDep.get(nfcTag); break; default: nfcInstance = undefined; break; } if (!nfcInstance) { uni.showToast({ title: 'NFC 实例获取失败', icon: 'none' }); return; } nfcInstance?.connect(); maxTransceiveLength = nfcInstance?.getMaxTransceiveLength?.() ?? 0; readWriteStandard = getReadWriteStandard(techs); if (techs.includes('NFC-A')) { // NFC-A 标签的 SAK 信息 const nfcAInstance = NfcA.get(nfcTag); const sakShortObject = plus.android.invoke(nfcAInstance, 'getSak') ?? 0; const sakStr = plus.android.invoke(sakShortObject, 'toString'); sak = Number(sakStr) || 0; sakType = getSakType(sak); sakHex = getSakHex(sak); const atqa = plus.android.invoke(nfcAInstance, 'getAtqa'); atqaInfo = parseAtqaAndroid(atqa); } } // TODO 实际使用中 获取的ndefMessage格式??? // 解析 NDEF 数据 const ndefMessage = intent.getParcelableArrayExtra(EXTRA_NDEF_MESSAGES); let message: any = ''; if (ndefMessage && ndefMessage.length > 0) { const records = ndefMessage[0].getRecords(); if (records && records.length > 0) { const payload = records[0].getPayload(); message = payload ? plus.android.newObject('java.lang.String', payload) : ''; isNdef.value = true; } } // 返回数据 data.value = { ...data.value, ...transform, ...atqaInfo, id, sak, sakHex, sakType, techs, message, maxTransceiveLength, readWriteStandard, }; // 回调通知外部 listener(data.value); // 可将改行去除 uni?.__dev_tool_nfc_collection__?.(data.value); uni.showToast({ title: 'NFC 读取成功', icon: 'none' }); checkConnected(); } catch (error) { uni.showToast({ title: 'NFC 数据解析失败', icon: 'none' }); } } function initNfcAdapter() { try { if (!mainActivity) { uni.showToast({ title: '无法获取主 Activity 实例', icon: 'none' }); return; } nfcAdapter = NfcAdapter.getDefaultAdapter(mainActivity); // 1. 设备不支持 NFC if (!nfcAdapter) { isSupportNFC.value = false; return; } // 2. NFC 功能未开启 if (!nfcAdapter.isEnabled()) { isSupportNFC.value = false; return; } // 3. 初始化成功,注册监听 isSupportNFC.value = true; } catch (error) { isSupportNFC.value = false; } } function startHCE() { if (!isSupportNFC.value) { uni.showToast({ title: '设备不支持 NFC', icon: 'none', }); return; } // 前置校验 if (!nfcAdapter) { uni.showToast({ title: '请先初始化 NFC 适配器', icon: 'none' }); return false; } // if (isListening.value) { uni.showToast({ title: 'NFC 监听已开启,无需重复操作', icon: 'none' }); return true; } try { // 1. 注册 newintent 事件监听 plus.globalEvent.addEventListener('newintent', innerListener); // 2. 构建前台调度 PendingIntent(缓存实例,避免重复创建) if (!nfcPendingIntent) { const intent = new Intent(mainActivity, mainActivity.getClass()); intent.addFlags(INTENT_FLAGS_SINGLE_TOP); nfcPendingIntent = PendingIntent.getActivity( mainActivity, 0, intent, PENDING_INTENT_FLAGS, ); } // 3. 构建 Intent 过滤器 const filters = NFC_INTENT_FILTERS.map( (filter) => new IntentFilter(filter), ); // 4. 启用前台调度(核心:App 前台优先捕获 NFC 事件) nfcAdapter?.enableForegroundDispatch( mainActivity, nfcPendingIntent, filters, NFC_TECH_LISTS, ); // 5. 更新监听状态 isListening.value = true; uni.showToast({ title: '开始监听NFC', icon: 'none', }); return true; } catch (e) { uni.showToast({ title: 'NFC 监听开启失败', icon: 'none' }); isListening.value = false; return false; } } function stopHCE() { // 前置校验 if (!isSupportNFC.value) { uni.showToast({ title: '设备不支持 NFC', icon: 'none', }); return; } // 前置校验 if (!nfcAdapter) { uni.showToast({ title: '请先初始化 NFC 适配器', icon: 'none' }); return false; } // if (!isListening.value) { uni.showToast({ title: 'NFC 监听已关闭,无需重复操作', icon: 'none' }); return true; } try { // 1. 禁用前台调度 nfcAdapter.disableForegroundDispatch(mainActivity); // 2. 更新监听状态 isListening.value = false; isConnected.value = false; uni.showToast({ title: '停止监听NFC', icon: 'none', }); clearInterval(checkInterval); return true; } catch (e) { uni.showToast({ title: 'NFC 监听停止失败', icon: 'error' }); return false; } } function connect() { if (!isSupportNFC.value) { uni.showToast({ title: '设备不支持 NFC', icon: 'none' }); return; } if (!isListening.value) { uni.showToast({ title: '请先开启前台调度监听', icon: 'none' }); return; } if (!nfcInstance) { uni.showToast({ title: '请先靠近 NFC 标签', icon: 'none' }); return; } try { if (!nfcInstance.isConnected()) { // 补充连接状态判断 nfcInstance.connect(); isConnected.value = true; uni.showToast({ title: 'NFC 连接成功', icon: 'none' }); } else { isConnected.value = true; uni.showToast({ title: 'NFC 已处于连接状态', icon: 'none' }); } checkConnected(); } catch (error) { uni.showToast({ title: 'NFC 连接失败', icon: 'none' }); isConnected.value = false; } } function close() { if (!isSupportNFC.value) { uni.showToast({ title: '设备不支持 NFC', icon: 'none' }); return; } if (!nfcInstance) { uni.showToast({ title: '无连接中的 NFC 标签', icon: 'none' }); return; } try { if (nfcInstance.isConnected()) { nfcInstance.close(); } // 清空缓存,重置状态 nfcTag = null; nfcInstance = null; isConnected.value = false; isNdef.value = false; clearInterval(checkInterval); uni.showToast({ title: 'NFC 连接已关闭', icon: 'none' }); } catch (error) { uni.showToast({ title: 'NFC 连接关闭失败', icon: 'none' }); } } function offHCEMessage() { try { if (!isSupportNFC.value) { uni.showToast({ title: '设备不支持 NFC,无需移除监听', icon: 'none' }); return true; } // 1. 移除 newintent 事件监听 plus.globalEvent.removeEventListener('newintent', innerListener); // 2. 停止前台调度,关闭连接 if (nfcAdapter && isListening.value) { nfcAdapter.disableForegroundDispatch(mainActivity); } if (nfcInstance && nfcInstance.isConnected()) { nfcInstance.close(); } // 3. 清空所有缓存,重置状态 nfcPendingIntent = null; nfcTag = null; nfcInstance = null; isListening.value = false; isConnected.value = false; isNdef.value = false; clearInterval(checkInterval); uni.showToast({ title: 'NFC 监听已移除', icon: 'none' }); return true; } catch (e) { uni.showToast({ title: 'NFC 监听移除失败', icon: 'none' }); return false; } } /** * TODO 寻找一张卡来进行测试写入 * * @param {ArrayBuffer} writeData * @return {*} */ function write(writeData: ArrayBuffer) { if (!nfcTag) { uni.showToast({ title: '请先靠近 NFC 标签建立连接', icon: 'none' }); return; } try { uni.showToast({ title: '开始写入数据,请勿断开连接', icon: 'none', }); // 1. 转换 ArrayBuffer 为 Java 字节数组 const uint8Array = new Uint8Array(writeData); const text = Array.from(uint8Array) .map((byte) => String.fromCharCode(byte)) .join(''); const textBytes = plus.android.invoke(text, 'getBytes'); // 2. 构建 NDEF 记录与消息 const mimeBytes = plus.android.invoke('text/plain', 'getBytes'); const textRecord = new NdefRecord( TNF_MIME_MEDIA, mimeBytes, plus.android.invoke('', 'getBytes'), textBytes, ); const message = new NdefMessage([textRecord]); // 3. 写入数据(支持已格式化和未格式化标签) nfcInstance.connect(); if (!nfcInstance.isWritable()) { uni.showToast({ title: 'NFC 标签不允许写入', icon: 'none' }); nfcInstance.close(); return; } if (nfcInstance.getMaxTransceiveLength() < message.toByteArray().length) { uni.showToast({ title: '写入数据超出标签容量', icon: 'none' }); nfcInstance.close(); return; } nfcInstance.writeNdefMessage(message); nfcInstance.close(); checkConnected(); uni.showToast({ title: '数据写入成功', icon: 'none' }); } catch (e) { uni.showToast({ title: '数据写入失败', icon: 'none' }); } } /** * TODO 寻找一张卡来进行测试写入 * * @param {NFC.NdefData} data */ function writeNdf(data: NFC.NdefData) {} onLoad(() => { initNfcAdapter(); }); onUnload(() => { stopHCE(); offHCEMessage(); }); return { data, isNdef, isSupportNFC, isListening, isConnected, nfcInstance, startHCE, stopHCE, connect, close, write, writeNdf, }; }