Como Construir uma Skill Multimodal

Adicione animações e controle de vídeo

Índice

Nesta seção você vai aprender sobre os Comandos APL e sobre como adicioná-los à sua skill. Vamos animar os componentes de texto e as imagens em todas as telas APL e adicionar um componentes de vídeo.

1. O que são os comandos APL?

Os comandos APL são mensagens que mudam a apresentação visual ou de áudio do conteúdo apresentado ao cliente. Os comandos podem ser usados de algumas formas. A primeira é usar a diretiva Alexa.Presentation.APL.ExecuteCommands. Há também diferentes propriedades de handlers em alguns componentes e até em documentos APL. Por exemplo, o componente responsivo TouchWrapper tem uma propriedade onPress, que engloba uma lista de comandos. Hoje nós usaremos um mecanismo similar dentro do documento APL, a propriedade onMount, que executa comandos imediatamente após expandir o documento. Isso é útil para os comandos de animação que usaremos na introdução.

Agora vamos animar alguns objetos de texto. Queremos criar isto aqui:

final launch screen image

2. Atualize os documentos APL

Antes de mais nada, precisamos atualizar os documentos APL para podermos usar comandos, adicionando uma propriedade de ID aos componentes. Como queremos que os comandos sejam executados imediatamente durante o carregamento da tela APL, colocaremos os comandos dentro da propriedade onMount do documento de inicialização. Para que os comandos tenham os componentes como alvo, precisamos adicionar alguns IDs lógicos aos componentes de texto. Guarde essa ideia! Esses IDs são definidos no pacote APL. Se você der uma olhada na versão hospedada, vai ver que há IDs de componentes adicionados por padrão! São eles o textTop, textMiddle, textBottomque correspondem aos componentes de texto do início, meio e fim, então não é necessário adicionar mais nada. Todavia, se quiser adicionar IDs diferentes, cada ID de componente de texto aparece como um parâmetro no pacote APL.

A. No documento de inicialização (arquivo launchDocument.json), adicione  "id": "image", ao componente AlexaImage.

B. No elemento visual de aniversário, (arquivo birthdayDocument.json), adicione"id": "birthdayVideo", ao componente de vídeo.

3. Adicione comandos ao documento

Usaremos o comando AnimateItem. Esse comando foi adicionado na APL 1.1, então lembre-se de que seu documento APL está declarando a versão 1.1, que ficará como  version: '1.1'. O comando de animação de item vai executar uma sequência de animação de duração fixa em uma ou mais propriedades de um único componente. Vamos modificar a propriedade transform (transformar).

A estrutura básica dos comandos de animação de item será:

{
    "type": "AnimateItem",
    "easing": "ease-in-out",
    "duration": 2000,
    "componentId": "textTop",
    "value": [
        {
            "property": "transform",
            "from": [
                {
                    "translateX": 1200
                }
            ],
            "to": [
                {
                    "translateX": 0
                }
            ]
        }
    ]
}

A propriedade transform exige que você especifique de onde o componente vem e para onde vai.  Todas as transformações nas arrays (matrizes) “to” (para) e “from” (de onde) devem estar de acordo com os tipos. Neste caso, o tipo é translationX. Para mais informações sobre os valores válidos, confira a documentação técnica sobre os valores do comando de animação. Vamos usar o ParallelCommand para executar todas as animações de texto em paralelo. Esse comando tem uma lista de comandos a serem executados, chamados “comandos-filho” (child commands). Todos os comandos-filho são executados simultaneamente.

{
    "type": "Parallel",
    "delay": 500,
    "commands": [
        <List_of_commands>
    ]
}

Agora que entendemos a estrutura dos comandos, precisamos criar um JSON que represente os comandos dentro da propriedade onMount do arquivo launchDocument.json. Vamos adicionar alguns comandos. Para começar, vamos animar os componentes de texto para que todos deslizem em diferentes direções, como em um gif, e queremos que os comandos executem todos ao mesmo tempo, então os três comandos de AnimateItem estarão contidos no comando paralelo.  Para chegar aos valores dessas translações, precisamos entender o sistema de coordenadas da janela de visualização.

A janela de visualização é desenhada com a coordenada (0,0) contida no canto superior esquerdo. Além disso, todas as propriedades “to” serão relativas à posição do objeto no documento APL que criamos, e não às coordenadas da janela de visualização. Para o primeiro objeto, vamos transladar de 1600 para 0, no eixo X. O texto do meio virá da esquerda, então precisamos transladar de -400 (para garantir que o texto não seja visto antes da animação) para 0, no eixo X, e o último item virá de baixo, usando “translateY” de 1200 para 0, já que o Y é positivo e descendente.

A. Adicione o conjunto de comandos à propriedade onMount no documento launchDocument.json. Colocando o que está acima dentro do comando paralelo resultará em: 

[
    {
        "type": "Parallel",
        "commands": [
            {
                "type": "AnimateItem",
                "easing": "ease-in-out",
                "duration": 2000,
                "componentId": "textTop",
                "value": [
                    {
                        "property": "transform",
                        "from": [
                            {
                                "translateX": 1200
                            }
                        ],
                        "to": [
                            {
                                "translateX": 0
                            }
                        ]
                    }
                ]
            },
            {
                "type": "AnimateItem",
                "easing": "ease-in-out",
                "duration": 2000,
                "componentId": "textMiddle",
                "value": [
                    {
                        "property": "transform",
                        "from": [
                            {
                                "translateX": -400
                            }
                        ],
                        "to": [
                            {
                                "translateX": 0
                            }
                        ]
                    }
                ]
            },
            {
                "type": "AnimateItem",
                "easing": "ease-in-out",
                "duration": 2000,
                "componentId": "textBottom",
                "value": [
                    {
                        "property": "transform",
                        "from": [
                            {
                                "translateY": 1200
                            }
                        ],
                        "to": [
                            {
                                "translateX": 0
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

Agora que isso está funcionando, vamos fazer uma animação mais complexa para o componente de imagem. Observando a forma como a animação roda, precisaremos ajustar a imagem, de uma escala bem pequena para 1 (tamanho grande). Além disso, vamos girá-la de 0 para 360 graus durante os dois segundos de duração. Você vai perceber que o caminho que ela faz não é muito linear e que é diferente das outras animações. Isso ocorre porque ela tem definição personalizada. Você não precisa se prender às propriedades definidas na tabela abaixo, mas pode definir sua própria curva com as curvas cúbicas de Bézier ou com um caminho linear. Na verdade, todas as curvas mencionadas têm as definições matemáticas listadas na tabela abaixo. As coordenadas começam em (0,0) e vão até (1,1). Pense na coordenada X como tempo e na Y como a magnitude da mudança. Esta é a curva que eu defini:  "easing": "path(0.25, 0.2, 0.5, 0.5, 0.75, 0.8)", mas você pode escrever a sua própria curva. Fique à vontade!

defined easing curve image

B. Junte tudo e o comando da imagem nos dará:

{
    "type": "AnimateItem",
    "easing": "path(0.25, 0.2, 0.5, 0.5, 0.75, 0.8)",
    "duration": 3000,
    "componentId": "image",
    "value": [
        {
            "property": "transform",
            "from": [
                {
                    "scale": 0.01
                },
                {
                    "rotate": 0
                }
            ],
            "to": [
                {
                    "scale": 1
                },
                {
                    "rotate": 360
                }
            ]
        }
    ]
}

Adicione isso ao seu arquivo launchDocument.json, dentro da lista de comandos da onMount.

C. Agora teste!

D. Quando estiver funcionando, informe seu aniversário e teste o launchHandler com contexto, quando não for seu aniversário. Você deve ver os comandos aplicados aqui também. Ainda não terminamos. O que acha das animações quando for o aniversário? Essa experiência é definida no arquivo birthdayDocument.json, ao qual ainda não adicionamos comandos. Vamos consertar isto.

4. Adicione controle de vídeo

Você reparou na outra mudança no gif acima? Um novo componente foi adicionado ao documento birthdayDocument.json, o componente responsivo AlexaTransportsControls. Tenha sempre um controle na tela para o seu vídeo, se não ele pode não passar na certificação. Vamos adicioná-lo. Esse componente também faz parte do pacote alexa-layouts.

A. Adicione o componente AlexaTransportsControls ao contêiner que tem o componente de vídeo, dentro do birthdayDocument.json, já que queremos que ele fique centralizado também. Ele deve entrar depois do componente de vídeo, na lista de itens.

{
    "type": "Container",
    "alignItems": "center",
    "items": [
        ...<Video_Component>...
        {
            "primaryControlSize": 50,
            "secondaryControlSize": 0,
            "mediaComponentId": "birthdayVideo",
            "type": "AlexaTransportControls"
        }
    ]
}

Nosso componente tem um controle secundário de 0, pois não queremos exibir os botões de controle secundário. Estes são os botões de “pular” e “voltar”, se você está reproduzindo uma série de vídeos. O controle primário é do tamanho do botão de play. O mediaComponentId deve referenciar o VideoComponent mais no começo do documento.

B. Salve e implemente essas mudanças e teste-as no cenário do seu aniversário. O botão deve estar funcionando, parando e reproduzindo o vídeo quando clicado.

Você reparou no corte na resposta de áudio da Alexa? Você não deve ter notado isso se seu aniversário está próximo, mas a resposta de voz da Alexa é cortada quando o vídeo começa a ser tocado. Para consertar isso, precisamos usar a diretiva ExecuteCommands.

5. Diretiva ExecuteCommands

A fala da Alexa é interrompida quando o vídeo começa. Queremos que a Alexa termine de falar e que só então o vídeo comece automaticamente. Precisamos desligar a reprodução automática para resolver isso, mas não faz sentido que nossos clientes precisem dar uma ordem para o vídeo começar. Usaremos os comandos para resolver isso.

Para consertar o áudio, vamos adicionar a diretiva ExecuteCommands ao back-end, além de um payload para ela. A diretiva ExecuteCommands executa a lista de comandos fornecidos depois que a Alexa termina de falar. Ela fica assim:

{
    "type" : "Alexa.Presentation.APL.ExecuteCommands",
    "token": "[SkillProvidedToken]",
    "commands": [
        <List_of_commands>
    ]
}

Para o nosso uso, precisaremos do token fornecido pela skill como alvo da diretiva ExecuteCommand, para que seja "birthdayToken". Sem isso, o comando não saberá em qual documento será executado.

A. Adicione um novo campo de token para a diretiva RenderDocument da APL, com o valor birthdayToken. Sua addDirective(…​) agora deve estar assim:

// Create Render Directive
handlerInput.responseBuilder.addDirective({
    type: 'Alexa.Presentation.APL.RenderDocument',
    token: 'birthdayToken',
    document: birthdayDocument,
    datasources: {
        ... Omitted for brevity...
    }
});

B. Precisamos adicionar outra diretiva ao bloco else do seu HasBirthdayLaunchRequestHandler . Ela pode ser conectada à atual diretiva de exibição. Adicione o código abaixo ao handlerInput.responseBuilder.

.addDirective({
    type: "Alexa.Presentation.APL.ExecuteCommands",
    token: "birthdayToken",
    commands: [
        <List_of_commands>
    ]
});

C. Substitua a  <List_of_commands> pela sua lista de comandos. Esse será um comando único para iniciar o vídeo. Como isso ocorre quando a Alexa termina de falar, conseguimos o comportamento que queríamos! O comando fica assim:

{
    type: "ControlMedia",
    componentId: "birthdayVideo",
    command: "play"
}

No fim, você terá um código de diretiva em APL com essa cara:

// Create Render Directive
handlerInput.responseBuilder.addDirective({
    type: 'Alexa.Presentation.APL.RenderDocument',
    token: 'birthdayToken',
    document: birthdayDocument,
    datasources: {
        text: {
            type: 'object',
            start: "Happy Birthday!",
            middle: "From,",
            end: "Alexa <3"
        },
        assets: {
            video: "https://public-pics-muoio.s3.amazonaws.com/video/Amazon_Cake.mp4",
            backgroundURL: getBackgroundURL(handlerInput, "confetti")
        }
    }
}).addDirective({
    type: "Alexa.Presentation.APL.ExecuteCommands",
    token: "birthdayToken",
    commands: [{
        type: "ControlMedia",
        componentId: "birthdayVideo",
        command: "play"
    }]
});

D. Agora salve, implemente e teste tudo.

Animação irada, não é? Você mandou bem expandindo seu Cake Time com imagens, textos, vídeos e animações!

Código completo no GitHub