[日本語Alexa] APLにおけるTouchWrapperコンポーネントによるイベント処理 〜ユーザーの画面タッチ 操作をハンドリングする〜

1 はじめに

CX事業本部の平内(SIN)です。

Amazon AlexaのAPLを構成するTouchWrapperコンポーネントは、1つの子コンポーネントをラップして、タッチイベントを取得します。Alexaで音声以外のイベントを処理するためには、TouchWrapperコンポーネントが必須です。

今回は、TouchWrapperコンポーネントの使い方について、確認してみたいと思います。

2  単一コンポーネント

下記の例は、30vw*20vwのボタンを画面の中央に配置したものです。ちょっと分かりやすいように、TouchWrapperコンポーネントをFrameコンポーネントの中に配置しています。タッチされた際に、SendEventが 発生します。

{
    "type": "APL",
    "version": "1.1",
    "settings": {},
    "theme": "dark",
    "import": [],
    "resources": [],
    "styles": {},
    "onMount": [],
    "graphics": {},
    "commands": {},
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                "type": "Container",
                "alignItems": "center",
                "justifyContent": "center",
                "item": [
                    {
                        "type": "Frame",
                        "item": {
                            "type": "TouchWrapper",
                            "onPress": [
                                {
                                    "type": "SendEvent",
                                    "arguments": [
                                        "button"
                                    ]
                                }
                            ],
                            "items": [
                                {
                                    "type": "Text",
                                    "width": "30vw",
                                    "height": "20vh",
                                    "textAlign": "center",
                                    "textAlignVertical": "center",
                                    "text": "Button"
                                }
                            ]
                        },
                        "backgroundColor": "#006600",
                        "borderColor": "#00ff00",
                        "borderWidth": "4"
                    }
                ]
            }
        ]
    }
}

3 イベント処理

先のドキュメントが表示された画面で、TouchWrapperコンポーネントをタップすると、Alexa.Presentation.APL.UserEventリクエストがスキルに送られます。

UserEventリクエストのプロパティは以下のとおりです。

  • type リクエストタイプ Alexa.Presentation.APL.UserEvent
  • token トークン
  • arguments SendEventから送られた値
  • source 発生元コンポーネント
  • components コンポーネントの情報

source.typeで、TouchWrapperコンポーネントからのイベントであることが分かり、argumentsの値を見ることで、対象コンポーネントを判定することができます。

"request": {
    "type": "Alexa.Presentation.APL.UserEvent",
    "requestId": "amzn1.echo-api.request.xxxxxxxxx",
    "timestamp": "2019-07-19T06:49:48Z",
    "locale": "ja-JP",
    "arguments": [
        "button"
    ],
    "components": {},
    "source": {
        "type": "TouchWrapper",
        "handler": "Press",
        "id": "",
        "value": false
    },
    "token": "xxxxxx"
}

スキル側でこのイベントを処理するとしたら、下記のようなハンドラになるでしょう。

const TouchEventHandler = {
    canHandle(h: Alexa.HandlerInput) {
        const request = h.requestEnvelope.request;
        return ((request.type === 'Alexa.Presentation.APL.UserEvent' &&
        (request.source.handler === 'Press' || request.source.handler === 'onPress')));
    },
    handle(h: Alexa.HandlerInput) {
        const request = h.requestEnvelope.request;
        if (request.type === 'Alexa.Presentation.APL.UserEvent') {
            if (request.arguments) {
                // request.arguments[0]には、"button"が入っている
                const speechText = `${request.arguments[0]} がタップされました。`
                return h.responseBuilder
                    .speak(speechText)
                    .withShouldEndSession(true)
                    .getResponse();            
            }
        }
        throw new Error("error");
    }
};


参考:APL UserEventスキルのリクエスト

3 複数コンポーネント

Containerコンポーネント若しくは、Sequenceコンポネントの子としてTouchWrapperコンポーネントを使用すると、結果的に列挙されるデータ数分のコンポーネントのイベントを取得することができます。

下記の例は、30vw*20vwのボタンをContainerコンポーネントの子として配置したものです。ちょっと分かりやすいように、TouchWrapperコンポーネントをFrameコンポーネントの中に配置しています。

タッチされたのが、何番目のコンポーネントかを取得するためには、SendEventarguments"${data.text}" のような変数を指定する必要があります。

const mainDocument = {
    "type": "APL",
    "version": "1.1",
    "settings": {},
    "theme": "dark",
    "import": [],
    "resources": [],
    "styles": {},
    "onMount": [],
    "graphics": {},
    "commands": {},
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                    "type": "Container",
                    "height": "100vh",
                    "data": [
                        {
                            "text": "Button001"
                        },
                        {
                            "text": "Button002"
                        },
                        {
                            "text": "Button003"
                        },
                        {
                            "text": "Button004"
                        },
                        {
                            "text": "Button005"
                        },
                        {
                            "text": "Button006"
                        },
                        {
                            "text": "Button007"
                        }
                    ],
                    "alignItems": "center",
                    "items": [
                        {
                            "type": "Frame",
                            "backgroundColor": "#006600",
                            "borderColor": "#00ff00",
                            "borderWidth": "4",
                            "item": {
                                "type": "TouchWrapper",
                                "onPress": [
                                    {
                                        "type": "SendEvent",
                                        "arguments": [
                                            "${data.text}"
                                        ]
                                    }
                                ],
                                "item": {
                                    "type": "Text",
                                    "width": "30vw",
                                    "height": "20vh",
                                    "textAlign": "center",
                                    "textAlignVertical": "center",
                                    "text": "${data.text}"
                                }
                            }
                        }
                    ]
                }
            ]
    }
}

5番目のボタンをタップした時にスキルに送られるクリエスとは、次のとおりです。

arguments"${data.text}" の内容が入っていることが分かります。ちなみに、source.idで、何番目のコンポーネントかを取得することはできません。

"request": {
    "type": "Alexa.Presentation.APL.UserEvent",
    "requestId": "amzn1.echo-api.request.xxxxxxxxx",
    "timestamp": "2019-07-24T06:14:46Z",
    "locale": "ja-JP",
    "arguments": [
        "Button005"
    ],
    "components": {},
    "source": {
        "type": "TouchWrapper",
        "handler": "Press",
        "id": ""
    },
    "token": "token"
}

4 最後に

今回は、TouchWrapperコンポーネントについて確認してみました。

Sequenceコンポーネントや、Containerコンポーネントの中で利用することで、リストの選択が簡単に実現できます。

ちなみに、TouchWrapperコンポーネントの中に、Containerコンポーネント等を配置することも可能ですが、argumentsで区別できないため、リスト等の選択には利用できません。

コメントは受け付けていません。