It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Android】NFCを使ってみる① (読み込み処理)

NFC(Near Field Communication)は、近距離無線のテクノロジーで、
Android 2.3からサポートされている技術です。

また、Android 4.0からは、Android Beamという機能が加わり、
2つのAndroid搭載のデバイス間でのピアツーピアのデータ交換が可能となりました。

アプリケーションへの NFC タグのディスパッチ方式

AndroidデバイスがNFCを検知すると対応するインテントを発行します。
NFCに関する起動インテントには、下記の3種類があり、 それぞれの優先度が決まっています。

[ACTION_NDEF_DISCOVERED]

読み込んだタグが、NDEF(NFC Data Exchange Format)ペイロードを持つ場合、
このアクションが定義されたアクティビティにIntentが通知されます。

起動インテントの優先度としては、最も高く、
このアクションでアクティビティが起動した場合は、ACTION_TECH_DISCOVEREDまたはACTION_TAG_DISCOVEREDで登録されているアクティビティがあっても起動されません。

[ACTION_TECH_DISCOVERED]

ACTION_NDEF_DISCOVEREDインテントをハンドルするアクティビティが登録されていない場合には、 このアクションでが定義されたアクティビティにIntentが通知されます。 優先度としては、ACTION_NDEF_DISCOVEREDの次に高いです。

[ACTION_TAG_DISCOVERED]

このインテントACTION_NDEF_DISCOVERED または、ACTION_TECH_DISCOVEREDインテントをハンドルするアクティビティがない場合に開始されます。

読み込みサンプル

以下のサンプルは、NFCカードをかざすとNFCの識別子情報を読み取るサンプルになります。

Android Manufestの定義

Android Manufestには、NFCを使用するためのパーミッションの定義と、
各ディスパッチ方式に対応するintent-filterの定義が必要です。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.nfcreadsample"
    android:versionCode="1"
    android:versionName="1.0" >

    <!-- NFCを使用する為のパーミッションを付与 -->
    <uses-permission android:name="android.permission.NFC" />
    <!-- Google Playで、NFCハードウェアを持つデバイスのみに制限するための   機能を定義 -->
    <uses-feature
        android:name="android.hardware.nfc"
        android:required="true" />

    ・・・

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.test.nfcreadsample.MainActivity"
            android:label="@string/app_name" >
            
             ・・・
            
            <!-- NFC NDEF text -->
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
            
            <!-- NFC TECH -->
            <intent-filter>
                <action android:name="android.nfc.action.TECH_DISCOVERED" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <!-- ACTION_TAG_DISCOVEREDで登録するフィルタを参照する -->
            <meta-data
                android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/nfc_tech_filter" />
            
            <!-- NFC TAG -->
            <intent-filter>
                <action android:name="android.nfc.action.TAG_DISCOVERED" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>

</manifest>

ACTION_NDEF_DISCOVEREDインテントをフィルタするには、
フィルタしたい対象データのタイプを加えたインテントフィルタを宣言します。
上記では、ACTION_NDEF_DISCOVEREDフィルタにtext/plainMIMEタイプを付加しています。

ACTION_TECH_DISCOVEREDインテントをフィルタさせる場合は、
XMLリソースファイルを作成し、tech-listセットの中にアクティビティがサポートするNFCのフォーマット形式を定義します。

作成したXMLファイルは、<project-root>/res/xmlディレクトリ配下へ保存します。

以下は、tech-listリソースファイルのサンプルです。

nfc_tech_filter.xml

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcF</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcV</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NdefFormatable</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

NFCを読み込む

下記では、起動Activityより受信インテントを受け取り、
NFCの起動によるものかを判定しています。

インテントNFCのアクションである場合、
NfcAdapter.EXTRA_IDというキーを使ってNFCIDm(固有識別子)を読み込んでいます。

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        Intent intent = getIntent();
        String action = intent.getAction();
        
        setContentView(R.layout.activity_main);
        
        // NFCかどうかActionの判定
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)
                ||  NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
                ||  NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
            
            // IDm(固有識別子)を表示させる
            String idm = getIdm(getIntent());
            if (idm != null) {
                TextView idmView = (TextView) findViewById(R.id.idm);
                idmView.setText(idm);
            }
        }
    }

    /**
     * IDmを取得する
     * @param intent 受信インテント
     * @return IDm文字列
     */
    private String getIdm(Intent intent) {
        String idm = null;
        StringBuffer idmByte = new StringBuffer();
        byte[] rawIdm = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
        if (rawIdm != null) {
            for (int i = 0; i < rawIdm.length; i++) {
                idmByte.append(Integer.toHexString(rawIdm[i] & 0xff));
            }
            idm = idmByte.toString();
        }
        return idm;
    }
}