How to Build a Multimodal Alexa Skill

Modify Your Backend to Add LaunchRequest Visuals

Table of Contents

In this section, you will learn how to modify your backend to safely add APL visuals to your response. At the end, you will have enhanced your Cake Walk Launch request with the APL visuals you built in the last section. In addition, you will learn how to use S3 in the Alexa Hosted environment to host your own assets and use this to add a background image to your skill.

Cake Walk code will be used in this section. If you have not done the Cake Walk Course, and you are a new Alexa developer, check out the intro course, now, then come back to this module. If you are already a skill builder and experienced with the basics of a nodeJS skill, then feel free to skip the course and see the quick setup instructions to get the code and overview of the Cake Walk Skill.

1. Adding Visuals To Your Skill

For an Alexa device to safely render your APL document, you have to respond with a render directive only when the device supports APL. This directive takes the form of Alexa.Presentation.APL.RenderDocument. You can add a directive using the nodeJS SDK easily by calling handlerInput.responseBuilder.addDirective({…​});

Even though you can add a render directive to every response, not all devices can react to this. In order to safely respond with the Alexa.Presentation.APL.RenderDocument, you must first make sure the calling device sends the proper request object. This information is contained in the supportedInterfaces object found like context.System.device.supportedInterfaces. For example, your sample request might look like:

{
...
"context": {
    "System": {
        ...
        "device": {
            "deviceId": "amzn1.ask.device.1...",
            "supportedInterfaces": {
                "Alexa.Presentation.APL": {
                    "runtime": {
                        "maxVersion": "1.1"
                    }
                }
            }
        }
        ...
    }
    }
}

If you are using the SDK, checking for the supported interfaces is simple.

 

A. In your LaunchRequestHandler.handle() before the response is created, add:

if (Alexa.getSupportedInterfaces(handlerInput.requestEnvelope)['Alexa.Presentation.APL']) {
    // Create Render Directive.
}

This "if" statement is going to check if the APL interface is sent in the request envelope. Only then, do we want to add the response. Now, let’s add the response.

B. Underneath the // Create Render Directive comment, add:

handlerInput.responseBuilder.addDirective({
    type: 'Alexa.Presentation.APL.RenderDocument',
    document: launchDocument,
    datasources: {
        text: {
            type: 'object',
            start: "Welcome",
            middle: "to",
            end: "Cake Walk!"
        }
    }
});

Notice, we left out information about the image. There are some additional steps we need in order to serve the image from our Alexa hosted environment, so we’ll come back to this later. Now, let’s import our launchDocument. We will be naming our document launchDocument.json since it represents the APL Document for the generic Launch Requests.

C. On the left, select the lambda folder. Right-click and select Create Folder. Call this folder documents. Add a new file inside the new folder called launchDocument.json.

D. Inside this new file, add the JSON from the authoring tool we created in module 2 (the full JSON document is at the end of module 2).

E. Then, import this at the top of your index.js like so:

const launchDocument = require('./documents/launchDocument.json');

The launchDocument variable in your javascript code represents the contents of your json file.

F. Now, deploy the code. Remember to test it!

2. Using the Developer Console Simulator to Test

Did you have issues testing your APL additions? Before we can test our document changes, we have to enable APL in the Build tab.

A. Click on the build Tab. You will need to click on Interfaces on the left side.

B. Scroll down and toggle the Alexa Presentation Language and you will see the block expand. Select all of the boxes.

Hub Round, Hub Landscape, and TV Fullscreen are selected by default and must be supported. Each of these are screens you will be officially supporting. They will not scale your content, so whatever assets and APL layout you send in the response will be shown as is. This also means whatever viewport profiles are not supported will scale your content from the closest sized viewport you do support. Select them all because we will be verifying and testing each of these profiles. By selecting these, you are saying you tested your layout on each of these profiles. To learn more about selecting viewport profiles, check out the tech docs.

C. After you toggle APL on and select all of the viewport profiles, you must build the interaction model. Note: this also means changes to the supported profiles need a new skill certification round in order to go live.

D. After building, you are ready to test. Go to the test tab and make sure "Device Display" is checked. Now if you scroll down past the request/response json, you can select the device you want to try. Uncheck the Skill I/O so you do not need to scroll down to see visuals!

Remember to delete your user data if you have any! Since we modified the no context launch handler, you must delete your user data from S3 to make sure there is actually no context in order to test this new screen.

E. Test your screen on a medium hub by triggering the launch handler for instance by typing, open cake walk.

F. Repeat this test on all the other screen types. Remember, we did not include the birthday cake image, yet, so don’t worry if you do not see it!

3. Uploading Assets to S3

Before we can make use of image assets, we will need to host these images. Ideally, you will use a CDN (such as Amazon Cloudfront in conjunction with S3) to serve your assets to locations closer to your users, but for this exercise, we will use S3 since this is provided by Alexa hosted.

A. To upload the S3 Assets, access your S3 provision under the Code tab at the very bottom on the left side.

B. In your S3 provision, open the media page.

C. In here, upload all of the assets from this page. For your convenience, we have the assets zipped here. We will use all of these over the course of the module. After you upload, you will see all of the assets on the page in S3.

Now that your assets are uploaded, we can update our Launch Request with more images.

4. Updating Our Launch Request Handler

Now that we have our no context launch request working, it is time to add a background image rather than using the default background.

A. To do so, first open up the authoring tool and paste in the launchDocument.json information.

B. In the data section of the authoring tool, use:

{
    "text": {
        "start": "Welcome",
        "middle": "to",
        "end": "Cake Walk!"
    },
    "assets": {
        "cake":"https://github.com/alexa/skill-sample-nodejs-first-apl-skill/blob/master/modules/assets/alexaCake_960x960.png?raw=true",
        "backgroundURL": "https://github.com/alexa/skill-sample-nodejs-first-apl-skill/blob/master/modules/assets/lights_1920x1080.png?raw=true"
    }
}

You may notice a new field in our data, backgroundURL, under the assets object. This represents where the device will fetch a background image from. We will use the Github repo for hosting it for now while we develop the screen since this is a public link, but our actual code will use the S3 presigned link util function. The presigned link utility is needed to generate a short-lived public URL to the private bucket you uploaded the assets to. Now, we need to add our background component.

C. Go back to the APL tab in the authoring tool.

D. We are going to add the AlexaBackground responsive component. To use this, you need the alexa-layouts package which we already have! Using the AlexaBackground is easy; just add the following to the top of both of your containers in the items array of each:

{
    "type": "AlexaBackground",
    "backgroundImageSource": "${payload.assets.backgroundURL}"
},

You should see the background light up…​ Er…​ See the lights in the background.

E. Now that we are using a background Image, we want to modify the text color. Since we have a style for all of our text objects this is easy! Simply, add "color": "black", as a new property in our bigText style. This will give you:

"bigText": {
    "values": [
        {
            "fontSize": "72dp",
            "color": "black",
            "textAlign": "center"
        }
    ]
}

F. Apply the same changes to the @hubRoundSmall variation and ensure it works in the authoring tool.

Make sure the AlexaBackground responsive component is above the other components, otherwise it will occlude them!

You may notice we are using a single 1920x1080 png for each of the devices and it scales pretty well. We want to use the highest possible resolution to consider FireTV devices. Scaling down produces a better quality image. The tradeoff is that smaller resolution devices which do not support this quality level will download unnecessary data. The best course of action would be to provide two or more different image resolutions for different devices classes. We will see how to do this in the next section.

5. Applying our images

A. Now that we have our document ready, replace the launchDocument.json contents in your Code tab with the JSON from the authoring tool.

B. Go back to the index.js. Since we are adding in links to our private S3 instance, we will need to import the util module. At the top of this file, add in another import:

const util = require('./util');

C. Add the new datasources to our code in the index.js. Since our images are in the non-public S3 bucket, we are going to be using the util function to get a short lived public URL to the asset. The S3 object keys in this case are going to be of the form, 'Media/imageName.png'. Lets add our images inside the APL render block’s data. Our datasources block will now look like:

datasources: {
    text: {
        type: 'object',
        start: "Welcome",
        middle: "to",
        end: "Cake Walk!"
    },
    assets: {
        cake: util.getS3PreSignedUrl('Media/alexaCake_960x960.png'),
        backgroundURL: util.getS3PreSignedUrl('Media/lights_1920x1080.png')
    }
}

D. Deploy and test your new document on each of the screen sizes.

E. Working? Well, we aren’t fully done yet! We are going to add the optimization we mentioned in the last section. This requires another asset. We have already uploaded the lights_1280x800.png. We need to change out images to conditionally pull the right asset. Replace the value for our backgroundURL with:

util.getS3PreSignedUrl(backgroundKey)

F. To conditionally set the proper backgroundKey, we need to use the Ask-sdk-core which we already have imported as Alexa. To get the viewport profile, inside your APL conditional, add:

const viewportProfile = Alexa.getViewportProfile(handlerInput.requestEnvelope);

G. We can implement this logic with the statement added below the viewportProfile statement:

const backgroundKey = viewportProfile === 'TV-LANDSCAPE-XLARGE' ? "Media/lights_1920x1080.png" : "Media/lights_1280x800.png";

H. Test this out making sure to use the TV and the hub devices in the test console. You may not notice much of a difference. If you want to verify this is working, check out the Skill I/O section in the test console and make sure you have the correct assets served when you use a TV vs a smaller resolution device.

Since our frontend is scaling properly by using the responsive components, we are done! Let’s head to the next module and learn some about more advanced APL document concepts.

Complete code in Github