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() でオブジェクトを開放

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

音声認識機能の実装

音声認識機能の実装

まずは、追加ボタンをクリックした時に音声認識を起動するために
Intent を使って音声認識機能を呼び出す

まず、音声認識機能を呼び出すIntent のREQUEST_IDは
あらかじめ定数で定義しておく

こっちは、最初のアダプター定義のあたりに書く
private static final int REQUEST_SPEECH =1;

次に、実際の処理
ちなみに、書く場所は
case R.id.add:
listAdapter.add(“追加”);
の下

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
で音声認識 intent 作成

intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);

で機能を拡張
それぞれの意味は
RecognizerIntent.EXTRA_LANGUAGE_MODEL

言語モデルを設定

RecognizerIntent.LANGUAGE_MODEL_FREE_FORM

自由に音声を入力したいので
EXTRA_LANGUAGE_MODEL
にしている

次に、/音声認識画面に表示する文字列を設定
今回はthis.getTitle()で呼び出し元Activity の表題にする

intent.putExtra(RecognizerIntent.EXTRA_PROMPT, this.getTitle());
.EXTRA_PROMPT,

音声認識画面に表示する文字列を設定する
ちなみに

Extra() は
文字列をキーとして
値や数値などを格納するリスト
ハッシュとか JSON みたいなイメージ
これを使うことで
intent の詳細設定を追加していくことができる

なので、intent を定義したら
putExtra() で機能追加と考えるとわかりやすい

ここまでできたら
音声認識のダイアログを表示する
例外がでるので
try catch で行う
失敗の可能性のあるものは
try catch と考えるといいかもしれない

あらかじめ、strings.xml で
toast で表示する文字列をつくっておく
これはソースに書くよりも後で変更などのメンテがしやすいため

			//音声認識 dialog 表示
			try{
				startActivityForResult(intent, REQUEST_SPEECH);
			}catch (ActivityNotFoundException e) {
				// TODO: handle exception
				//音声認識が使えなかったら toast で知らせる
				Toast.makeText(this	,getString(R.string.word_disable), Toast.LENGTH_SHORT).show();
				e.printStackTrace();
			}

ここまでできたら
音声入力が正しく終了できたときの処理をつくる

これには
別Activity の処理結果を取得する
onActivityResult() を使う

右クリック > source >
override / implement methods

これはActivity の項目にある
onActivityResult
を選ぶとソースが追記される

ここで
正常処理である場合のみ処理できるように判定する
判定は
if(requestCode == REQUEST_SPEECH && resultCode == RESULT_OK){
でできる

REQUEST_SPEECH

private static final int REQUEST_SPEECH =1;
で設定した定数

ArrayList results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
で結果リストを取得

.getStringArrayListExtra()
認識結果の配列を取得している

このあたりの流れは

http://wiki.livedoor.jp/moonlight_aska/d/%B2%BB%C0%BC%A4%C7%CA%B8%BB%FA%C6%FE%CE%CF%A4%B9%A4%EB
に載っていたのでこれを参考に

音声認識の結果は
data の Extra() に入っているのでこれを
定数の
RecognizerIntent.EXTRA_RESULTS
をキーにして取り出している

if(results.size() > 0){

で0以上、つまり認識結果があるなら
listAdapter.add(results.get(0));
でリストに追加する

ちなみに、
http://wiki.livedoor.jp/moonlight_aska/d/%B2%BB%C0%BC%A4%C7%CA%B8%BB%FA%C6%FE%CE%CF%A4%B9%A4%EB
では
EdutText へ追加するため

inpText.setText(input); 

で文字としてセットしている

private EditText inpText;
inpText = (EditText)findViewById(R.id.result_id);

となっているのが EditText の設定

これで音声入力はできるけど

//音声認識画面に表示する文字列
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, this.getTitle());

になっているから
音声入力を促す画面文字がアプリ名なので微妙だ

intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "音声を入力してください。");

というようにしたほうがすっきりする

Intent によるActivity 呼び出しと戻り値の取得

Intent によるActivity 呼び出しと戻り値の取得

アプリの音声入力を実装
Android の音声入力はIntent により呼び出し可能な独立したアプリとして実装されており
これを呼び出すことで音声を認識し
認識した結果を取得できる

Android Activity は
必要に応じて呼び出 し元Activity に戻り値を返却することができる

Intent により、他のActivity を呼び出し
その戻り値を取得するには
Activity のstartActivityForResult()を使う

とくに戻り値を要求しないなら
Activity の startActivity() のほうを使えばいい

startActivityForResult()は
intent
これはActivity を呼び出すためのIntent

requestCode
これは、リクエストID
という引数になっている

音声処理を始めるのは
追加ボタンを押したときなので
case R.id.add:
listAdapter.add(“追加”);
のところへ追加する

とりあえず、解説を先にメモ

アプリから別のActivity つまり画面を呼び出す
この処理は
同一のアプリでも、別のアプリでもやり方は一緒

構文は

Intent intent = new Intent();

try{
  startActivityForResult(intent , REQUEST_ID);
}
catch(ActivityNotFoundException e){
  //指定したActivity が存在しないときに起こる例外処理
e.prinstackTrace();
}

となる

REQUEST_ID は
Intent
呼び出しを識別するたのID

戻り値を取得するために使うので
あらかじめ定数としておくのが一般的

Intent が暗黙的Intent の場合
Intent が示すActivity を system が発見できないため
ActivityNorFoundException が発生する

呼び出し元Activity が確実に存在する場合
つまり同じアプリの中のActivity 呼び出しとかなら
いらないけど
そんな都合のいいことばかりじゃないので
ActvityNorFooundException が発生する前提でアプリを作成すること

次にActivity の戻り値の取得について
startActivityForResult() でActivity を呼び出した場合
呼び出した Activity が閉じると
呼び出し元、つまり今のActivity の
onActivityResult() が呼び出される

ソースにすると

@Override
public void onActivityResult(int requestCode, int resultCode, intent data){
if(requestCode == REQUEST_ID &&  resultCode == RESULT_OK){
.....
}
super.onActivityResult(requestCode,resultCode,data);
}

となる

この引数の意味は
requestCode
onActivityForResult() で指定した値が格納される

resultCode
Activity が閉じたときの終了コード
Activity クラスの定数である
RESULT_OK
RESULT_CANCEL
RESULT_FIRST_USER
のどれかが格納される
これらの意味は
RESULT_OK
処理が正常終了

RESULT_CANCEL
処理がキャンセルされた

RESULT_FIRST_USER
その他の動作

という意味になる

data
これは、Activity が設定した戻り値を示すIntent オブジェクトを格納

となる

このため

if(requestCode == REQUEST_ID &&  resultCode == RESULT_OK){


requestCode が目的のIDで
resultCode が RESULT_OK
のときだけ処理するという判定になる

ちなみに、自作した Activity の戻り値をかえすようにするには
Activity Object の setResult() を使う

構文は

public final void setResult(int resultCode Intent data);

となる

この時の引数の意味は
resultCode
Activity の処理結果
onActivityResult() のresultcode 対応

data
Activity の処理結果を示すIntent
onActivityResult のdata 対応

Android Beamの送信方法

Android Beamの送信方法

Android Beam でデータ送信するには

NfcAdapterクラスのメソッドを使う

setNdefPushMessage()を使うと
呼び出した時点でビーム送信になる

これは、Activity起動時とか
ボタンのクリックイベントなど特定のタイミングで送信する時に使う

ソースにすると
NfcAdapter.setNdefPushMessage(NdefMessage message, Activity activity, Activity activitys …)

もう一つの送信方法として
setNdefPushMessageCallback()
を使う
これは、端末同士を近づけて
ビームが送受信できる状態になったときにコールバックが受け取れるようにする

コールバックを受け取ると
返信データとなるNDEF Messageを作成する
createNdefMessage()
が自動的に呼ばれる

createNdefMessage()は
Overrideすることで送信に使う
NDEF Messageを作成できる

ソースにすると
NfcAdapter.setNdefPushMessageCallback(NdefAdapter.createNdefMessageCallback callback, Activity activity, Activity activitys …)

となる

NDEFフォーマットでない場合

NDEFフォーマットでない場合

この場合、
NdefFormatableクラスを使う
get()
connect()
close()
の使い方は
NDEFフォーマットのときと同じだけど
タグへの書き込み方が違う

NdefFormatableへ書き込むときには
format()を使って
NDEFへフォーマットしながら
NDEF Messageを書き込むことになる

タグの書き込み

タグの書き込み

NDEFフォーマットで書き込む場合、
はじめにNdefクラスのメソッドを使って
タグ情報から既存のNDEF情報をもったインスタンスを取得する

そして、取得したインスタンスへ
新たに作成したNDEF Messageを追加する形で書き込む

タグへ書き込むときには
connect()
で書き込み操作開始

writeNdefMessage()でタグへ書き込む

終わったら、close()で書き込み終了

このあたりは、他の言語の
ファイルオープン
書き込み
ファイルを閉じる
という流れと変わらない

ソースにすると
//Ndefが含まれている場合なら
Ndef ndef = Ndef.get(tag);

try{
//Ndefデータの上にNDEF Messageを書き込む
ndef.connect();
ndef.writeNdefMessage(tag);
}catch(IOException e){
throw new RuntimeException(getString(R.string.err_connect,e));
}catch(FormatException e){
throw new RuntimeException((R.string.err_format,e());
}finally{
try{
ndef.close();
}catch(IOException e){
}
}

tryには行う処理
catchには例外発生の処理
finallyには絶対行う処理を書く

構文については
http://www.javaroad.jp/java_exception2.htm
を参考に