Alexa APL, 第7回　タッチ対応　その１（SendEvent）

#はじめに
Alexaを搭載した画面付きデバイスの画面レイアウトを作成します。
画面の画像、もしくは商品名をタッチすると、その商品名を答えるレイアウトを作成します。
TouchWrapperコンポーネントを使って、スキルにタッチを通知（SendEvent）し、応答する方法を実施します。
そのほか、TouchWrapperを使って、スキルではなく直接Alexa？に答えさせる方法（SpeakItem）もあるようですがそれは次回以降に紹介します。

##今回実施する内容
画面の画像、もしくは商品名をタッチすると、その商品名を答えるレイアウトを作成します。

中型デバイス（Echo Show用）
![中型レイアウト1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/ba0e9c4a-1462-f3cc-2e5c-0883e8ace127.jpeg)


   - 図の通り、発話した場合、Alexaを通して「スキル」にリクエストを送信し処理することで、「音声」や「画面表示」を応答できます。
   - タッチした場合も同様に、「スキル」にリクエストを送信（SendEvent）し処理することで、「音声」や「画面表示」を応答できます。
   - タッチした場合、「スキル」にリクエストを送信しなくても、「音声」の応答をする方法（SpeakItem）を使うこともできます。どの装置が処理しているか定かではないですが、Alexaなのかな？と思っています。第6回で説明したSequenceもスクロール機能はついていますが、特にスキルにリクエストを送信したわけではないですよね。SpeakItemもそれと同じでスキルは関与しません。

#タッチ対応
##レイアウトの作成
今回作成するレイアウトの構成は、以下のようなイメージです。
![レイアウト1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/72d5a3c8-c33a-8e89-91a0-8c7f7c698b9e.jpeg)

1. APLオーサリングツールを起動して「最初から作成」を選択する。
2. 「レイアウト画面」の「mainTemplate」を選択し、「Container」を追加する。
3. 「レイアウト画面」の「Container」を選択し、「TouchWrapper」を追加する。
ここのプロパティは後ほど追加する。
4. 「レイアウト画面」の「TouchWrapper」を選択し、「Container」を追加する。
5. 「レイアウト画面」の「Container」を選択し、「詳細設定画面」で、以下を設定する。

      ・ direction：row
6. 「レイアウト画面」の｛Container」を選択し、「Image」を追加する。
7. 「レイアウト画面」の「Image」を選択し、「詳細設定画面」で、以下をそれぞれ設定する。

      ・ source：https://coffee_480x480.jpg
      ・ width：20vw
      ・ height：20vh
8. 「レイアウト画面」の｛Container」を選択し、「Text」を追加する。
9. 「レイアウト画面」の「Text」を選択し、「詳細設定画面」で、以下を設定する。

      ・ text：コーヒー
10. APL JSONの画面を開いて、「TouchWrapper」の記載を以下の通り、`onPress`に`type`と`arguments`を追加する（詳細設定画面でonPressに値を設定するのと同じことですが、入力ボックスが小さいため、ソースに書き込んだほうがやりやすいです）。

```json
...
"type": "TouchWrapper",
"onPress": {
    "type": "SendEvent",
    "arguments": "コーヒーをタッチしました。"
},
...
```
##TouchWrapperの補足
- TouchWrapperは、画面レイアウトに設定すると、その部分がタッチされたことを検出します。
`onPress`の`type`で設定したコマンドが実施されます。`SendEvent`は、スキルにタッチされたことを送信するためのコマンドです。
`arguments`は、スキル側で値を使用するための引数です。今回は、「コーヒーをタッチしました。」とAlexaに会話させるために送信します。
- TouchWrapperの範囲は、その上に置いたコンポーネントの範囲ではなく、TouchWrapperの範囲です。今回作成したAPLではタッチの範囲は下図の通り、幅は全体になります。
- タッチする範囲をImageやTextの範囲にしたい場合は、TouchWrapperのWidthとheightを調整してきっちり合わせる必要があります。
![概要図2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405480/ba30d797-341b-1ebb-f591-cf88027d5544.jpeg)

##APLソースコード
出来上がったAPLのフルソースコードとデータJSONを載せます。実際にスキルで表示を試す場合は、[Alexa ハローAPL、Alexaスキルの画面への対応](https://qiita.com/Mount/items/72d9928ff2c0ae5de737)を参考しに実施します。

```json:APL
{
    "type": "APL",
    "version": "1.1",
    "settings": {},
    "theme": "dark",
    "import": [],
    "resources": [],
    "styles": {},
    "onMount": [],
    "graphics": {},
    "commands": {},
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                "type": "Container",
                "items": [
                    {
                        "type": "TouchWrapper",
                        "onPress": {
                            "type": "SendEvent",
                            "arguments": "コーヒーをタッチしました。"
                        },
                        "items": [
                            {
                                "type": "Container",
                                "direction": "row",
                                "items": [
                                    {
                                        "type": "Image",
                                        "width": "20vw",
                                        "height": "20vh",
                                        "source": "https://coffee_480x480.jpg"
                                    },
                                    {
                                        "type": "Text",
                                        "text": "コーヒー"
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }
}
```

##スキルのソースコード作成
LaunchRequestの時の画面表示については、割愛しますので、必要な場合は、[Alexa ハローAPL、Alexaスキルの画面への対応](https://qiita.com/Mount/items/72d9928ff2c0ae5de737)をご覧ください。

ここでは、ユーザーが画面をタッチした場合のスキルでの処理のソースについて説明します。まずはソースコードを載せます。

```javascript:index.js
...
const TouchEventHandler = {
  canHandle(handlerInput) {
  return ((handlerInput.requestEnvelope.request.type === 'Alexa.Presentation.APL.UserEvent' &&
      (handlerInput.requestEnvelope.request.source.handler === 'Press' || 
      handlerInput.requestEnvelope.request.source.handler === 'onPress')));
  },
  handle(handlerInput) {
      // TcouhWrapperのargumentsで指定したパラメータを取得する
      const speechText = handlerInput.requestEnvelope.request.arguments[0];

      return handlerInput.responseBuilder
          .speak(speechText)
          .getResponse();            
  }
};

...

exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
    TouchEventHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();

```

- `TouchEventHandler`は、画面をタッチした場合の処理を記載するハンドラーとして定義しました。名前はわかりやすければ何でもよいと思います。この定数は、`.addRequestHandlers`にも追加が必要です（ソースコードの最下部参照）。
- `TouchEventHandler`の`canHandle`では、受信するイベントの種類を識別します。
      - type：Alexa.Presentation.APL.UserEvent
      - source.handler：Press、または、onPress
      
      この辺は、お決まり事なのでそのまま記載すればよいと思います。PressとonPressがありますが、デバイスによってどちらかが飛んでくるということなのかと思います。
- `handlerInput.requestEnvelope.request.arguments[0]`は、TouchWrapperのargumentsに記載した文字列です。ここで取得できます。今回はこれをspeechTextに入れて、Alexaに応答するようにしました。
- 今回は取得した`arguments`をspeechTextに代入し、`handlerInput.responseBuilder`の`speak`に代入し、応答しました。

#おわりに
今回は、以下について説明しました。
・TouchWrapperを使った画面レイアウト
・TouchWrapperからスキルへのタッチの通知
・TouchWrapperのタッチ範囲
・スキルにおけるTouchWrapperからのイベントハンドラーの取得方法
・TouchWrapperから送信した`arguments`の取得方法

次回は、おそらくSpeakItemか、TouchWrapperとSequenceについて記載する予定です。