Como Construir uma Skill Multimodal

Como construir seus primeiros elementos visuais com a Authoring Tool da APL

Índice

Nesta seção, você vai aprender sobre a authoring tool (ferramenta de criação) e sobre como usá-la para criar um documento de resposta do LaunchRequest da APL para sua skill de Cake Walk. No final desta seção, você terá uma tela que diz “Bem-vindo ao Cake Walk!”, com uma pequena imagem de um bolo. Para fazer essa tela, você vai aprender sobre o seguinte:

 

  • Componentes de texto e imagem da APL
  • Estilos
  • Fundamentos da vinculação de dados
  • Documentos responsivos

 

Vamos começar.

Sugerimos o uso de Firefox ou Chrome para este curso. A authoring tool não suporta o Safari.

1. Hello Cake Time

A. Clique aqui para ir para a authoring tool. Salve este link como favorito, pois faremos referência a ele ao longo do curso.

B. Você será recebido com uma solicitação para ver a nova experiência. Clique em Yes (Sim).

authoring tool new experience image

A nova experiência permitirá que você salve os Documentos da APL na sua skill.

C. Encontre a skill que está usando no menu e clique em Select (Selecionar).

 

Select Skill Image

D. Clique em Create Template (Criar Modelo). Agora você verá diversos modelos.

Authoring Tool Landing page image

E. Vamos começar do zero. Com isso, teremos um documento de APL básico, sem os componentes adicionados ao mainTemplate.

F. Na authoring tool, lembre-se de selecionar a GUI (interface gráfica do usuário), à esquerda. Assim, teremos uma representação gráfica da estrutura do documento (em Layouts), além de algumas caixas de texto onde podemos incluir as propriedades. À direita, você verá os componentes e pode arrastá-los para o lugar certo. Arraste um componente de texto. Você vai ver algo assim:

hello world image

G. Vamos adicionar um texto: “Hello Cake Time!” Clique no componente de texto, na seção de layouts.

H. Role a tela até a seção do meio, até encontrar a propriedade de texto do componente de texto. Mude a mensagem para “Hello Cake Time!”

I. Acima, encontre a propriedade fontSize e mude para 72dp. A ideia é aumentar o tamanho da fonte para contemplar os clientes que estão olhando para o dispositivo à distância. Agora o texto está fora da caixa azul... Eita! Como o texto é limitado pela caixinha, ele não é mais mostrado corretamente com uma fonte tão grande. Vamos consertar isto.

J. Role a tela até a seção de Width (Largura) e localize a propriedade de largura do componente de texto. Apague o valor que está na caixa de texto de largura, para que ele fique, por padrão, em automático.

K. Faça o mesmo com a altura na seção Height (Altura). Esta é uma configuração melhor porque automaticamente ajusta o tamanho da caixa ao conteúdo do texto. Isso pode ser útil mais tarde, se resolvermos mudar o tamanho da fonte, como fizemos acima.

Agora, você deveria ver algo assim:

Hello cake walk gui

Parabéns! Você tem um documento hello caketime que funciona. Você pode ver a saída do documento na aba APL, na barra lateral.

Para referência, este é o JSON que você deveria ter. 

{
    "type": "APL",
    "version": "1.1",
    "settings": {},
    "theme": "dark",
    "import": [],
    "resources": [],
    "styles": {},
    "onMount": [],
    "graphics": {},
    "commands": {},
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                "type": "Container",
                "items": [
                    {
                        "type": "Text",
                        "paddingTop": "12dp",
                        "paddingBottom": "12dp",
                        "fontSize": "72dp",
                        "text": "Hello Cake Time!"
                    }
                ],
                "height": "100%",
                "width": "100%"
            }
        ]

    }
}

Criamos um componente de texto e modificamos algumas propriedades dele com a GUI. Para ver o conjunto completo de propriedades dos componentes de texto, dê uma olhada na documentação. Se você viu a página, vai notar que faltam o paddingTop e o paddingBottom. Isso ocorre porque todos os componentes herdam as propriedades das parent properties (propriedades originárias). Veja a lista completa de propriedades de estilo que todos os componentes têm.

A authoring tool também criou um contêiner. Os contêineres são componentes que abrigam outros componentes, em alinhamento com a “coluna” ou “linha”. Como os contêineres também são componentes, eles têm as propriedades básicas de componentes. Os contêineres são essenciais para criar documentos responsivos da APL. Documentos complexos usam uma combinação de contêineres de linha e coluna para dispor os componentes de forma que eles se ajustem corretamente aos dispositivos.

2. Hello Cake Time Com Estilo

Algumas propriedades dos componentes podem ser definidas com estilos. Os estilos são um conjunto de propriedades identificado que pode ser reutilizado em outros componentes.

Nem todas as propriedades serão estilizadas em todos os componentes. Veja a lista completa de propriedades estilizáveis de cada tipo de componente.

A. Clique na barra lateral de styles (estilos). Você verá parênteses vazios. Substitua os parênteses vazios por isto:

{
    "bigText": {
        "values": [
            {
                "fontSize": "72dp"
            }
        ]
    }
}

B. Clique na aba da APL, na barra lateral, e você vai ver que seu documento foi atualizado com os estilos que foram adicionados à seção.

C. Agora vamos modificar o item de Texto para apagar a propriedade fontSize e incluir o seguinte::

"style": "bigText"

Note que as propriedades ainda são observadas, já que está pegando do estilo que você definiu. Você pode fazer o teste mudando a propriedade fontSize no bloco de estilo. Seu código APL agora deve estar parecido com isto:

final hello apl diagram

Vamos avançar e centralizar o texto usando os estilos.

D. Na seção de estilos, vamos adicionar a propriedade textAlign e definir como centralizado.

"textAlign": "center"

Com isso, você terá uma “massa” de estilo parecida com isso:

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

Mesmo que você não tenha modificado o componente de texto em si, como ele usa o estilo bigText, este é aplicado ao componente de texto.

3. Vinculação de dados

Você reparou no botão Data (dados)? Ele simula a fonte de dados que pode fazer parte da diretiva   Alexa.Presentation.APL.RenderDocument que é o que você envia do back-end da sua skill para reproduzir o documento. Voltaremos a isso mais tarde, mas, antes, vamos dar uma olhada em como construir nosso documento com fontes de dados.

Para referenciar dados em uma fonte de dados, você precisa passar um parâmetro pelo documento APL. Nas primeiras versões da APL, a fonte de dados inteira estava presa a um único parâmetro, que, por padrão, tinha o nome “payload”. Hoje em dia, você pode passar vários parâmetros, que são definidos na sua fonte de dados, desde que nenhum dos parâmetros se chame “payload”. Usar um parâmetro chamado “payload” retorna a este antigo comportamento, por razões de compatibilidade reversa, mas não é recomendável usar esse nome antigo. Se você olhar seu documento APL atual, verá o nome do parâmetro da authoring tool padrão: “payload”. Precisamos modificar isso para ficar de acordo com seus parâmetros de dados. Vamos adicionar e usar uma fonte de dados simples.

A. Na array (matriz)  mainTemplate.parameters array, , troque a palavra “payload” por “text”. Com isso, você terá:

Copied to clipboard
"mainTemplate": {
    "parameters": [
       "text"
    ]
   ...
}

Agora que o parâmetro está no documento, podemos referenciá-lo. A fonte de dados é uma representação do JSON de pares de valores chave. Podemos alocar este objeto da forma que fizer sentido para o aplicativo. Agora vamos adicionar outro componente de texto, que usará a fonte de dados e o estilo que definimos. Para referenciar os dados, você escreverá uma expressão como: , ${parameterName.YourDefinedObject}. Vamos modificar o JSON da APL.

B. Dentro da array dos itens do contêiner, sob o objeto de texto, inclua:

{
    "type": "Text",
    "style": "bigText",
    "text": "${text.middle}"
},
{
    "type": "Text",
    "style": "bigText",
    "text": "${text.end}"
}

C. Já que estamos mexendo nisso, vamos mudar os dados do texto do primeiro componente de texto para ${text.start}.  Ué... Para onde ele foi? O texto sumiu porque não temos dados na fonte de dados que estamos referenciando. Vamos consertar isso usando a aba Data.

D. Após clicar em Data, você verá um conjunto vazio de dados {}. Precisamos adicionar dados que sigam a estrutura que definimos no parâmetro que chamamos de “texto”. Agora temos um objeto de “texto” com campos de “início”, “meio” e “fim”.

E. Adicione o seguinte à seção Data da authoring tool:

{
    "text": {
        "start": "Welcome",
        "middle": "to",
        "end": "Cake Time!"
    }
}

O objeto JSON de dados representa dados variáveis no documento. Vamos reutilizar esse layout mais à frente, para apresentar um texto com estrutura semelhante, mas com novos dados. Esta técnica permite que você localize a skill com mais facilidade, dado que toda a lógica da localização pode ficar no back-end. Além disso, alavancaremos essa funcionalidade para reutilizar o documento APL. Você verá o seguinte:

welcome to cakewalk image

Agora temos um conjunto de estilos reutilizáveis no documento APL e aprendemos como fazer uma tela usando a vinculação de dados. Vamos adicionar a imagem de um bolo de aniversário.

4. Adicione um bolo de aniversário

Precisamos adicionar um componente de imagem e usar a vinculação de dados. Os componentes de imagem usam uma URL para o recurso que está armazenando a imagem. Ocorre que a imagem é um componente primitivo. Seria muito trabalhoso ajustar a imagem para todos os tamanhos de janela de visualização e isso dependeria de várias resoluções de imagem, dado que ela não é autoajustável. Em vez disso, use o componente responsivo AlexaImage para que possamos usar uma única imagem que se ajustará a todas as resoluções dos dispositivos.

Para usar o componente AlexaImage, precisamos adicionar uma importação. A importação permite que você referencie layouts, estilos e recursos definidos em outros pacotes. Usaremos um pacote padrão chamado alexa-layouts. A importação ficará assim:

{
    "name": "alexa-layouts",
    "version": "1.1.0"
}

Adicione o objeto de importação acima à sua lista de importação na seção de importação de documentos de APL. Depois, o código ficará assim:

{
    "type": "APL",
    "version": "1.1",
    "settings": {},
    "theme": "dark",
    "import": [
        {
            "name": "alexa-layouts",
            "version": "1.1.0"
        }
    ],
 ...<Omitted_rest_of_doc>
}

O alexa-layouts é um pacote importante para a criação de layouts responsivos. O componente AlexaImage tem muitos parâmetros, a maioria opcional.

B. Inclua o bloco de imagem a seguir em um novo contêiner, sob o último componente de texto. Este novo bloco deve ser incluído no contêiner já existente, então não se esqueça de colocá-lo na mesma array de “itens” dos componentes de texto.

{
    "type": "AlexaImage",
    "alignSelf": "center",
    "imageSource": "${assets.cake}",
    "imageRoundedCorner": false,
    "imageScale": "best-fill",
    "imageHeight":"40vh",
    "imageAspectRatio": "square",
    "imageBlurredBackground": false
}

Vamos por partes:

  • Para os campos que estamos usando no AlexaImage, o imageSource é relevante porque especifica a URL onde a imagem está hospedada.
  • Queremos que fique com a proporção padrão de paisagem, já que queremos manter a resolução da imagem.
  • Quando a imagem mudar de tamanho, ela usará a estratégia de melhor encaixe.
  • Para controlar o tamanho, estamos usando a propriedade imageHeight, configurando para que fique em 40% da altura da janela de visualização.

Para aprender mais sobre isso, confira os parâmetros na documentação técnica. Se você ler a documentação técnica, vai ver que não há referências ao alignSelf. Esta propriedade existe e funciona porque é um componente “filho” (child) do contêiner. O alignSelf passa por cima do alinhamento do contêiner apenas para aquele filho. Há algumas outras propriedades que também são adicionadas ao filho de um contêiner. Ele depende de um novo objeto “assets.cake” ser adicionado à seção de dados. A nova seção de dados ficará assim:

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

C. Vá para a aba Data e atualize seus dados com o novo objeto “assets” (ativos).

D. O último passo é adicionar os “ativos”, novo parâmetro do mainTemplate. Volte para a aba APL e adicione-o à lista mainTemplate.parameters, e o resultado será:

Copied to clipboard
"mainTemplate": {
    "parameters": [
       "text",
       "assets"
    ]
   ...
}

Você vai ver algo assim:

authoring tool image

Como está? Uma delícia? Está começando a ficar mais com cara de skill com tema de aniversário. Vamos fazê-la funcionar para os outros perfis de janela de visualização também.

5. Crie um documento responsivo

Abaixo da janela do simulador, onde vemos as mudanças, estão alguns dispositivos Echo com tela. Vínhamos usando o dispositivos “Hub Médio” (que são os parâmetros de tela do Echo Show) por enquanto, mas há vários outros dispositivos suportados. Vamos testar nosso documento em outras telas.

A. Clique nos vários símbolos no topo e anote os problemas que encontrar.

Os tipos de dispositivo de simulação

  • Hub Pequeno [Redondo] (480x480)
  • Hub Pequeno [Paisagem] (960x480)
  • Hub Médio (1024x600)
  • Hub Grande (1280x800)
  • TV Extra Grande (1920x1080)
  • Adicionar dispositivo personalizado (valor x valor)

Com a última opção, você consegue criar qualquer resolução de tela para simular a apresentação do dispositivo.

AVISO: spoiler abaixo

broken hello spot image
Figura 1. Isso não está certo...

B. O texto está cortado na tela do Hub Pequeno (Redondo). Vamos consertar isto usando a propriedade quando (when). Esta propriedade permite o uso da avaliação booleana. Se for verdadeiro, mostrará um componente e seus filhos; se for falso, não. Além do when, usaremos Recursos da importação de alexa-layouts. Os recursos são chamados simplesmente de constantes e são referenciados com  @<Resource_name>. Desta vez, usaremos as definições de constantes do pacote de alexa-layouts, representando os tipos de dispositivos acima e os perfis de janela de visualização. Com isso, você pode criar declarações com constantes específicas e pré-definidas de janela de visualização, tais como:

${@viewportProfile == @hubLandscapeLarge}

em vez de

${viewport.width == "1280dp"}

Não há diferença entre essas declarações para a solicitação de um dispositivo Echo Show 2. Mas vamos considerar que há um novo dispositivo com tela panorâmica (wide) de 1300dp. Será que devemos incluir outra declaração nesta condicional? E se houver um terceiro dispositivo em uma classe parecida? Usando os recursos definidos pela Amazon, teremos documentos APL que se ajustam melhor, mesmo sem sabermos todas as possíveis permutações de tamanhos de tela. Isso ocorre porque o @hubLandscapeLarge representa telas com larguras entre 1280 e 1920, englobando mais dispositivos dessa classe. Mesmo que esteja na mesma classe do dispositivo, dado que a tela não tem a mesma largura que estamos checando, ela não mostrará nada.

C. Como nosso documento parece estar funcionando em todos os dispositivos, exceto o Hub Pequeno (Redondo), vamos adicionar outro conjunto de componentes para ele. Clique no ícone do Hub Pequeno (Redondo).

D. Como uma avaliação falsa fará com que nenhum componente filho seja mostrado, vamos adicionar uma declaração no topo do primeiro contêiner.

"when":"${@viewportProfile != @hubRoundSmall}"

E. Uma tela preta deve aparecer agora! Confira nas telas retangulares e os componentes aparecerão. Como omitimos a classe @hubRoundSmall deste contêiner e dos filhos, precisamos fazer um novo contêiner que será exibido quando estivermos em um dispositivo .@hubRoundSmall.

F. Duplique o primeiro contêiner e os componentes de texto (filhos) e adicione-os à lista de itens do mainTemplate. Você deve adicionar o inverso dessa declaração acima, neste bloco:

"when":"${@viewportProfile == @hubRoundSmall}"

G. Agora, vamos consertar a exibição. Basta adicionar um preenchimento (padding) no topo do primeiro componente de texto.

"paddingTop": "75dp",

H. Agora, remova todos os valores de preenchimento que estão nas caixas de texto.

I. Depois, remova a imagem do bolo. Agora todos os tipos de dispositivos devem estar exibindo a tela corretamente. Confira seu trabalho nas outras classes para ter certeza de que está tudo certo.

J. Usaremos este JSON na próxima seção. Salve o documento APL como  launchDocument.json.

Um detalhe: poderíamos ter consertado o documento de várias formas para adequá-lo ao perfil do Hub Pequeno (Redondo). Poderíamos só manter a imagem e apagar o texto ou mover a imagem para o fundo do Hub Pequeno (Redondo). Em termos de estrutura, podemos manter tudo em um contêiner e adicionar o preenchimento, como condição, escondendo a imagem para gerar a mesma experiência. O benefício dessa abordagem técnica é que não termos componentes recentemente adicionados por padrão no futuro. Isso também significa que conforme fazemos iterações e mudamos os hubs retangulares, não modificaremos a estrutura das telas do Hub Pequeno (Redondo). Como essa tela é fundamentalmente diferente das demais, especialmente no design, nós a dividimos. Sinta-se livre para testar outras abordagens com as skills, se for melhor para o seu design!

O documento APL JSON final, para referência:

Copied to clipboard
{
   "type": "APL",
   "version": "1.1",
   "settings": {},
   "theme": "dark",
   "import": [
       {
           "name": "alexa-layouts",
           "version": "1.1.0"
       }
   ],
   "resources": [],
   "styles": {
       "bigText": {
           "values": [
               {
                   "fontSize": "72dp",
                   "textAlign": "center"
               }
           ]
       }
   },
   "onMount": [],
   "graphics": {},
   "commands": {},
   "layouts": {},
   "mainTemplate": {
       "parameters": [
           "text",
           "assets"
       ],
       "items": [
           {
               "type": "Container",
               "when":"${@viewportProfile != @hubRoundSmall}",
               "items": [
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "12dp",
                       "paddingBottom": "12dp",
                       "text": "${text.start}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "12dp",
                       "paddingBottom": "12dp",
                       "text": "${text.middle}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "12dp",
                       "paddingBottom": "12dp",
                       "text": "${text.end}"
                   },
                   {
                       "type": "AlexaImage",
                       "alignSelf": "center",
                       "imageSource": "${assets.cake}",
                       "imageRoundedCorner": false,
                       "imageScale": "best-fill",
                       "imageHeight":"40vh",
                       "imageAspectRatio": "square",
                       "imageBlurredBackground": false
                   }
               ],
               "height": "100%",
               "width": "100%"
           },
           {
               "type": "Container",
               "when":"${@viewportProfile == @hubRoundSmall}",
               "items": [
                   {
                       "type": "Text",
                       "style": "bigText",
                       "paddingTop": "75dp",
                       "text": "${text.start}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "text": "${text.middle}"
                   },
                   {
                       "type": "Text",
                       "style": "bigText",
                       "text": "${text.end}"
                   }
               ],
               "height": "100%",
               "width": "100%"
           }
       ]
   }
}

Vamos usar esse documento na próxima sessão.

Código completo no Github