AIR for Androidでネイティブ拡張を試しました その3

2011.12.12

AIR for Androidでネイティブ拡張を試しました その2 の続きです。

今回はネイティブの音声認識を利用したサンプルを作成します。 ソースは前回作ったものを使います。

まずはネイティブ側の実装です。 その1で作ったSampleFREExtensionを以下のように修正します 前回と違う点はファンクションの名前とstatic変数にFREContextを格納しているところです。

package sample.ane;

import java.util.HashMap;
import java.util.Map;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREExtension;
import com.adobe.fre.FREFunction;

public class SampleFREExtension implements FREExtension {

	public static FREContext context;
	
	@Override
	public FREContext createContext(String arg0) {
		
		if(context == null) {
			context = new FREContext() {
				
				@Override
				public Map<String, FREFunction> getFunctions() {
			        Map<String, FREFunction> result = new HashMap<String, FREFunction>();
			        result.put("recognizeSpeech", new MyFREFunction());
			        return result;
				}

				@Override
				public void dispose() {}
			};
		}
		
		return context;
	}
	
	@Override
	public void dispose() {}

	@Override
	public void initialize() {}
}

 

次にMyFREFunctionのcallメソッド内を以下のように修正します。 処理としてはSampleActivityというアクティビティを呼び出しているだけになります。

package sample.ane;

import android.app.Activity;
import android.content.Intent;

import com.adobe.fre.FREContext;
import com.adobe.fre.FREFunction;
import com.adobe.fre.FREObject;
import com.adobe.fre.FREWrongThreadException;

public class MyFREFunction implements FREFunction {
	
	@Override
	public FREObject call(FREContext arg0, FREObject[] arg1) {
		try {
		    Activity activity = arg0.getActivity();
			Intent intent = new Intent(activity, SampleActivity.class);
			activity.startActivity(intent);
            return null;
             
        } catch (Exception e) {
            try {
                return FREObject.newObject(e.toString());
            } catch (FREWrongThreadException e1) {}
        }
         
        return null;
	}
}

 

次にcallメソッド内で呼び出しているSampleActivityを実装します。 onCreate内で暗黙的なIntentで音声認識の画面に遷移します。 結果をonActivityResult内で取得して検索結果を格納したStatusEventをFREContextにディスパッチします。 イベントをディスパッチしたらアクティビティを終了します。

実装が完了したら前回と同じようにjarファイルを作成して下さい。

package sample.ane;

import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognizerIntent;

public class SampleActivity extends Activity {
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
            "SampleActivity");
        
        startActivityForResult(intent, 0);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    	
        if (requestCode == 0 && resultCode == RESULT_OK) {
        	List<String> results = data.getStringArrayListExtra(
                RecognizerIntent.EXTRA_RESULTS);
        	SampleFREExtension.context.dispatchStatusEventAsync(results.get(0), "result");
        }
        super.onActivityResult(requestCode, resultCode, data);
        finish();
    }
}

 

次にネイティブ拡張を修正します。 前回とは違いEventDispatcherをスーパークラスにしています。これはネイティブ側から受け取ったStatusEventをディスパッチするためです。 recognizeSpeechファンクションの戻り値はエラーが発生した場合のログ取得用で音声検索の結果はStatusEventから取得します。

実装できたら前回と同じ方法でANEファイルを作成します。

package sample.ane
{
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	
	public class SampleExtension extends EventDispatcher
	{
		private var context:ExtensionContext;
		
		public function SampleExtension() {
			context = ExtensionContext.createExtensionContext(
				"sample", "type");
		}
		
		public function recognizeSpeech():String {
			context.addEventListener(StatusEvent.STATUS, statusHandler);
			return context.call("recognizeSpeech") as String;
		}
		
		private function statusHandler(event:StatusEvent):void {
			dispatchEvent(event);
		}
		
		public function dispose() : void {
			return context.dispose();
		}
	}
}

最後にAIRのソースコードです。 ボタンを押すと音声認識の画面に遷移します。 そこで何か音声を入力すると検索結果がTextAreaに表示されます。

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160">
	<fx:Script>
		<![CDATA[
			import sample.ane.SampleExtension;
			
			private var extension:SampleExtension;
			
			protected function button1_clickHandler(event:MouseEvent):void
			{
				extension = new SampleExtension();
				extension.addEventListener(StatusEvent.STATUS, statusHandler);
				extension.recognizeSpeech();
			}
			
			private function statusHandler(event:StatusEvent):void {
				textArea.text = event.code;
			}
		]]>
	</fx:Script>
	
	<s:VGroup width="100%" height="100%">
		<s:Button width="100%" label="Recognize Speech" click="button1_clickHandler(event)"/>
		<s:TextArea id="textArea" width="100%" height="100%"/>
	</s:VGroup>
</s:Application>

 

最後にActivityを追加した場合はネイティブアプリ同様Activityを登録する必要があります。 アプリケーション記述ファイル(*-app.xml)の下の方にパーミッションなどを設定している箇所があるので、 以下のように修正します。

<android>
	<colorDepth>16bit</colorDepth>
	<manifestAdditions><![CDATA[
		<manifest android:installLocation="auto">
		    <!--See the Adobe AIR documentation for more information about setting Google Android permissions-->
		    <!--android.permission.INTERNET 権限を削除すると、
	デバイス上でアプリケーションをデバッグできなくなります-->
		    <uses-permission android:name="android.permission.INTERNET"/>
		    <!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>-->
		    <!--<uses-permission android:name="android.permission.READ_PHONE_STATE"/>-->
		    <!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>-->
		    <!--AIR の SystemIdleMode API にアクセスするには、DISABLE_KEYGUARD 権限と
	WAKE_LOCK 権限を同時に切り替える必要があります-->
		    <!--<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>-->
		    <!--<uses-permission android:name="android.permission.WAKE_LOCK"/>-->
		    <!--<uses-permission android:name="android.permission.CAMERA"/>-->
		    <!--<uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
		    <!--AIR の NetworkInfo API を使用するには、ACCESS_NETWORK_STATE 権限と
	ACCESS_WIFI_STATE 権限を同時に切り替える必要があります-->
		    <!--<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>-->
		    <!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>-->
		    
			<application>
		        <activity>
		          <intent-filter>
                      <action android:name="android.intent.action.MAIN"/>
                      <category android:name="android.intent.category.LAUNCHER"/>
                  </intent-filter>
                </activity>
                <activity android:name="sample.ane.SampleActivity"></activity>
           </application>
		</manifest>
	]]></manifestAdditions>
</android>