【Google home】API.AIのAndroid SDKのサンプルを動かしてみる/API.AIのGithubをざっくり日本語訳

# API.AIのAndroid用SDKのサンプルを動かしてみる

# 初めに
今回の記事は、API.AIサービス上で作成したエージェントをAndroidから呼び出しが可能になるサンプルのgitのread.meの翻訳です。
API.AIで作成したエージェントをAndroidのSDKのHTTPリクエストを使って呼び出しているイメージです。

# GitHubより翻訳しながら動作を確認
※参考のリンク[API.AI github](https://github.com/api-ai/apiai-android-client)
verは8212c36  on 9 Mar

# Android SDK for api.ai
API.AI Android SDKはAPI.AIの言語プロセスを使用することで、Android機器上で容易にインテグレートできる。
API.AIは特定のエージェントの音声認識コマンドやインテグレーションが可能。

以下2つのパーミッションが必要

* **android.permission.INTERNET** for internet access
* **android.permission.RECORD_AUDIO** for microphone access

以下のdependenciesを追加

```
compile 'ai.api:libai:1.4.8'
compile 'ai.api:sdk:2.0.5@aar'
// api.ai SDK dependencies
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.google.code.gson:gson:2.3.1'
compile 'commons-io:commons-io:2.4'
```

現在、音声認識はGoogleのAndroid SDKによりクライアント機器またはクラウド上で使用可能。
HTTPのリクエストを通してAPI.AIに認識されたテキストが渡される。また、Speaktoit recognition engine も使用可能([AIConfiguration.RecognitionEngine.Speaktoit](https://github.com/api-ai/api-ai-android-sdk/blob/master/ailib/src/main/java/ai/api/AIConfiguration.java#L37)).

認証は[AIConfiguration](https://github.com/api-ai/api-ai-android-sdk/blob/master/ailib/src/main/java/ai/api/android/AIConfiguration.java) objectの初期化のタイミングで行われる。
それにより、どのエージェントが使われるかを判定する。

**Note:** API.AI Android SDKはqueryリクエストのみ作成できる。そして、entityやintentを使用する際には使用できない。
しかし、その代わりにAPI.AI user interfaceまたはREST APIをentityやintentの作成、取得、更新、削除の際に使用できる。

* [Sample Code実行](#running_sample)
* [自作Appで開始する](#getting_started)
* [Tutorial](#tutorial)
* [Brief Integration Instruction (for experienced developers)](docs/integration.md)
* [Feature examples](#feature-examples)
    * [User specified contexts](#user-specified-contexts)
    * [User specified entities](#user-specified-entities)
    * [Bluetooth support](#bluetooth-support)
* [Troubleshooting](#troubleshooting)

# <a name="running_sample" />Sample Code実行

API.AI Android SDKは簡単なサンプルで、どのようにしてAPI.AI.を使い開発できるかを説明している。
以下のステップでsample codeを実行できる:

1. API.AIのエージェントを作成する(entityとintentを持つものを作ること). 参考：[API.AI documentation](http://api.ai/docs/getting-started/5-min-guide/)
　※エージェントの作成方法は別途記載するかも
2. 起動： [Android Studio](https://developer.android.com/sdk/installing/studio.html).
3. **api-ai-android-master** のディレクトリをインポート
　[GitHub](https://github.com/api-ai/apiai-android-client)からダウンロードまたはクローン
4. SDKマネージャの起動(Android Build Tools 19.1.がインストールされていること)
5. プロジェクト上で**apiAISampleApp/src/main/java/ai.api.sample/Config**を開く
6. ファイルの一番上に定数(static final string)で定義された*ACCESS_TOKEN*がある. そこに、1で作成したエージェントのトークンをセットする　API.AI-設定っぽいマーク(歯車)-General-Client access token にあるよ！
7. Android機器を接続(PCだったら、AndroidにUSBをさしてデバッグ許可する)もしくはAndroid Emulatorの起動
8. **Run**メニュー(起動？), **Debug** (もしくはDebugマークをクリック). 接続機器を選択。
9. アプリの起動後に左記のようなボタンが現れる(サービスサンプル service sample内): **Listen**, **StopListen**, and **Cancel**.
10. **Listen** をクリックしエージェント認識のフレーズを機器に話しかける。数秒後にAPI.AI serviceが応答を返す。.

# <a name="getting_started" />自作Appで開始する

このセクションはAPI.AI Android SDKを使用するには何が必要かを説明している。1つ目にはSDKをどう使用するのかの全体像、2つ目にアプリ作成を段階的に説明しているチュートリアルがある。

もし、開発経験があれば先を使用することも可能 [簡易インストラクション](docs/integration.md).

## 全体像

音声認識や自然言語処理の特色を実装するためには、API.AI SDKのライブラリを追加する必要がある。追加の方法は2通り
最初のやつがお勧め:

1. *build.gradle*にdependencyを追加. 下の行を**build.gradle**に追加. (サンプルappでは, **apiAISampleApp/build.gradle**がサンプルとなっている)

    ```
    compile 'ai.api:libai:1.4.8'
    compile 'ai.api:sdk:2.0.5@aar'
    // api.ai SDK dependencies
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.google.code.gson:gson:2.3'
    compile 'commons-io:commons-io:2.4'
    ```
    
2. (おすすめじゃないやつ)githubからライブラリをダウンロードしてプロジェクトに追加

これで、自作またはインテグレートした音声認識をAPI.AI serviceを通してapp上で使用できる。

## インテグレートした音声認識の使用

SDKのライブラリを追加したら、以下を実行:

1. AndroidManifestにパーミッションを追加:
    * **android.permission.INTERNET**
    * **android.permission.RECORD_AUDIO**
    
2. AIListenerの実装クラスを実装する. このクラスがAPI.AI.のレスポンスを処理する

    ```java
    public interface AIListener {
        void onResult(AIResponse result); // here process response
        void onError(AIError error); // here process error
        void onAudioLevel(float level); // callback for sound level visualization
        void onListeningStarted(); // indicate start listening here
        void onListeningCanceled(); // indicate stop listening here
        void onListeningFinished(); // indicate stop listening here
    }
    ```

3. AIConfigurationのインスタンスを作成, アクセストークン, locale, recognition engineを特定

    ```java
    final AIConfiguration config = new AIConfiguration("CLIENT_ACCESS_TOKEN",
                AIConfiguration.SupportedLanguages.English,
                AIConfiguration.RecognitionEngine.System);
    ```

4. query requestを作成するAIServiceを参照するためにAIConfiguration使用

    ```java
    AIService aiService = AIService.getService(context, config);
    ```

5. AIService インスタンスにAIListenerインスタンスを設定

    ```java
    aiService.setListener(yourAiListenerInstance);
    ```

6. **startListening**メソッドを使用してマイクからのリスニングを起動。SDKがモバイルのマイクからの読み込みを開始する。

    ```java
    aiService.startListening();
    ```

7. リスニングを停止し結果リクエストをAPI.AI送信するためにAIServiceクラスの**stopListening**メソッドを呼び出します。

    ```java
    aiService.stopListening();
    ```

8. API.AIサービスにリクエストを送らずに停止をする場合をAIServiceクラスの**cancel**メソッドを使用します。

    ```java
    aiService.cancel();
    ```

9. エラーがない場合、**AIResponse.getResult**を使用し結果を取得できます。そして、そこからアクションとパラーメータも取得可能です。

    ```java
    public void onResult(final AIResponse response) {
        Log.i(TAG, "Action: " + result.getAction());
        // process response object
    }
    ```

## 独自の音声認識を使用する

このセクションは自作の音声認識とその自然言語処理が適切かの確認を行うことができる。SDKライブラリを追加したのちに以下のステップを実行する：

1. AndroidManifestにパーミッションを追加:
    * **android.permission.INTERNET**
    
2. **AIConfiguration**のインスタンスを作成し、アクセストークン・ロケール・認識エンジンを特定する。使用されていない認識エンジンを特定することができる。
3. Objectのコンフィグで使用する**AIDataService**インスタンスを作成
4. 空の**AIRequest**インスタンスを作成。リクエストテキストを**setQuery**メソッドを使用して設定。
5. **aiDataService.request(aiRequest)**を使用しAPI.AIにリクエストを送信。
6. レスポンスの処理を行う。

下記のコードはテキスト「Hello」を送信している。
初めに、`aiDataService`と`aiRequest`のインスタンスを初期化している

```java
final AIConfiguration config = new AIConfiguration(ACCESS_TOKEN, 
    AIConfiguration.SupportedLanguages.English, 
    AIConfiguration.RecognitionEngine.System);

final AIDataService aiDataService = new AIDataService(config);

final AIRequest aiRequest = new AIRequest();
aiRequest.setQuery("Hello");
```

そして、`aiDataService.request`メソッドを読んでいる。注意してもらいたいのは、バックグラウンドのスレッドから`AsyncTask`クラスなどを使用して`aiDataService.request`メソッドを呼び出さなければいけない。
例えば：

```java
new AsyncTask<AIRequest, Void, AIResponse>() {
    @Override
    protected AIResponse doInBackground(AIRequest... requests) {
        final AIRequest request = requests[0];
        try {
            final AIResponse response = aiDataService.request(aiRequest);
            return response;
        } catch (AIServiceException e) {
        }
        return null;
    }
    @Override
    protected void onPostExecute(AIResponse aiResponse) {
        if (aiResponse != null) {
            // process aiResponse here
        }
    }
}.execute(aiRequest);
```

## 結果の取得
AIListenerインターフェースの実装後、リスナー内部のapi.aiからレスポンスを取得できる。
例えば:

```java
public void onResult(final AIResponse response) {
   // Use the response object to get all the results
}
```

下記がどのように違う個所の結果オブジェクトを取得するかの説明である。

* ステータスの取得

   ```java
   final Status status = response.getStatus();
   Log.i(TAG, "Status code: " + status.getCode());
   Log.i(TAG, "Status type: " + status.getErrorType());
   ```
   
* resolved queryの取得

   ```java
   final Result result = response.getResult();
   Log.i(TAG, "Resolved query: " + result.getResolvedQuery());
   ```
   
* actionの取得

   ```java
   final Result result = response.getResult();
   Log.i(TAG, "Action: " + result.getAction());
   ```
   
* speechの取得

   ```java
   final Result result = response.getResult();
   final String speech = result.getFulfillment().getSpeech();
   Log.i(TAG, "Speech: " + speech);
   ```
   
* metadataの取得

   ```java
   final Result result = response.getResult();
   final Metadata metadata = result.getMetadata();
   if (metadata != null) {
     Log.i(TAG, "Intent id: " + metadata.getIntentId());
     Log.i(TAG, "Intent name: " + metadata.getIntentName());
   }
   ```

* parametersの取得

   ```java
   final Result result = response.getResult();
   final HashMap<String, JsonElement> params = result.getParameters();
   if (params != null && !params.isEmpty()) {
     Log.i(TAG, "Parameters: ");
     for (final Map.Entry<String, JsonElement> entry : params.entrySet()) {
         Log.i(TAG, String.format("%s: %s", entry.getKey(), entry.getValue().toString()));
     }
   }
   ```

# <a name="tutorial" />Tutorial

このセクションは自作の新規アプリケーションをAPI.AIに接続する詳細なチュートリアルである。

## Appの作成

下記の手順に従い、環境構築を行い、API.AIのインテグレーションを使用してアプリを作成する:

1. API.AIエージェントを使用してentityやintentを作成する。もしくは、既に作成しているものを使用する。API.AIのドキュメントを読み進めてください。
2. [Android Studio](https://developer.android.com/sdk/installing/studio.html)を開く. (もし持っていなければダウンロードしてね)
3. スタート画面(もしくは**File**メニュー)から , **New Project...**を選択.<br/> ![New Project](docs/images/NewProject.png)
4. 新規プロジェクトダイアログの中の**Application name**と**Company Domain**を記載, **Next**をクリック.<br/> ![New project dialog](docs/images/NewProjectDialog.png)
5. プロジェクトの最低限のSDKを選択, 最低限のAPI.AIのSDKとは**9 Gingerbread**です。**Next**をクリック.<br/> ![Min SDK](docs/images/MinSDK.png)
6. **Blank Activity**を選び**Next**をクリック.
7. main activity名を入力し**Finish**押下.

## SDKを使用しインテグレート

次は呼び出し可能にするためにSDKを使用しインテグレートする。 以下を参考:

1. **AndroidManifest.xml**(**app/src/main**配下)を開く. 
2. `<application>`タグの上に下記を追加し、インターネットアクセスとモバイルのマイクにアクセス可能にする:

    ```xml
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    ```
    
3. **AndroidManifest.xml**保存.
4. 次にAI.APIのライブラリ用のDependencyを追加する。プロジェクトナビゲータのモジュール名上で右クリック(_app_内にあるはず)、そして**Open Module Settings**を選択. **Dependencies**タブをクリック. ボタン左の**+**マークを選択し**Library dependency**をクリック. <br/>![Add dependency](docs/images/Dependencies.png)
5. ダイアログ検索内で**ai.api**を検索, **ai.api:sdk:2.0.5**末尾に`@aar`を付けたものを入力しOKをクリック。(ここらへんは本家に画像がついてるから見て)<br/> ![Add dependency](docs/images/Dependencies2.png)
    * SDKのライブラリも追加が必要 : *com.android.support:appcompat-v7*, *com.google.code.gson:gson*, *commons-io:commons-io*. (同じ方法で追加してね).
6. **MainActivity.java**(**app/src/main/java/com.example.yourAppName.app**内)を開く, もしくは何でもいいので自分のパッケージを開く。
7. 下記API.AIクラスをimportして機能拡張:
    
    ```java
    import ai.api.AIListener;
    import ai.api.android.AIConfiguration;
    import ai.api.android.AIService;
    import ai.api.model.AIError;
    import ai.api.model.AIResponse;
    import ai.api.model.Result;
    import com.google.gson.JsonElement;
    import java.util.Map;
    ```
    
## ユーザインタフェースの作成
1. **activity_main.xml**(**app/src/main/res/layout**配下)を開く. designer内のlayout を開く。<br/>![activity_main.xml in Designer](docs/images/LayoutDesigner.png)
2. "Hello World"のTextViewを削除.
3. Widget下のボタンを画面上部にドラッグ。 プロパティ**id**を"listenButton"に変更し、その**text**を"Listen"に変更<br/>![Listen button](docs/images/ListenButton.png)
4. Widget下のTextViewをそのボタンの下にドラッグ。開くと、残りスクリーンをそのテキストビューが埋める。プロパティ**id**を"resultTextView"に変更し、プロパティ**text**を空にする<br/>![Result TextView](docs/images/ResultViewText.png)
6. MainActivity.javaのファイルを開き(さっき開いてるかな？). 下記をimportし、作成したステートメントにアクセスするようにする:
    
    ```java
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    ```
    
7. MainActivity に下記のprivateメンバーを追加する(widget操作用):
   
    ```java
    private Button listenButton;
    private TextView resultTextView;
    ```
    
8. OnCreateの最後尾あたりに下記のwidget初期化分を追加:
    
    ```java
    listenButton = (Button) findViewById(R.id.listenButton);
    resultTextView = (TextView) findViewById(R.id.resultTextView);
    ```
    
## AIサービスとリスナーの作成

1. イベント起動の際に呼び出されるMainActivityクラス(AIListenerクラスをimplementしたやつ)を使う。クラス宣言を下記に置き換える:
    
    ```
    public class MainActivity extends ActionBarActivity implements AIListener {
    ```
    
2. MainActivity クラス内でprivateメンバーとして`aiService`(**AIService**クラス)を生成。
    
    ```java
    private AIService aiService;
    ```
    
3. 音声認識システム使用のコンフィグを設定するためにOnCreateメソッド内に下記を追加。CLIENT_ACCESS_TOKENを自分のアクセストークンに追加(API.AIのサービスの設定みたいなとこからクライアントのアクセストークンをとってきてね) 
    
    ```java
     final AIConfiguration config = new AIConfiguration("CLIENT_ACCESS_TOKEN",
            AIConfiguration.SupportedLanguages.English,
            AIConfiguration.RecognitionEngine.System);
    ```
    
    ![Api keys](docs/images/apiKeys.png)
    
4. 下記でAIのサービスを初期化し、リスナーとしてインスタンスをハンドルイベントに追加
    
    ```java
    aiService = AIService.getService(this, config);
    aiService.setListener(this);
    ```
    
5. ボタンクリックでリスニング開始のメソッドを追加:

    ```java
    public void listenButtonOnClick(final View view) {
        aiService.startListening();
    }
    ```
    
6. activity_main.xmlに移り、Listenボタンをクリック。プロパティぺーん内にonClickのプロパティとしてlistenButtonOnClickを設定する。リスニングが終わった時の結果を表示するために下記を追加する:
    
    ```java
    public void onResult(final AIResponse response) {
        Result result = response.getResult();

        // Get parameters
        String parameterString = "";
        if (result.getParameters() != null && !result.getParameters().isEmpty()) {
            for (final Map.Entry<String, JsonElement> entry : result.getParameters().entrySet()) {
                parameterString += "(" + entry.getKey() + ", " + entry.getValue() + ") ";
            }
        }

        // Show results in TextView.
        resultTextView.setText("Query:" + result.getResolvedQuery() +
            "\nAction: " + result.getAction() +
            "\nParameters: " + parameterString);
    }
    ```
    
8. エラーハンドルように追加:
    
    ```java
    @Override
    public void onError(final AIError error) {
        resultTextView.setText(error.toString());
    }
    ```
    
9. AIListenerインターフェースを実装するために下記の空メソッドを追加:

    ```java
    @Override
    public void onListeningStarted() {}

    @Override
    public void onListeningCanceled() {}

    @Override
    public void onListeningFinished() {}
    
    @Override
    public void onAudioLevel(final float level) {}
    ```
    
## App起動
1. Android機器をPCに接続または仮想機を起動
2. ドロップダウンにモジュールが選択されていることを確認し、デバッグボタンをクリック<br/>![Debug button](docs/images/DebugButton.png)
3. 機器または仮想機で起動しているはず。。。**Listen**ボタンをクリックしintentを起動するフレーズを言う。数秒後、TextViewに結果が表示される <br/>![Result](docs/images/AppScreenshot.png)

# 例

## ユーザ特定のcontext

`RequestExtras`オブジェクトを使ったクエリ内のユーザの[contexts](http://api.ai/docs/getting-started/key-concepts.html#contexts)を特定するには

まず、必要なcontextの一覧を作成:

```java
List<AIContext> contexts = new ArrayList<>();
contexts.add(new AIContext("firstContext"));
contexts.add(new AIContext("secondContext"));
```

そして`RequestExtras`インスタンスを作成し、リクエストで使用

```java
RequestExtras requestExtras = new RequestExtras(contexts, null);
aiService.startListening(requestExtras);
```

## ユーザの特定のentityの場合

`RequestExtras`オブジェクトを使ったクエリ内のentityの特定を行うために

必要なentityの一覧を作成:

```java
final Entity myDwarfs = new Entity("dwarfs");
myDwarfs.addEntry(new EntityEntry("Ori", new String[] {"Ori", "Nori"}));
myDwarfs.addEntry(new EntityEntry("Bifur", new String[] {"Bofur","Bombur"}));
final List<Entity> entities = Collections.singletonList(myDwarfs);
```

リクエストとして`RequestExtras`インスタンスを使うためにインスタンスを作成

```java
RequestExtras requestExtras = new RequestExtras(null, entities);
aiService.startListening(requestExtras);
```

メソッドを使用してアップロードできる。

```java
aiService.uploadUserEntities(entities);
```
 

## Bluetooth support

Bluetooth機器を使用しSDKのワークをするために下記の手順を行う:

1. アプリケーションクラス付近で[BluetoothController](https://github.com/api-ai/api-ai-android-sdk/blob/master/ailib/src/main/java/ai/api/util/BluetoothController.java)の実装作成を行う

    ```java
    private class BluetoothControllerImpl extends BluetoothController {

        public BluetoothControllerImpl(Context context) {
            super(context);
        }

        @Override
        public void onHeadsetDisconnected() {
            Log.d(TAG, "Bluetooth headset disconnected");
        }

        @Override
        public void onHeadsetConnected() {
            Log.d(TAG, "Bluetooth headset connected");

            if (isInForeground() && !bluetoothController.isOnHeadsetSco()) {
                bluetoothController.start();
            }
        }

        @Override
        public void onScoAudioDisconnected() {
            Log.d(TAG, "Bluetooth sco audio finished");
            bluetoothController.stop();

            if (isInForeground()) {
                bluetoothController.start();
            }
        }

        @Override
        public void onScoAudioConnected() {
            Log.d(TAG, "Bluetooth sco audio started");
        }

    }
    ```
2. `Application`クラスにActivityを数えるintとBlueTooth管理のための`BluetoothController`をフィールドとして追加

    ```java
    private int activitiesCount;
    private BluetoothControllerImpl bluetoothController;
    ```

3. helperメソッドを`Application`に追加

    ```java
    protected void onActivityResume() {
        if (activitiesCount++ == 0) { // on become foreground
            bluetoothController.start();
        }
    }

    protected void onActivityPaused() {
        if (--activitiesCount == 0) { // on become background
            bluetoothController.stop();
        }
    }

    private boolean isInForeground() {
        return activitiesCount > 0;
    }
    ```

4. 全てのActivityの`onPause`と`onResume`からこのメソッドを呼び出す必要がある。そして、すべてのactivityの基底クラスとできる

    ```java
    public class BaseActivity extends ActionBarActivity {

        private AIApplication app;

        private static final long PAUSE_CALLBACK_DELAY = 500;

        private final Handler handler = new Handler();
        private Runnable pauseCallback = new Runnable() {
            @Override
            public void run() {
                app.onActivityPaused();
            }
        };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            app = (AIApplication) getApplication();
        }

        @Override
        protected void onResume() {
            super.onResume();
            app.onActivityResume();
        }

        @Override
        protected void onPause() {
            super.onPause();
            handler.postDelayed(pauseCallback, PAUSE_CALLBACK_DELAY);
        }
    }
    ```

[Sample Application](https://github.com/api-ai/api-ai-android-sdk/tree/master/apiAISampleApp/src/main/java/ai/api/sample)にソースがある.

# <a name="troubleshooting" />Troubleshooting

* インストール中に"INSTALL_FAILED_OLDER_SDK"のエラーが出た場合は、Android SDK 19があるか、ビルドツールの19.1がインストールされているか確認してね