Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.5k views
in Technique[技术] by (71.8m points)

android - How to simulate the tag touch from other application

I want to simulate the touch event for my application. My manifest is like

<activity
    android:name=".activity.TagActivity_"
    android:label="@string/app_name"
    android:launchMode="singleTask"
    android:noHistory="true"
    android:permission="android.permission.NFC"
    android:screenOrientation="portrait" >

    <intent-filter>
        <action android:name="android.nfc.action.TECH_DISCOVERED" />
    </intent-filter>
    <meta-data
        android:name="android.nfc.action.TECH_DISCOVERED"
        android:resource="@xml/nfc_tech_filter" />

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data
            android:host="ext"
            android:pathPrefix="/abc:d"
            android:scheme="vnd.android.nfc" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

Question (1): I want to invoke my application from other application. How do I do that? My current code is like:

try {
    final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
    NdefMessage ndefMessage = buildNdefMessage(getTagData());
    intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {ndefMessage});
    startActivity(intent);
} catch (Exception e) {
    e.printStackTrace();
}

But this is not invoking my application. probably because the data type and path prefix do not match. How do I pass this while starting the activity?

Question (2): For temp purpose I have added

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

to make it work, and my application is invoked. But while reading the tag I check the tag type, and since I am not passing any tag type, my app crashes. So how do I create a tag instance? There is no constructor for this. I mean I can't do Ndef ndef = new Ndef();. And I don't basically have a tag so I can't event do Ndef ndef = Ndef.get(tag);.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Regarding question (1): How to invoke NDEF_DISCOVERED intent filter

Your activity ".activity.TagActivity_" filters has two different NDEF_DISCOVERED. The first one matches if the intent contains a URI of the form "vnd.android.nfc://ext/abc:d". The second one matches if the intent contains a data type of "text/plain".

Consequently, you need to either add a matching URI or a matching MIME type to the intent that you use to start that activity.

  1. MIME type "text/plain":

    final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intent.setType("text/plain");
    startActivity(intent);
    
  2. URI "vnd.android.nfc://ext/abc:d":

    final Intent intent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
    intent.setData(Uri.parse("vnd.android.nfc://ext/abc:d"));
    startActivity(intent);
    

Any of the two will start your activity.

NOTE that you specified android:permission="android.permission.NFC" for your receiving activity. You normally would not do that. Specifying this permission means that your sending application needs to have that permission.

Regarding question (2): How to pass a mock tag object?

It's possible to create a mock tag object instance using reflection (note that this is not part of the public Android SDK, so it might fail for future Android versions).

  1. Get the createMockTag() method though reflection:

    Class tagClass = Tag.class;
    Method createMockTagMethod = tagClass.getMethod("createMockTag", byte[].class, int[].class, Bundle[].class);
    
  2. Define some constants for preparing a valid mock NDEF tag instance:

    final int TECH_NFC_A = 1;
    final String EXTRA_NFC_A_SAK = "sak";    // short (SAK byte value)
    final String EXTRA_NFC_A_ATQA = "atqa";  // byte[2] (ATQA value)
    
    final int TECH_NDEF = 6;
    final String EXTRA_NDEF_MSG = "ndefmsg";              // NdefMessage (Parcelable)
    final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength";  // int (result for getMaxSize())
    final String EXTRA_NDEF_CARDSTATE = "ndefcardstate";  // int (1: read-only, 2: read/write, 3: unknown)
    final String EXTRA_NDEF_TYPE = "ndeftype";            // int (1: T1T, 2: T2T, 3: T3T, 4: T4T, 101: MF Classic, 102: ICODE)
    
  3. Create an NDEF message:

    NdefMessage ndefMessage = new NdefMessage(NdefRecord.createMime("text/plain"), "Text".getBytes("US-ASCII"));
    
  4. Create the tech-extras bundle for a Type 2 tag with that NDEF message:

    Bundle nfcaBundle = new Bundle();
    nfcaBundle.putByteArray(EXTRA_NFC_A_ATQA, new byte[]{ (byte)0x44, (byte)0x00 }); //ATQA for Type 2 tag
    nfcaBundle.putShort(EXTRA_NFC_A_SAK , (short)0x00); //SAK for Type 2 tag
    
    Bundle ndefBundle = new Bundle();
    ndefBundle.putInt(EXTRA_NDEF_MAXLENGTH, 48); // maximum message length: 48 bytes
    ndefBundle.putInt(EXTRA_NDEF_CARDSTATE, 1); // read-only
    ndefBundle.putInt(EXTRA_NDEF_TYPE, 2); // Type 2 tag
    ndefBundle.putParcelable(EXTRA_NDEF_MSG, ndefMessage);  // add an NDEF message
    
  5. Prepare an anti-collision identifier/UID for your tag (see Tag.getId() method). E.g. a 7-byte-UID for a Type 2 tag:

    byte[] tagId = new byte[] { (byte)0x3F, (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78, (byte)0x90, (byte)0xAB };
    
  6. Then you can create a mock tag instance by invoking the createMockTag() method

    Tag mockTag = (Tag)createMockTagMethod.invoke(null,
            tagId,                                     // tag UID/anti-collision identifier (see Tag.getId() method)
            new int[] { TECH_NFC_A, TECH_NDEF },       // tech-list
            new Bundle[] { nfcaBundle, ndefBundle });  // array of tech-extra bundles, each entry maps to an entry in the tech-list
    

Once you created that mock tag object, you can send it as part of the NDEF_DISCOVERED intent:

Intent ndefIntent = new Intent(NfcAdapter.ACTION_NDEF_DISCOVERED);
ndefIntent.setType("text/plain");
ndefIntent.putExtra(NfcAdapter.EXTRA_ID, tagId);
ndefIntent.putExtra(NfcAdapter.EXTRA_TAG, mockTag);
ndefIntent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[]{ ndefMessage });

Optionally, you can explicitly define your target activity as the receiving component:

ndefIntent.setComponent(...); // or equivalent

You can then send this intent to your activity:

startActivity(ndefIntent);

The receiver can then use the moch tag object to retrieve instances of the technology classes (e.g. Ndef.get(tag)) but any method that requires IO operations will fail.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...