|
|
|
|
|
package com.rehome.dywoa;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static com.rehome.dywoa.utils.NfcUtil.stringsToBytes;
|
|
|
|
|
|
|
|
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
|
|
import android.content.Intent;
|
|
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
|
|
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 (BuildConfig.LOG_ERROR) {
|
|
|
|
|
|
if(TextUtils.isEmpty(logText)){
|
|
|
|
|
|
Log.i("app", "logText is null");
|
|
|
|
|
|
}else{
|
|
|
|
|
|
Log.i("app", logText);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示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) {
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|