※2019/3/11に内容を更新しました。
今回は初めてのAPLということで、まずは簡単にお手持ちのスキルにAPLを使った画面表示を加える方法をご紹介します。 APLによる画面デザインはゼロから作成することもできますが、今回はスキルビルダーで用意しているサンプルを利用します。
スキルビルダーの左側「インターフェース」を開いて「Alexa Presentation Language」を有効にします。
skill.json では以下のような定義になります。
"apis": {
...
"interfaces": [
{
"type": "ALEXA_PRESENTATION_APL"
}
]
}
},
スキルビルダーの左側「画面表示」をクリックしてください。
いくつかサンプルデザインが出てきます。 どれでも結構ですが、ここでは「画面表示サンプル」を選択します。
選択したサンプルデザインのオーサリング画面になります。 デザインをカスタマイズするときや一から作成するときには、ここで視覚的効果を確認しながら編集することができます。 ここでは「コードを書き出し」をクリックしてサンプルをそのままダウンロードします。 とりあえず BodyTemplate7.json という名前で Lambda 関数が置かれているフォルダに保存してください。 Alexa-hostedスキル環境の場合は、コードエディタで BodyTemplate7.json という名前のファイルを作成して、ダウンロードしたファイルの中身をコピペします。
BodyTemplate7.json は以下のような構成になっています。 ①の部分は画面デザインを定義した APLドキュメント、②の部分は データソース のサンプルです。 データソースのサンプルの部分は実際のスキルでは不要なので、①部のみを抽出してもいいのですが、APLオーサリングツールはこのファイル形式でダウンロード・アップロードしますので、このような構成で管理するのもよいと思います。
Lambda 関数の適当なインテントハンドラに、以下のように .addDirective()
を追加します。Lambda関数を保存、デプロイしたら完成です。
handle(handlerInput) {
const speechOutput = '画面サンプルです';
const aplSample = require('./BodyTemplate7.json');
return handlerInput.responseBuilder
.speak(speechOutput)
.addDirective({
type : 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: aplSample.document,
datasources: aplSample.datasources
})
.getResponse();
}
Alexaシミュレータで該当インテントを発生させてみてください。右側画面の下のほうで出力された画面を確認することができます。
APLドキュメントはサンプルのままでも利用できますが、データソースはスキル毎に異なります。 場合によっては、データソースは動的に生成されるかもしれません。 ここでは、以下のようにデータソースを変更してみます。
.addDirective({
type : 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: aplSample.document,
// aplSample.dataSources の代わりに、プログラム内で定義した
// object を datasources に指定
datasources:
{
"bodyTemplate7Data": {
"title": "コーヒーショップ",
"backgroundImage": {
"sources": [
{
"url": pictureUrl
}
]
}
}
}
})
Alexa-hostedスキル環境では、別途サービスを利用することなく、Amazon S3 を利用したメディアファイルを扱うことができます。
左側「Media Storage:S3」をクリックしてください。 ここで「アップロード」ボタンをクリックして、ご自身のファイルを選択し、再び「アップロード」ボタンをクリックしたら画像の準備は完了です。
スキルからこの S3 のメディアファイルにアクセスするには、署名済み URL を入手する必要があります。 以下例のように util.js
で用意されている getS3PreSignedUrl()
を使うと簡単に実装できます(このS3へ画像をアップロードするとデフォルトで Media
フォルダに収まります)。
コードができたら、デプロイして動作確認してみてください。 サンプルを利用すると、下のように画面サイズの違いを考慮したデザインがはじめから適用されます(Alexa シミュレータと実際のデバイスは表示が異なる場合がありますのでご注意ください)。
画面のないタイプのデバイスに対してAPLドキュメントを配信すると、エラーになりますので注意が必要です。 次のようなヘルパー関数を用意して、APL対応デバイスかどうか判別できるようにするとよいでしょう。
// util.js へ以下を追加
module.exports.isAPLSupported = function isAPLSupported(request) {
return request
&& request.context
&& request.context.System
&& request.context.System.device
&& request.context.System.device.supportedInterfaces
&& request.context.System.device.supportedInterfaces['Alexa.Presentation.APL'];
}
この関数を使って、例えば次のように、APLドキュメントの追加をコントロールします。
const Util = require('util.js');
...
handle(handlerInput) {
...
const builder = handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText);
if (Util.isAPLSupported(handlerInput.requestEnvelope)) {
...
builder.addDirective({
type : 'Alexa.Presentation.APL.RenderDocument',
...
});
}
return builder.getResponse();
}
};
APLで画面表示をする場合は、画面なしの時とセッションの挙動が違ってきます。 画面表示がない場合、.reprompt()
を使うなどして shouldEndSesssion
を false に明示しないと、ただちにセッションが終了します。 一方、画面表示をする場合は、shouldEndSesssion
が指定されていない(reprompt()
がない)、もしくは null
のとき、セッションは開かれたままになります。 ユーザーがウェイクワードに続き発話を行うと、引き続き画面表示を行っているスキルでインテント処理が行われます。
スキルを終了させるには、.withShouldEndSession(true)
を使い、明示的にセッションを終了させる必要があります。
const OrderIntentHandler = {
...
handle(handlerInput) {
...
return handlerInput.responseBuilder
.speak(speechText)
.withShouldEndSession(true)
.getResponse();
}
};
reprompt()
が指定されている場合、マイクがオンになり青い輪が表示されます。 この時、ユーザーの応答待ち(8秒+8秒)がタイムアウトすると青い輪が消えますが、画面表示は継続し、表示がタイムアウトするまでセッションは開かれたままになります。 再プロンプトなしで shouldEndSession
が false
に設定された場合も表示が継続され、30秒間にわたりアクティビティが行われないとセッションは終了されます。
ユーザーが混乱しますので、StopIntent
や終了時には、必ず .withShouldEndSession(true)
を使うようにしてください。
以前画面に対応したスキルを作った方はお気づきかもしれませんが、現時点の APLデザインサンプルは、Display テンプレートのデザインがベースとなっています。 Displayテンプレートのリファレンス も参照してみてください。 以下 APL のサンプルと Display テンプレートの関係です。
APLサンプル | Displayテンプレート |
---|---|
画像表示サンプル |
BodyTemplate7 |
詳細テキストサンプル |
BodyTemplate1 |
短いテキストサンプル |
BodyTemplate6 |
右側画像と詳細のサンプル |
BodyTemplate2 |
左側画像と詳細のサンプル |
BodyTemplate3 |
画像メインのリストのサンプル |
ListTemplate2 |
テキストメインのリストのサンプル |
ListTemplate1 |