NDEF Recordのバイト列構成

NDEF Recordのバイト列構成

MB
Message Begin(1 bit)
現在のレコードが、NDEF Messageの始まりであることを示す
NDEF messageの最初になるNDEF recordには
必ずこのbit に 1 をセットする

ME
Message End(1 bit)
現在のレコードが、NDEF Messageの終わりであることを示す
NDEF Messageの最後になる NDEF recordには必ずこのbitへ1をセットする

CF
Chuck Flag(1 bit)
このフラグが立っているレコードは
分割されたレコードの最初か、中間であることを示す

SR
Short Record(1 bit)
容量を節約するためのフラグ
これに1をセットすると
PAYLOAD_LENGTHが通常は4byte必要なところを
1byteにすることができる
ただし、PAYLOADが255byte以下であること

IL
ID_LENGTH field is present(1 bit)
ID_LENGTHレコードがあるかどうかを表す
IDが必要ないなら、このレコードへ0をセットする
と、
NDEFの容量が節約できる
IDを指定した場合、
ID_LENGTHとIDは省略すること

TNF
Type Name Format(3 bit)
このレコードが持つTYPE ID PAYLOADに
どのような種類のデータが入っているか決定する

TYPE_LENGTH
タイプの長さをバイト単位で表す

PAYLOAD_LENGTH
payloadの長さをバイト単位で表す

ID_LENGTH
IDの長さをバイト単位で表す

TYPE
タイプを表す

ID
IDを表す

PAYLOAD
payloadを表す

このように、詳細なNDEF recordの構成を見ると
TNF
タイプ
ID
payload
以外のほとんどのフィールドは、
レコードの長さに関するもの

Androidは、この部分を自動的に計算してくれるため
バイト列から自分でNDEF recordを作ることなく扱うことができる

自分でNDEF recordをバイト列から作成することも可能だけど面倒

NDEFは、通信フォーマットとして定義されているので
原理的にデータをバイト列にしたり
それを元に戻したりできる

Androidでは、NdefMessageクラスは
コンストラクタにバイト列 byte[] を指定することができるので
これを使って自分で複数のNDEF recordを作成できる

NDEF Recordでクラスはなく
NdefMessageクラスを使うのは

NdefRecordクラス単位だと

headerにある
MB
ME
の扱いができないので、この部分の情報を失うから

Androidでは、NdefMessageのコンストラクタを利用して
1つまたは、複数のNdefRecordインスタンスを格納したときに
初めてNdefRecordの順序が決定し
各NdefRecordのMB ME のビットが決まる

そのため、NdefRecord単体のインスタンスをバイト列から生成する場合も
NdefMessageのコンストラクタを利用する

NDEF構成データメモ

NDEF構成データメモ

ID(payload ID)について

このフィールドは
アプリがpayloadを識別するときに使う
あまり利用することはない

また、このフィールドは任意のoption扱いになる
これも可変長なので
レコード内には長さを示す
ID_LENGTHを含める必要がある

payloadについて

データ本体になる
TNFやpayload typeにより
どのような内容が入っているかが定義される

このフィールドも可変長なので
レコード内には長さを示す
PAYLOAD_LENGTH
を含める必要がある

AndroidのNDEFレコードクラスは
TNF
type
ID
payload
の4つを指定すると
残りのフィールドは自動的に補ってくれる

payload type メモ

payload type メモ

このフィールドは、recordのpayloadがどのような情報か示す

recordを処理するアプリは
この値をチェックして
どのようにデータ処理すればよいか知ることができる

この値は、TNFによりどのような値が入るかが変わる

代表的なのは
0x01(NFC forum well known type)

0x02(media type)

まず、TNFが
0x01(NFC forum well known type)の場合

この場合、payloadは NFC forum RTDで定義されている値を入れる

値は以下のようになる

0x61,0x63(“ac”)
RTD Alternative Carrie type
NdefRecordクラスの定数は
RTD_ALTERNATIVE_CARRIER

0x48,0x63(“Hc”)
RTD Handover Carrier type
NdefRecordの定数は
RTD_HANDOVER_CARRIER

0x48,0x72(“Hr”)
RTD Handover Request type
NdefRecordの定数は
RTD_HANDOVER_RECORD

0x48,0x73(“Hs”)
RTD Handover Select type
NdefRecordの定数は
RTD_HANDOVER_SELECT

0x53,0x70(“Sp”)
RTD Smart Poster type
NdefRecordの定数は
RTD_SMART_POSTER

0x54(“T”)
RTD Text type
NdefRecordの定数は
RTD_TEXT

0x55(“U”)
RTD URI type
NdefRecordの定数は
RTD_URI

次に、TNFが0x02(media-type)を示す場合
ここには、MIME-type(RFC2046)を指定する
たとえば、PNG形式のアイコンの場合
image/png
となる
JSONデータの場合なら
application/json
となる

この形式に対応するデータは
payloadへ入れる

また、このフィールドは可変長なので
レコード内には長さを示す
TYPE_LENGTH
を含める必要がある

TNF メモ

TNF メモ

0x00(Empty)
recordに対応したtype payloadがない
これを使った場合、record内のTYPE_LENGTH
つまりタイプの長さ

そして
PAYLOAD_LENGTH これは、payloadの長さ
これらはゼロで
それに対応する値の 
type
id
payload
は省略する

このTNFは、アプリでpayloadがなくなったときや
NDEF messageの終わりであると判断されたときに使う

0x03(absolute-URI)
RFC3986で定義されている絶対URIに沿う値がtypeにふくまれること表す

URIは、0x01(NFC forum well known type)
にも定義されているため
これはあまり使わない

0x04(NFC forum external type)
NFC forum RTD仕様に定義されている外部タイプ名に沿う値がtypeに含まれることを表す
こちらは、RFC2141で定義されているURNへ準拠したもので
名前を一意に識別するときに使う

0x05(unknown)
payloadが不明であることを表すときに使うTNF
これは、MIMEとして定義されている
application/octet-stream
と類似している
これを使うときには
record内のTYPE_LENGTHは0
typeは省略すること

0x06(UnChanged)
NDEF messageを複数に分割するときに使うTNF
これが指定されたrecordは
分割されたpayloadの中間で利用しないといけない

また、これは、いくつかの他のrecordのなかでも使えない
これを使うときには
record内のTYPE_LENGTHは0
typeは省略すること

0x07(Reserved)
今後のために予約されているTNF
まだ使われていない

TNF(Type Name Format)について

TNF(Type Name Format)について

TNFは、NDEFがどのような種類かを表す値で、3 bit で表す
このTNFは、以下のように定義されている

0x00

定数はTNF_EMPTY

0x01
NFC forumで定められている既知のタイプ
定数はTNF_WELL_KNOWN

0x02
RFC2046で定義されたメディアタイプ
定数はTNF_MIME_MEDIA

0x03
RFC3986で定義された絶対URI
定数は、TNF_ABSOLUTE_URI

0x04
NFC forum外部タイプ
定数は、TNF_EXTERNAL_TYPE

0x05
不明
定数は、TNF_UNKNOWN

0x06
変更なし
定数は、TNF_UNCHANGED

0x07
予約済み
定数はない

NFC Recordの定数は
AndroidのNfcRecordクラスであらかじめ定義されている定数

TNFを使うときには、この定数を利用する

例えば、TNF_WELL_KNOWNなら
NdefRecord.TNF_WELL_KNOWN
とすることで、この値へアクセスできる

特に重要なのは、0x01 0x02の値

まず、0x01
NFC forum well known type
について
これは、NFC Forum RTD仕様に定義されているRTD type name formatに沿う値がtypeに含まれることを表す

次に、0x02
media type について
これは、RFC2046により定義されているmedia typeに沿う値がtypeに含まれることを表す

RFC、つまり
Request For Comments
これは、さまざまな技術仕様が定められている仕様書のこと

これらの中に media typeの定義がされていて
media typeのTNFを使うには
これらの仕様に則ったPayloadでないとだめ

media typeは、例えば
PNG画像ならimage/png

連絡先データなら
text/x-vcard

また、NFC Forum RTD(Record Type Definition)は
NFC Forumが定義した仕様で
TNFが 0x01 もしくは 0x04 のときには
この仕様に沿った NDEF recordにする必要がある

NDEF recordを作る場合、多くの場合は
0x01 0x02のいずれかを使うことになる

NDEF解説

NDEF解説

NDEFは、1つのNDEF messageという単位で構成される

そして、このNDEF messageは
複数のNDEF recordで成り立っている

このNDEF recordが
アプリで定義したデータ、例えば
URIとかテキストデータ
これらが含まれる部分となっている

NFCタグや他の端末とNDEFデータをやりとりする場合には

NDEF message単位でやりとりする

つまり、一度の通信で、複数の種類のNDEF recordを送受信することができる

一般的には、NFCタグに書き込まれているNDEF recordは1件であることが多い

これは
NFCタグはメモリ容量が少ないものが多いため
複数データが入らなかったり
1つのタグに関係ない複数のデータをいれてしまうと、
タグの役割が曖昧になってしまうから
かざすだけで動作するのがNFCの魅力なので、わかりにくいなら無意味になってしまう

NDEF recordを細かく見ると
これは
Header
Payload
が含まれている

headerにはNDEF messageを軽量に保つ仕組みや
NDEF messageを構成する情報がふくまれている

payloadには
URIやテキストデータなど
データ本体が入っている

payloadにはheader内の情報と関連しているので
payloadに何を入れたいかによって
headerの情報を変更する

NDEFを構成するデータで重要なのは、以下の4つ

TNF(Type Name Format)
タイプ
ID
payload

これらの項目は、公式にはフィールドと呼ばれる

headerは、TNFとタイプとIDを合わせたもの

タイプとTNFは、どちらもタイプとつくけど、別物なので注意

NDEFの基本

NDEFの基本

NFCでやりとりするデータは様々なものがあり
これらを独自の形式でやりとりしていると
特定の機器でしか使えない

これらのデータをNDEFという仕様をベースにすることでAndroid以外のNFC機器とデータを交換できるようになる

NFCの仕様の中で
NDEF、つまり
NFC Data Exchange Format
は、
やりとりするデータ形式について取り決めたもの

NDEF形式は
NFCのやりとりに特化した計量なバイナリ形式になっている

NDEFでやりとりできるデータは

多言語対応のテキストデータ

ftp://
http://
などのURLや
um:isbm4-8339-0450-4
などのURNを含むさまざまなURI

Bluetooth機器に接続するための認証情報を含んだデータ

画像データなど、MIME-Typeを含んだデータ

連絡先データ

タグ情報からステータス取得

タグ情報からステータス取得

getIntent()で取得したIntentオブジェクトに対して
NcfAdapter.EXTRA_NDEF_MESSAGESキーを指定して
intent.getPercelableArrayExtra()を呼び出すことでテキストデータが取得できる

NDEF messageは配列化されているため
0番目で先頭のNDEF messageが取得できる

同様にNDEF recordも配列化された状態で格納されていているので
NdefMessage.getRecord()で
NDEF messageの先頭のNDEF recordを取得し
さらに
NdefRecord.getPayload()で先頭のpayloadを取得
ソースにすると
Percelable[] rawMsgs =intent.getPercelableArrayExtra(NcfAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage ndefMessage = (NdefMessage) rawMsg[0];
String message = new String(ndefMessage.getRecords()[0].getPayload());

N2 TTS で日本語読み上げ

N2 TTS で日本語読み上げ

まず
N2 TTS のパッケージ名を使うので
これを定数に格納する

private static final String N2TTS =”jp.kddilabs.n2tts”;

同様に
TextToSpeechオブジェクトを格納する変数
private TextToSpeech tts;

TextToSpeech エンジン初期化完了フラグ
private boolean ttsInit;

次に、onCreate() の中へ
TextToSpeechオブジェクトを作成

tts = new TextToSpeech(this, this, N2TTS);
とする

引数の意味は
最初の this が
オブジェクトを利用するときの context
通常は this

次の this が
オブジェクトの初期化が完了したことを示す
OnInitListener オブジェクト

N2TTS
が使用するスピーチエンジン
これは、先に定数で指定したもの
そのときのソースが
private static final String N2TTS =”jp.kddilabs.n2tts”;

そして SpeechEngineが完了したときに呼ばれるメソッドは
onInit()

これを使うには
implements OnInitListener する必要があるので

implements OnClickListener

implements OnClickListener,OnInitListener {
とする

これで 右クリックで
source > override /implements

onInit() を追記できるので
ここに処理を書く
ここへは
読み上げ実行処理の有効化を書く

今回、判定を入れてあるのは
リスト項目をタップしたら
という条件があるから
この判定にフラグを使っている

if(status == TextToSpeech.SUCCESS){
は初期化が正しく成功したかの判定
処理が正しく成功したのなら
TextToSpeech.SUCCESS
となる

これで後はフラグである ttsinit を true にするだけ

次に
音声読み上げ処理の実装

音声読み上げで内部では
キュー構造が用意されていて
読み上げ処理は
そのキューに投入された順に実行される

キューを操作する場合
TextToSpeechクラスに定義された2つの定数を使う
定数は
QUEUE_ADD
新しく読み上げリクエスト追加

QUEUE_DELETE
現在のキュー内容を破棄し
直ちに読み上げリクエストを実行

この定数は
TextToSpeecオブジェクトで
何らかの読み上げリクエストを使うすべてのメソッドで使える

今回は リスト項目のクリック時にスピーチ実行する

このActivity はActivity クラスに
ListView オブジェクトの機能が統合された
ListActivity クラスをベースにしているので
onListItemClick() を
overrideするだけでList をクリックした時の動作を実装できる

中の処理には
if(ttsInit){
tts.speak(listAdapter.getItem(position), TextToSpeech.QUEUE_ADD, null);
}
を追加

onListItemClick() のパラメータの説明は
ListView l,
クリックされたlistview オブジェクト

View v,
クリックされたリストの項目ビューオブジェクト

int position,
クリックされたリスト項目のインデックス

long id
クリックされたリスト項目のID
となる

今回はAdapter Object のgetItem() から文字列を取得して
speack() に設定している

tts.speak(listAdapter.getItem(positi on), TextToSpeech.QUEUE_ADD, null);
で音声の読み上げ

この speak() のパラメータは
listAdapter.getItem(position)
が読み上げたい文字列

TextToSpeech.QUEUE_ADD
が処理をキューに追加する方法の指定
今回は QUEUE_ADD

null

読み上げ処理の追加パラメータ
今回は使わないので null
ここでは読み上げIDとかステレオ設定などをする

ここでキューに登録された文字列は
キューに格納され
読み上げ可能なタイミングになると読み上げられる

これで、アプリを起動してリストをタップすると
音声で文字列が読み上げられる

これを応用すれば twitter とかルート案内とかでもできる

とりあえず日本語読み上げはできたので
今後は Edit Text で入力し
それを喋らせるようにしてみよう

さすがに音声入力だと精度があまりよくないようだ

N2 TTS インストール

N2 TTS インストール

Android の音声認識は読み上げに用いるエンジンを切り替えることで様々な言語の文字列を読み上げることができる

日本語の読み上げに関しては
日本語対応エンジンをインストールする必要がある

今回は
KDDI研究所が作ったN2 TTS を使う

これはとくに権限を要求されることはないので安心して使える

今回使用したのは Google Nexus 7

まず GooglePlay で
N2 TTS を探して実機にインストール

インストール完了後
このアプリを開くと、利用規約がでるので同意してOKをおす

すると設定画面がでるので
テキスト読み上げの設定
をタップすると

優先するエンジンの選択がでるので

現在
Google テキスト読み上げエンジンになっているのを
KDDILABS N2 TTS
のほうにする

このときに、注意がでるけど
気にせず続行

プログラムから実行する方法もあり
こちらの場合
アプリで音声読み上げ機能を使うときに
オブジェクトのコンストラクタで NS TTS パッケージを設定するという方法もある

Android 音声読み上げを使うには
TextToSpeech() を使うことになる

アプリから文字列読み上げをするには常にこのオブジェクトに対して処理を行う

TextToSpeechクラスのコンストラクタを呼び出すと
まず音声認識エンジンの初期化処理が始まる

この初期化処理が終わると
OnInitListener イベントリスナーの
onInit() が呼び出され
音声認識が可能になる

音声認識を行う場合
TextToSpeechオブジェクトの speak() を使う

アプリで利用終了したら
TextToSpeechオブジェクトの shutdown() でオブジェクトを開放

オブジェクトもメモリみたいに開放すると覚えておくとよいかも