2012/05/07

Titanium Module for Voice Recognition on Android

https://github.com/mumumu/Titanium-Android-VoiceRecognition

お久しぶりです。最近 Titanium Mobile を触っていて、Android へのかゆいところへの手の届かなさにムズムズしている mumumu です。

Android は 1.5 から 音声認識の機能にアクセスできるAPIを備えていて、Intent に情報を詰めてビルトインの Activity を起動すればいとも簡単に音声認識の結果が得られる。ならば Titanium でも簡単でしょう。と思って以下のコードを書いた。

//Add behavior for UI
label.addEventListener('click', function(e) {
var intent = Ti.Android.createIntent({
action: "android.speech.action.RECOGNIZE_SPEECH"
});
intent.putExtra("android.speech.extra.LANGUAGE_MODEL", "free_form");
intent.putExtra("android.speech.extra.PROMPT","VoiceRecognitionTest");
//intent発行&音声認識結果受け取り
Ti.Android.currentActivity.startActivityForResult(
intent,
function(e){
alert("voice recognition activity returned!");
}
);
});
view raw FirstView.js hosted with ❤ by GitHub

上記のコードでも、Android 2.x 系で Ti.Android.currentActivity.startActivityForResult の結果が返ってこないので困った。3.x 系では返ってくるので不思議不思議。どうせ Titanium は getStringArrayListExtra による Intent からの結果取得に対応していないので、モジュールはどうせ書かねばならない。

よって、Titanium の JavaScript から Activity を起動するのではなくて、 Java 側で 「音声認識 -> 結果取得 -> 結果を戻す」 というモジュールを書いた。それが上のリンクである。

これを使えば以下のようなそれっぽいコードで Android 2 系でも 3系でも結果が取得できた。


if (Ti.Platform.name == "android") {
var speechModule = require('org.mumumu.ti.android.speech');
var voiceRecognitionProxy = speechModule.createVoiceRecognition();
voiceRecognitionProxy.callback = function (e) {
var voice_recognition_enabled = e.voice_enabled;
var voice_results = e.voice_results;
if (e.voice_canceled) {
alert("voice recognition canceled");
} else {
if (!voice_recognition_enabled) {
alert("voice recognition seems to be disabled");
} else {
alert(voice_results[0]);
}
}
};
voiceRecognitionProxy.voiceRecognition();
}
view raw gistfile1.js hosted with ❤ by GitHub


実装の肝は、 Android ビルトインの Activity を起動する VoiceRecognitionProxy.java である。本当は Activity#startActivityResult で ビルトインのActivityの起動と動作結果取得を同時に行うのが筋であるが、呼び出し元の Activity が Titanium 側の初期化済みアクティビィティなので onActicityResult で結果が取得できない。なので、Titanium 側のアクティビティと呼び出し側のビルトインアクティビィティは Handler を包む Messenge と それを Intent で運ぶ Messenger を使って結ぶやり方を採っている。

正直迂回コードである。気持ち悪い。Titanium の startActivityForResult がちゃんと動いてくれればこんなモジュールはいらないのだが。。(´ー`; )