2012年4月16日 星期一

使用NFC取得Mifare Card的UID

版本:2.3.3以上
卡片:MiFare Classic 1k
目的:感應Mifare卡後取得UID,可以再來做後續處理
作法:
大致上照著這篇文章去做:
http://mifareclassicdetectiononandroid.blogspot.com/2011/04/reading-mifare-classic-1k-from-android.html
1.編輯AndroidManifest.xml,把NFC權限、使用的action放上去,在layout資料夾裏新增xml資料夾,裡面新增一個xml檔記載會用到的NFC讀取種類。
權限:<uses-permission android:name="android.permission.NFC"/>
<uses-feature android:name="android.hardware.nfc" android:required="true"/>

action: <intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="text/plain"/>
</intent-filter>
 <intent-filter>
 <action android:name="android.nfc.action.TECH_DISCOVERED"/>
 </intent-filter>
 <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/filter_nfc"/>
讀取種類filter_nfc:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list> 
        <tech>android.nfc.tech.NfcA</tech> 
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.IsoDep</tech>               
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareUltralight</tech> 
    </tech-list>
</resources> 


2.程式碼部分,重點在於
(1)副程式resolveIntent裡IF的判斷,參考文章為ACTION_TECH_DISCOVERED
但是我用ACTION_NDEF_DISCOVERED才跑的進去,不知道跟我在寫到這之前把卡片寫了一個訊息進去有沒有關係?
(2)以byte陣列抓到ID,這時只是0跟1的資料,無法直接轉成string,再來要把他轉成16進位,這個轉換的步驟也是網路上找一下就有了。
參考:http://www.rgagnon.com/javadetails/java-0596.html

public PendingIntent mPendingIntent;
public TextView uidtext;
public IntentFilter[] mFilters;
public NfcAdapter mAdapter;
public String[][] mTechLists;

public void onCreate(Bundle savedInstanceState) {
 mAdapter = NfcAdapter.getDefaultAdapter(this);
 uidtext=(TextView)findViewById(R.id.uidtext);
 mPendingIntent = PendingIntent.getActivity(this, 0,
    new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
        
        try {
            ndef.addDataType("*/*");
        } catch (MalformedMimeTypeException e) {
            throw new RuntimeException("fail", e);
        }
        IntentFilter[] mFilters = new IntentFilter[] {
                ndef,
        };

        // Setup a tech list for all NfcF tags
        String[][] mTechLists = new String[][] { new String[] { MifareClassic.class.getName() } };
        
        Intent intent = getIntent();
        resolveIntent(intent);
 }
@Override
 protected void onNewIntent(Intent intent) {
      Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
         resolveIntent(intent);  
 }

 @Override
 protected void onPause() {
  super.onPause();
  mAdapter.disableForegroundDispatch(this);
 }

 @Override
 protected void onResume() {
  super.onResume();
  mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
    }
void resolveIntent(Intent intent) {
     Log.i("test", "start method");
     // 1) Parse the intent and get the action that triggered this intent
        String action = intent.getAction();
     // 2) Check if it was triggered by a tag discovered interruption.
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
         Log.i("test", "in if");
     //  3) Get an instance of the TAG from the NfcAdapter
        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        byte[] id = tagFromIntent.getId();
     try {
       uidtext.setText(getHexString(id));
     } catch (Exception e1) {
       e1.printStackTrace();
     }  
    }// End of method
        Log.i("test", "end method");
    }
    public static String getHexString(byte[] b) throws Exception {  
       String result = "";  
       for (int i=0; i < b.length; i++) {  
         result +=  
               Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );  
       }  
       return result;  
     }