我有一个基本的倒计时应用程序,从10秒倒计时时,按钮被点击。当与特定NFC标签进行接触时,最简单的计时器启动方法是什么?(NTAG203)
我已经阅读了几个NFC教程,我唯一能够开始工作的是一个简单的标签阅读器。如果我想让一个特定的NFC标签触发一个简单的计时器应用程序,就像一个按钮,我该怎么做?最好是尝试读取ID还是将纯文本解释为标识符?
Thanks (Using Android Studio)
NFC应用程序主活动(代码取自本教程:http://shaikhhamadali.blogspot.com/2013/10/near-field-communication-nfc-android.html
package com.example.nfctest2.nfctest2app;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
public static final String TAG = "NfcTut";
private TextView tV_ReadNFC;
private NfcAdapter nfcAdapt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//initialize control
tV_ReadNFC = (TextView) findViewById(R.id.tV_ReadNFC);
//initialise nfc adapter
nfcAdapt = NfcAdapter.getDefaultAdapter(this);
//check is NFC adapter initialized null when device doesn't support NFC
if (nfcAdapt == null) {
// device deosn't support nfc
Toast.makeText(this, "your device doesn't support NFC.", Toast.LENGTH_SHORT).show();
finish();
return;
}
//check is NFC adapter feature enabled
if (!nfcAdapt.isEnabled()) {
tV_ReadNFC.setText("NFC is disabled.");
} else {
tV_ReadNFC.setText(R.string.attachNFCToRead);
}
handleIntent(getIntent());
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
//Call this before onPause, to avoid an IllegalArgumentException.
stopForegroundDispatch(this, nfcAdapt);
super.onPause();
}
@Override
protected void onNewIntent(Intent intent) {
handleIntent(intent);
}
private void handleIntent(Intent intent) {
//get action from intent
String action = intent.getAction();
//is action matches the NDEF_DISCOVERED
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
//what is the mime type
String type = intent.getType();
//is text plain or not
if (MIMETYPE_TEXT_PLAIN.equals(type)) {
//create tag instance and retrieve extended data from intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//execute background task
new NdefReaderBgTask().execute(tag);
} else {
Log.d(TAG, "mime type is not text/plain: " + type);
}
}
//is action matches the ACTION_TECH_DISCOVERED
else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
// In case we would still use the Tech Discovered Intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//get the available technologies
String[] techList = tag.getTechList();
//get class name
String searchedTech = Ndef.class.getName();
for (String tech : techList) {
//tag matched then execute background task
if (searchedTech.equals(tech)) {
new NdefReaderBgTask().execute(tag);
break;
}
}
}
}
/**
* @param act The corresponding {@link Activity} requesting the foreground dispatch.
* @param adp The {@link NfcAdapter} used for the foreground dispatch.
*/
public static void requestForegroundDispatch(final Activity act, NfcAdapter adp) {
//create instance of intent
final Intent intent = new Intent(act.getApplicationContext(), act.getClass());
//set flags on top
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//crate instance of pending intent
final PendingIntent pendingIntent = PendingIntent.getActivity(act.getApplicationContext(), 0, intent, 0);
//create intent filters array
IntentFilter[] filters = new IntentFilter[1];
//create 2D array of techlist String
String[][] techList = new String[][]{};
// Note: This is the same filter as in our manifest.
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
try {
//add data type
filters[0].addDataType(MIMETYPE_TEXT_PLAIN);
} catch (MalformedMimeTypeException e) {
//throw exception on different mime type
throw new RuntimeException("Check your mime type.");
}
//enable foreground dispatch to current activity
adp.enableForegroundDispatch(act, pendingIntent, filters, techList);
}
public static void stopForegroundDispatch(final Activity act, NfcAdapter adp) {
adp.disableForegroundDispatch(act);
}
private class NdefReaderBgTask extends AsyncTask<Tag, Void, String> {
@Override
protected String doInBackground(Tag... params) {
Tag tag = params[0];
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
// when NDEF is not supported by this Tag.
return null;
}
//Get the NdefMessage that was read from the tag at discovery time.
NdefMessage ndefMessage = ndef.getCachedNdefMessage();
//Get the NDEF Records inside this NDEF Message.
NdefRecord[] records = ndefMessage.getRecords();
for (NdefRecord ndefRecord : records) {
if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
try {
return readNDEFRecordText(ndefRecord);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unsupported Encoding", e);
}
}
}
return null;
}
private String readNDEFRecordText(NdefRecord record) throws UnsupportedEncodingException {
// get record pay load variable length
byte[] payload = record.getPayload();
// Get the Text Encoding
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
// Get the Language Code
int languageCodeLength = payload[0] & 0063;
// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
// e.g. "en"
// Get the Text
return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
}
@Override
protected void onPostExecute(String result) {
if (result != null) {
tV_ReadNFC.setText("Content in your TAG: " + result);
}
}
}
}
使用前台调度来接收NFC事件是一个好的开始。
识别一个特定的标签很大程度上取决于你在上下文中需要什么:
-
可以匹配标签id。这当然是最简单的方法,因为每个标签都会暴露NFC设备在标签枚举和防碰撞期间使用的一些ID。然而,这种方法有一些限制:
-
不是所有的标签都暴露静态id。一些标签在通电时生成一个新的随机ID。因此,从其ID中识别这样的标签是不可能的。
-
根据协议的不同,id的长度可能不同。例如,ISO 14443A (
NfcA
)标签可能具有4字节,7字节或10字节的UID。因此,在创建有效标记数据库时必须考虑这一点。 -
你不能基于一个特定的ID自动启动你的应用程序。
-
你必须在一些数据库中记录每个允许的ID,并且不容易定义大量允许的标签。
-
-
您可以在标签上使用特定的NDEF记录。在我看来,这是最好的方法。
我将避免使用Text记录类型(或Text/* MIME类型记录),因为这些类型应该用于传输人类可读和(更重要的是)人类解释的文本
。相反,我建议您定义自己的NFC论坛外部类型名称(规范可以从NFC论坛免费获得),并使用该类型名称创建一个记录。
这允许你通过过滤特定的类型名称来轻松地自动启动你的(并且只有你的)应用程序。
您可以根据具有特定类型名称的NDEF记录的存在或
开始倒计时。您可以为标签添加特定的数据有效载荷(例如自定义标识符),以允许仅具有此类型名称的某些标签开始倒计时或区分携带该类型名称记录的不同标签。
对于ID匹配场景,我建议您这样修改前台调度激活(不要忘记在onResume()
中实际激活前台调度):
public static void requestForegroundDispatch(final Activity act, NfcAdapter adp) {
final Intent pendingIntent = PendingIntent.getActivity(act, 0, new Intent(act, act.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter[] filters = new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED) };
String[][] techList = new String[][] { new String[] { NfcA.class.getName() } };
adp.enableForegroundDispatch(act, pendingIntent, filters, techList);
}
这将匹配任何ISO 14443类型A标签(如NTAG203)。
然后你可以在你的handleIntent方法中检索标签的ID,像这样:
private void handleIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
byte[] id = tag.getId();
// do something with the ID
}
}