[Alexa]APLのTouchWrapperのUIデザインを向上させる工夫

# TouchWrapperを利用すれば画面タップでイベントを飛ばせる
Alexaスキル開発において、APLを利用して画面付Alexa端末に画面描写をするようなスキルが増えてきました。
画面開発ではタッチ処理の実装も可能ですが、こちらも実装しているスキルが増えているように思います。
タッチ処理は、APLテンプレート部にTouchWrapperを配置し、そのonPressプロパティ内にSendEventコマンドを埋め込むことで実現できます。

公式のリファレンスに実装例がありますので、こちらを参照してみるとわかりやすいかもしれません。
[APL標準コマンド - SendEventコマンド](https://developer.amazon.com/ja-JP/docs/alexa/alexa-presentation-language/apl-standard-commands.html#sendevent-command)

また、少し前からAPLを含んだサンプルコードも提供されているので、その辺もチェックすればより理解が深まると思います。

# TouchWrapper-SendEventの問題点
そんなTouchWrapper-SendEventコマンドの組み合わせですが、問題点があります。

## 「タッチした」感がない
まず、TouchWrapperをただ配置しただけだと、**タッチしても何の変化もせず、UIとして微妙なもの**となってしまいます。
そのため、タッチしてからAlexa端末が応答するまでわずかな時間しかかからなかったとしても、ユーザーが「これってタッチできるものなのか？」とか、「ちゃんと押せてなかったのかな」といったように感じてしまうようなUIになってしまいます。

## TouchWrapperは押した数だけコマンド実行される
次に、**TouchWrapperを連打すると押した数だけコマンド実行されてしまい、SendEventも複数実行されてしまう**ということです。
上記の問題点ともあいまって、以下のような変な挙動になってしまうこともあります。

1. ユーザーがTouchWrapperを押下する
2. 「タッチした感」がないため、Alexa端末から応答が返ってくる前に、ユーザーが「あれ、今ちゃんとタッチできてなかったかな？」となり再度押下(2回目の押下)
3. Alexa端末が最初のTouchWrapper押下の応答で喋りだす
4. Alexa端末が喋っている途中で2回目のTouchWrapper押下の応答によってまた最初から喋りだす(※Alexaの発話の途中であっても、後からきた発話内容が上書きするようになっているようです)

# 対応策
そういうわけで、TouchWrapperを取り入れる際にはUIデザインも意識した方が良いと私は考えます。
上記の問題点に対応するための方法のひとつとして、例えばonPress時に実行されるコマンドを以下のように順次実行させていくやり方があります。

## 実行していくコマンド
具体的には、以下のコマンドを順に実行していきます。

1. TouchWrapperのdisabled属性をtrueにする
2. SendEvent実行
3. AnimateItemを利用し、対象コンポーネントを少し下に動かす
4. AnimateItemを利用し、対象コンポーネントの位置を戻す(※delayをかけて一定時間は3の状態でキープさせておく)
5. TouchWrapperのdisabled属性をfalseにする

この方法では、1でdisabledをtrueにすることで、5で戻すまでの間はタッチを無効化しています。
また、3,4にて上下に動くことで、ボタンのような「押した感」を演出しています。
なお、SendEventが2の位置にあるのは、単純に少しでも早くイベントを飛ばしたいからです。
この実装だと、5になったところでタッチをすると結局もう1回イベントを飛ばせてしまうので、実際には5に達する前にAlexaからの応答が返ってくる想定です。

## JSON例
これをテンプレート部のJSONとして書いてみると、例えば以下のようになります。
payloadは使用していないので、データ部の方のJSONは適当で良いです。

``` buttonTemplate.json
{
    "type": "APL",
    "version": "1.2",
    "settings": {},
    "theme": "dark",
    "import": [],
    "resources": [],
    "styles": {},
    "onMount": [],
    "graphics": {},
    "commands": {},
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                "type": "Container",
                "alignItems": "center",
                "justifyContent": "center",
                "item": [
                    {
                        "type": "TouchWrapper",
                        "onPress": [
                            {
                                "type": "SetState",
                                "state": "disabled",
                                "value": true
                            },
                            {
                                "type": "SendEvent",
                                "arguments": [
                                    "(ここにパラメータ)"
                                ]
                            },
                            {
                                "type": "AnimateItem",
                                "duration": 100,
                                "value": [
                                    {
                                        "property": "transform",
                                        "from": [
                                            {
                                                "translateY": 0
                                            }
                                        ],
                                        "to": [
                                            {
                                                "translateY": 3
                                            }
                                        ]
                                    }
                                ]
                            },
                            {
                                "type": "AnimateItem",
                                "delay": 2000,
                                "duration": 100,
                                "value": [
                                    {
                                        "property": "transform",
                                        "from": [
                                            {
                                                "translateY": 3
                                            }
                                        ],
                                        "to": [
                                            {
                                                "translateY": 0
                                            }
                                        ]
                                    }
                                ]
                            },
                            {
                                "type": "SetState",
                                "state": "disabled",
                                "value": false
                            }
                        ],
                        "item": {
                            "type": "Frame",
                            "width": "15vw",
                            "height": "10vh",
                            "item": [
                                {
                                    "type": "Text",
                                    "width": "15vw",
                                    "height": "10vh",
                                    "textAlign": "center",
                                    "textAlignVertical": "center",
                                    "color": "#efefef",
                                    "fontSize": "${viewport.width * 0.02}",
                                    "text": "BUTTON",
                                    "fontWeight": "900"
                                }
                            ],
                            "backgroundColor": "#ee9977",
                            "borderRadius": "5vh"
                        }
                    }
                ]
            }
        ]
    }
}
```

これを画面付Alexa端末で画面描写すると、以下のような感じで表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354808/1e9a0a22-3659-f8ef-23d1-4c833be66480.png)

ボタンっぽいUIをクリックすると、ボタンを押したかのように少し下に沈み、しばらくすると戻ります。
雑ではありますが、ボタンっぽさは出せているかと思います。

# APL標準コマンドを使えば意外といろいろできる
Alexa = VUI という印象が強いですが、APLを使って画面し、画面にも制御機能をつけるのであれば、当然ながら画面のUIデザインも必要になってきます。
APL標準コマンドはを活用することで、コンポーネントを動かしたりすることも可能ですので、その辺をうまく使ってUIデザインの向上を図ってみるのもAlexaスキル開発の楽しみのひとつなのかなと思ったりします。