アプリをAndroidビームへ対応させるのは難しくはありません。しかし、Android 4.0(API Level 14)でのAndroidビームとAndroid 4.1(API Level 16)でのAndroidビームは仕組みが若干異なり、排他的にしか使用できなくなっています。
NFCによる通信は最大で424kbpsと、速くありません。NFCタグとの通信のように、数十バイト程度であれば、まったく問題ありませんが、例えばAndroidビームで相手に写真を送ろうと思ったら、両方の端末を数十秒〜数分はかざし続けなければならず、非常にわずらわしいです。
そこで、Android 4.1のAndroidビームは、「NFCで通信を開始し、Bluetoothでコンテンツを送信する」方式に変更されました。これは、NFC ForumとBluetooth SIGが共同で策定した「Bluetooth Secure Simple Pairing Using NFC(BTSSP)」という仕様に基づく実装です。この方式の従来と比較したメリットは以下の通りです。
Bluetoothは、OFFになっていてもAndroidビームを実行すると自動的にONになります。自動的にONにした場合でも、送信完了後、また自動的にOFFに戻してくれるのもポイントです。送受信端末間でペアリング情報が作られることもないので、この点も安心です。
では、この2つのAndroidビームの使い方を見ていきましょう。
Androidビームをアプリに組み込むには、AndroidManifest.xmlに以下の宣言が必要です。
<uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" />
Androidビームアプリでは、Bluetoothの宣言は必要ありません。Bluetoothは別プロセスで動作します。
アプリには以下のIntentFilterを設定しています。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/com.example.android.beam" /> </intent-filter>
特定のMIME-TypeのNDEF(NFC Data Exchange Format)メッセージを受け取るようにしています。MIME-Typeから分かる通り、このIntentFilterはこのアプリ専用のNDEFメッセージを受け取る設定です。
後で説明しますが、特定のアプリに対してNDEFメッセージを送信することで、このIntentFilterは不要になります。汎用的なデータを受信したい場合、こうした定義をしておくとよいでしょう。
アプリ画面は下図のように構成されていて、テキストメッセージ送信、URLアドレス送信、PNGファイル送信が行えます。どのラジオボタンも選択していない場合、NFCで送信先を認識しても、「タップしてビーム」画面にはならないようにしてあります。
スパナアイコンをタップすると、AndroidビームのON/OFFを設定する画面を開けます。AndroidビームはNFCがONになっていなければならないため、実際にはAndroidビームのON/OFF設定画面よりも、NFCのON/OFFを設定する画面を以下の要領で開く方が良いでしょう。
startActivity(new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS)); // API Level 16以降の場合は以下 startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
サンプルアプリの動作としては、テキストメッセージとURLアドレスはAndroid 4.0のAndroidビームで、PNGファイルはAndroid 4.1のAndroidビームでBluetoothを併用して送信します。これらは排他的に使う必要がありますが、その方法を理解すれば簡単に両方をアプリに組み込むことが可能です。
アプリの起動時のシーケンスは以下の通りです。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mInfoText = (TextView) findViewById(R.id.textView); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { mInfoText = (TextView) findViewById(R.id.textView); mInfoText.setText("NFC is not available on this device."); } else { mNfcAdapter.setOnNdefPushCompleteCallback(this, this); mRadioGroup = (RadioGroup) findViewById(R.id.radioGroup); mRadioGroup.setOnCheckedChangeListener(this); savePng(); } }
重要なのは、NFCをサポートしている端末かどうかを7行目の「NfcAdapter#getDefaultAdapter()」で判定している個所です。サンプルでは、サポートしていなければ操作しても機能しないようにしています。
17行目の「savePng()」というメソッドは、アプリアイコンをアプリフォルダに保存するメソッドです。
private void savePng() { String filename = "ic_launcher.png"; File dir = getFilesDir(); mPng = new File(dir, filename); Log.d(TAG, "mJpeg is " + (mPng.exists() ? " exists." : "not exists.")); if (!mPng.exists()) { Drawable d = getResources().getDrawable(R.drawable.ic_launcher); Bitmap bitmap = ((BitmapDrawable) d).getBitmap(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] data = baos.toByteArray(); FileOutputStream fos = null; try { // 送信するファイルは他のプロセスでも読み取れるようにしておくこと fos = openFileOutput(filename, Context.MODE_WORLD_READABLE); fos.write(data); Log.d(TAG, "file saved to '" + mPng.getPath() + "'"); } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
これはBluetoothでファイルを送信するために、他のプロセスでアクセス可能な場所にファイルを保存する処理です。
注意しなければならない点は、15行目のようにファイル保存時にファイルのパーミッションを「Context.MODE_WORLD_READABLE」にして、どのプロセスからでも読み取り可能にしておくことです。もしBluetooth送信プロセスでファイルが読み取れなかった場合、エラー(内部的にはNullPointerException)が発生して送信に失敗します。
Copyright © ITmedia, Inc. All Rights Reserved.