我是 Android 新手,正在研究近场通信以从 NFC 标签读取数据。我既没有支持 NFC 的 Android 手机,也没有 NFC 标签来测试我创建的应用程序。
我发现下面两篇文章介绍了通过触发 Intent 来伪造 NFC 标签扫描。
我根据第一篇文章更改了我的代码,单击按钮后我将触发第一个 Activity 中所需的 Intent 。而我又创建了一项能够处理相同 Intent 的 Activity 。 NFC 标签的读取和处理数据基于第二个 Activity 中的按钮单击。
问题是:每当我从第一个 Activity 触发假 NFC 标签扫描 Intent 时,它都会抛出错误“找不到处理 Intent 的 Activity { act=android.nfc.action.NDEF_DISCOVERED (有额外的内容) ) }”。
list 文件如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.expensemanager.saubhattacharya">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.NFC"/>
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<application
android:allowBackup="true"
android:icon="@mipmap/icon1"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Set_Monthly_Target"
android:label="@string/title_activity_set__monthly__target"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.expensemanager.saubhattacharya.MainActivity" />
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*" />
</intent-filter>
</activity>
<activity
android:name=".Add_Daily_Expense"
android:label="@string/add_daily_expense_activity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.expensemanager.saubhattacharya.MainActivity" />
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*" />
</intent-filter>
</activity>
</application>
</manifest>
第一个 Activity 的 Intent 触发代码 fragment 如下:
public void scan_tag (View view)
{
final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, "Custom Messages");
startActivity(intent);
}
处理上述触发器的第二个 Activity 的代码 fragment 如下:
public class Add_Daily_Expense extends AppCompatActivity {
Button read_data;
TextView show_data;
Tag detected_tag;
NfcAdapter nfcAdapter;
IntentFilter[] intentFilters;
public static final String MIME_TEXT_PLAIN = "text/plain";
public static final String MIME_IMAGE_ALL = "image/*";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add__daily__expense);
final PackageManager pm = this.getPackageManager();
show_data = (TextView) findViewById(R.id.show_data);
nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
read_data = (Button) findViewById(R.id.read_nfc);
read_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(Add_Daily_Expense.this);
builder.setMessage("NFC feature is not available on this device!")
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(getApplicationContext(), "NFC feature is available on this device!", Toast.LENGTH_SHORT).show();
HandleIntent(getIntent());
}
}
});
}
public void HandleIntent(Intent intent)
{
String action = intent.getAction();
if(NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action))
{
detected_tag = getIntent().getParcelableExtra(nfcAdapter.EXTRA_TAG);
NDefReaderTask NDefReader = new NDefReaderTask();
NDefReader.execute();
}
}
public void onResume()
{
super.onResume();
if(nfcAdapter != null)
setupForeGroundDispatch(this, nfcAdapter);
}
public void onPause()
{
super.onPause();
if(nfcAdapter != null)
stopForeGroundDispatch(this, nfcAdapter);
}
public void onNewIntent(Intent intent)
{
HandleIntent(intent);
}
public void setupForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
Intent new_intent = new Intent(getApplicationContext(),Add_Daily_Expense.class);
new_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),0,new_intent,0);
intentFilters = new IntentFilter[1];
String[][] techList = new String[][]{};
intentFilters[0] = new IntentFilter();
intentFilters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
intentFilters[0].addCategory(Intent.CATEGORY_DEFAULT);
try {
intentFilters[0].addDataType(MIME_TEXT_PLAIN);
intentFilters[0].addDataType(MIME_IMAGE_ALL);
}
catch(IntentFilter.MalformedMimeTypeException me)
{
me.printStackTrace();
}
nfcAdapter.enableForegroundDispatch(activity, pendingIntent, intentFilters, techList);
}
public void stopForeGroundDispatch (final Activity activity, NfcAdapter nfcAdapter)
{
nfcAdapter.disableForegroundDispatch(activity);
}
public class NDefReaderTask extends AsyncTask <Tag, Void, String>
{
@Override
protected String doInBackground(Tag... params)
{
try
{
detected_tag = params[0];
Ndef ndef = Ndef.get(detected_tag);
ndef.connect();
if(ndef != null)
{
NdefMessage ndefMessage = ndef.getCachedNdefMessage();
NdefRecord[] records = ndefMessage.getRecords();
for(NdefRecord ndefRecord : records)
{
if((ndefRecord.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) || (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN))
{
byte[] payload = ndefRecord.getPayload();
String encoding1 = "UTF-8";
String encoding2 = "UTF-16";
String textEncoding = ((payload[0] & 128) == 0) ? encoding1 : encoding2;
int languageCodeLength = payload[0] & 0063;
return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
}
}
}
ndef.close();
}
catch (UnsupportedEncodingException UE)
{
UE.printStackTrace();
}
catch (IOException IE)
{
IE.printStackTrace();
}
return null;
}
@Override
protected void onPreExecute()
{
}
protected void onPostExecute(String result)
{
if(result != null)
{
show_data.setText(result);
}
}
}
}
我的问题是:我在这里做错了什么?还有其他方法可以通过伪造 NFC 标签扫描来测试我的应用程序吗?
最佳答案
您为 NDEF_DISCOVERED Intent 过滤器指定 MIME 类型过滤器:
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*" />
因此,伪造的 NFC Intent 需要包含这些 MIME 类型之一以匹配 Intent 过滤器。您可以使用 setType()
方法将类型信息添加到 Intent 中:
public void scan_tag (View view) {
final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
intent.setType("text/plain");
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, ...);
startActivity(intent);
}
另请注意,上述代码不会向 NFC Intent 添加标签句柄。因此,您无法使用
获取Tag
对象
detected_tag = getIntent().getParcelableExtra(nfcAdapter.EXTRA_TAG);
因此,您也无法使用以下方式获取 Ndef
连接类的实例
Ndef ndef = Ndef.get(detected_tag);
您可能想研究以下有关模拟标记对象的问题/答案:
- How to mock a Android NFC Tag object for unit testing
- Is there a way to create an ACTION_NDEF_DISCOVERED intent from code
- How to simulate the tag touch from other application
最后,请注意您的代码中还存在其他几个问题。
关于android - 通过伪造 NFC 标签扫描来测试应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34882171/