package com.rehome.dywoa; import static com.rehome.dywoa.utils.NfcUtil.stringsToBytes; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.nfc.FormatException; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.IsoDep; import android.nfc.tech.MifareClassic; import android.nfc.tech.MifareUltralight; import android.nfc.tech.Ndef; import android.nfc.tech.NdefFormatable; import android.nfc.tech.NfcA; import android.nfc.tech.NfcB; import android.nfc.tech.NfcF; import android.nfc.tech.NfcV; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.rehome.dywoa.utils.NfcUtil; import org.json.JSONException; import org.json.JSONObject; import java.util.Arrays; import java.util.Objects; /** * Description: * 子类在onNewIntent方法中进行NFC标签相关操作。 * launchMode设置为singleTop或singelTask,保证Activity的重用唯一 * 在onNewIntent方法中执行intent传递过来的Tag数据 * 将NFC标签卡靠近手机后部(NFC标签卡可网上自行购买) */ public class BaseNfcActivity extends AppCompatActivity { private NfcAdapter nfcAdapter; private String readResult = ""; private PendingIntent pendingIntent; private IntentFilter[] mFilters; private String[][] mTechLists; private boolean isFirst = true; private IntentFilter ndef; Tag mTag; Tag getTagHandle() { return mTag; } /** * 检测工作,判断设备的NFC支持情况 * * @return */ private Boolean ifNFCUse() { if (nfcAdapter == null) { Toast.makeText(this, "设备不支持NFC!", Toast.LENGTH_SHORT).show(); return false; } if (!nfcAdapter.isEnabled()) { Toast.makeText(this, "请在系统设置中先启用NFC功能!", Toast.LENGTH_SHORT).show(); return false; } return true; } /** * 初始化过程 */ public void initNFC() { //NFC适配器,所有的关于NFC的操作从该适配器进行 nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (!ifNFCUse()) { return; } //将被调用的Intent,用于重复被Intent触发后将要执行的跳转 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); } else { pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_UPDATE_CURRENT); } //设定要过滤的标签动作,这里只接收ACTION_NDEF_DISCOVERED类型 ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); ndef.addCategory("*/*"); mFilters = new IntentFilter[]{ndef};// 过滤器 mTechLists = new String[][]{new String[]{NfcA.class.getName()}, new String[]{NfcF.class.getName()}, new String[]{NfcB.class.getName()}, new String[]{NfcV.class.getName()}, new String[]{MifareClassic.class.getName()}, new String[]{MifareUltralight.class.getName()}, new String[]{IsoDep.class.getName()}};// 允许扫描的标签类型 if (isFirst) { if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent() .getAction())) { ndef = new IntentFilter(); if (readFromTag(getIntent())) { Toast.makeText(this, readResult, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "标签数据为空", Toast.LENGTH_SHORT).show(); } } isFirst = false; } } private String ByteArrayToHexString(byte[] inarray) { //converts byte arrays to string int i, j, in; String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; String out = ""; for (j = 0; j < inarray.length; ++j) { in = (int) inarray[j] & 0xff; i = (in >> 4) & 0x0f; out += hex[i]; i = in & 0x0f; out += hex[i]; } return out; } @Override protected void onPause() { super.onPause(); if (nfcAdapter != null) { nfcAdapter.disableForegroundDispatch(this); } } /* * 重写onResume回调函数的意义在于处理多次读取NFC标签时的情况 */ @Override protected void onResume() { super.onResume(); //设置强制竖屏 // setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // 前台分发系统,这里的作用在于第二次检测NFC标签时该应用有最高的捕获优先权. if (nfcAdapter != null && pendingIntent != null && mFilters != null && mTechLists != null) { nfcAdapter.enableForegroundDispatch(this, pendingIntent, mFilters, mTechLists); } } /* * 有必要要了解onNewIntent回调函数的调用时机,请自行上网查询 */ @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) { String id = ByteArrayToHexString(Objects.requireNonNull(intent.getByteArrayExtra(NfcAdapter.EXTRA_ID))); if (readFromTag(intent)) { // Toast.makeText(this, readResult, Toast.LENGTH_SHORT).show(); handleNfc(readResult, null); } else { Toast.makeText(this, "ID:" + id + " 标签数据为空", Toast.LENGTH_SHORT).show(); } } } private JSONObject ndef2Html(Ndef ndef, Parcelable[] messages) { return buildNdefJSON(ndef, messages); } JSONObject buildNdefJSON(Ndef ndef, Parcelable[] messages) { JSONObject json = NfcUtil.ndefToJSON(ndef); if (messages != null) { try { if (messages.length == 1) { NdefMessage message = (NdefMessage) messages[0]; json.put("ndefMessage", NfcUtil.messageToJSON(message)); json.put("type", "NDEF"); } } catch (JSONException e) { Log.e("BaseNfcActivity", "Failed to convert ndefMessage into json", e); } } return json; } public void getNdefMessage(Intent intent) { synchronized (this) { try { String action = intent.getAction(); if (action != null) { Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (detectedTag != null) { mTag = detectedTag; } } Ndef ndef = Ndef.get(getTagHandle()); Parcelable[] message = new NdefMessage[] {ndef.getNdefMessage()}; handleNfc(String.valueOf(ndef2Html(ndef, message)), null); } catch (Exception exception) { Log.d("BaseNfcActivity", exception.toString()); handleNfc(null, exception.toString()); } } } /** * 读取NFC标签数据的操作 */ private boolean readFromTag(Intent intent) { String action = intent.getAction(); if (action == null) { return false; } Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (tag == null) { return false; } mTag = tag; Ndef ndef; switch (action) { case NfcAdapter.ACTION_NDEF_DISCOVERED -> { ndef = Ndef.get(tag); Parcelable[] message = intent.getParcelableArrayExtra((NfcAdapter.EXTRA_NDEF_MESSAGES)); readResult = String.valueOf(ndef2Html(ndef, message)); } case NfcAdapter.ACTION_TECH_DISCOVERED -> { if (Arrays.asList(tag.getTechList()).contains(Ndef.class.getName())) { ndef = Ndef.get(tag); readResult = String.valueOf(ndef2Html(ndef, new NdefMessage[]{ndef.getCachedNdefMessage()})); } else { Log.i("app ACTION_TECH_DISCOVERED 2", String.valueOf(tag)); } } } return readResult != null; } //处理NFC public void handleNfc(String result, String errorMessage) { //showToast(result); //Toast.makeText(this, result, Toast.LENGTH_LONG).show(); } public void showLog(String logText) { if (isApkInDebug(BaseNfcActivity.this)) { if(TextUtils.isEmpty(logText)){ Log.i("app", "logText is null"); }else{ Log.i("app", logText); } } } /** * 判断当前应用是否是debug状态 */ public static boolean isApkInDebug(Context context) { try { ApplicationInfo info = context.getApplicationInfo(); return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } catch (Exception e) { return false; } } /** * 显示toast * * @param text */ public void showToast(String text) { if (text != null && !text.trim().equals("")) { Toast.makeText(this, text, Toast.LENGTH_SHORT).show(); } } /** * 往标签写数据的方法 */ public void writeNFCTag(String command) throws FormatException, JSONException { if (mTag == null) { return; } NdefMessage ndefMessage = new NdefMessage(stringsToBytes(command)); //转换成字节获得大小 int size = ndefMessage.toByteArray().length; try { //2.判断NFC标签的数据类型(通过Ndef.get方法) Ndef ndef = Ndef.get(getTagHandle()); //判断是否为NDEF标签 if (ndef != null) { ndef.connect(); //判断是否支持可写 if (!ndef.isWritable()) { return; } //判断标签的容量是否够用 if (ndef.getMaxSize() < size) { return; } //3.写入数据 ndef.writeNdefMessage(ndefMessage); Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show(); ndef.close(); ndef.connect(); } else { //当我们买回来的NFC标签是没有格式化的,或者没有分区的执行此步 //Ndef格式类 NdefFormatable format = NdefFormatable.get(mTag); //判断是否获得了NdefFormatable对象,有一些标签是只读的或者不允许格式化的 if (format != null) { //连接 format.connect(); //格式化并将信息写入标签 format.format(ndefMessage); Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "写入失败", Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { } } }