Criando uma fantástica experiência com o bloco (parte 2)

Na parte 1 desta postagem, aprendemos como criar atualizações de bloco e escolher os modelos que correspondem ao conteúdo que você deseja mostrar em seu bloco dinâmico. Configuramos o aplicativo com um amplo bloco padrão e agora estamos prontos para começar a atualizar os nossos blocos. Agora mergulhamos direto no código. Primeiro, veremos como configurar a sondagem no bloco do nosso aplicativo Contoso Food Trucks, incluindo a aparência do código do nosso serviço Web. Em seguida, adicionamos um bloco secundário ao aplicativo e o atualizamos usando a biblioteca NotificationsExtension fornecida no exemplo de notificações e blocos de aplicativo do SDK do Windows 8. Vamos colocar a mão na massa!

Escolhendo o método de entrega de uma notificação

Agora que sei qual aparência quero para os blocos (consulte a parte 1 para recordar), preciso descobrir quando atualizá-los.

Existem 4 formas em que um aplicativo pode atualizar seu bloco (consulte Escolhendo um método de entrega de notificação no Centro de Desenvolvimento). Os aplicativos podem usar notificações locais para atualizar seu bloco, o que é útil em caso de alterações de informações enquanto o aplicativo está em execução. Os aplicativos podem agendar atualizações de bloco e de notificações do sistema para que aconteçam em horários precisos. Além disso, os aplicativos podem usar notificações de bloco de sondagem ou de push para atualizar seus blocos a partir da nuvem enquanto eles não estão em execução. A sondagem é ótima para conteúdo de difusão de baixa frequência. O envio por push é ótimo para enviar notificações do sistema, que precisam chegar imediatamente, ou atualizações de bloco que são destinadas a usuários individuais. Nesta postagem, focalizo as atualizações de sondagem e as atualizações locais.

Sondagem por caminhões de comida mais próximos

Nosso aplicativo possui dois tipos diferentes de informações com as quais o bloco pode ser atualizado. O mais importante são os caminhões de comida próximos do local de almoço padrão de um usuário. Os usuários definem seus locais de almoço padrão no aplicativo quando ele é executado. Uso as informações desses locais padrão para atualizar o bloco e permitir que o usuário saiba sobre os caminhões de comida próximos desse local. As imagens aqui mostram os blocos do aplicativo da parte 1 da postagem. Agora, vamos observar como usar a sondagem para fazer com que esses blocos apareçam no bloco do nosso aplicativo.

O bloco retangular grande exibe: Caminhões de comida próximos a você / Caminhão Nom Nom Barbecue / Caminhão Sushi / Caminhão Macaroni Makin' Wagon    O bloco quadrado exibe: Próximo a você / Nom Nom / Caminhão Sushi

Os caminhões de comida geralmente ficam em um local o dia inteiro, ou pelo menos durante as horas de almoço. Como os locais dos caminhões de comida não mudam frequentemente, não preciso atualizar o bloco em tempo real. Por isso, posso controlar as notificações de push, que são mais úteis para notificações com detecção de hora. Mas, desejo que esses dados sejam atualizados pelo menos uma vez por dia, portanto, usar notificações periódicas que sondam atualizações em meu serviço Web é minha melhor opção.

Implementação do cliente: sondagem por caminhões de comida mais próximos

A implementação do lado do cliente para configurar notificações periódicas leva apenas algumas linhas de código. Chamamos TileUpdater.startPeriodicUpdate cada vez que um usuário inicia o aplicativo ou alterna para ele. Isso faz com que o URI passado para a API seja sondado imediatamente e que o bloco seja atualizado em cada inicialização ou alternância para o aplicativo. Isso é porque a chamada à API imediatamente alcança o URI do serviço da nuvem e atualiza o bloco. Esse comportamento é muito útil para depuração – podemos testar a sondagem de nosso serviço na nuvem sem esperar pelo próximo intervalo de sondagem.

A questão real é o que o URI fornece para a API startPeriodicUpdate. Em nosso caso, quero informar ao nosso serviço na nuvem para atualizar o bloco com as informações sobre um local específico. Para proteger as informações do local do usuário, não quero enviar o local exato do usuário para o nosso serviço. Em vez disso, identifico os locais pelo CEP que o usuário fornece no aplicativo.

Para fornecer o CEP ao meu serviço Web, eu anexo uma cadeia de pesquisa no fim do URI de sondagem para que o nosso serviço na nuvem saiba que local colocar no bloco:

https://www.contoso.com/foodtrucks/tile.xml?zipcode=98052

Em resposta a uma solicitação HTTP GET desse URI, nosso serviço Web retorna o XML de notificação do bloco formatado do CEP fornecido no URI. Aqui está o código para configurar a sondagem, com o CEP inserido no código da cadeia de caracteres do URI.

JavaScript:

 // update the tile poll URI
var notifications = Windows.UI.Notifications;
var polledUri = new Windows.Foundation.Uri("https://www.contoso.com/foodtrucks/tile.xml?zipcode=98052");
var recurrence = notifications.PeriodicUpdateRecurrence.hour;
var tileUpdater = notifications.TileUpdateManager.createTileUpdaterForApplication();
tileUpdater.startPeriodicUpdate(polledUri, recurrence);

C#:

 // update the tile poll URI
using Windows.UI.Notifications;
Uri polledUri = new Uri("https://www.contoso.com/foodtrucks/tile.xml?zipcode=98052");
PeriodicUpdateRecurrence recurrence = PeriodicUpdateRecurrence.Hour;
TileUpdateManager.CreateTileUpdaterForApplication().StartPeriodicUpdate(polledUri, recurrence);

Como um caminhão de comida pode se mover durante o dia, desejo atualizar o bloco muito frequentemente. Em nosso caso, uso um intervalo de atualização de uma hora para balancear entre a carga em nosso serviço backend e a atualização das informações no bloco.

Depois de eu chamar a API startPeriodicUpdate uma vez, o bloco continua a ser atualizado uma vez por hora, mesmo que o nosso aplicativo não esteja em execução. Se algum dia eu quiser alterar o URI para sondagem, basta chamar a API novamente com um URI diferente. Por exemplo, se o usuário alterar seu local padrão para um CEP diferente, atualizo o URI com o CEP correto, chamando startPeriodicUpdate novamente. Se o usuário apagar seu local padrão, ou quiser parar a atualização do bloco, o aplicativo pode parar as atualizações periódicas chamando a API stopPeriodicUpdate.

Para obter mais informações sobre como usar a API startPeriodicUpdate , consulte Exemplos de notificações periódicas e por push do cliente e Como configurar notificações periódicas de blocos no Centro de Desenvolvimento.

Implementação do servidor: sondagem por caminhões de comida mais próximos

Podemos implementar o lado do servidor de nosso bloco sondado em praticamente qualquer tecnologia de serviço. Aqui eu mostro um código PHP e ASP.NET de exemplo.

Quando nosso serviço Web é sondado, ele deve responder à solicitação HTTP GET com o XML que se adequa ao esquema XML do bloco. Você também pode usar HTTPS para que o serviço Web possa proteger o conteúdo quando ele é enviado por transferência eletrônica. Cookies não são aceitos. Todas as informações de que o seu serviço precisa para responder à solicitação devem ser incluídas como parte do URI. Por esse motivo, nosso aplicativo usa cadeias de pesquisa para passar o CEP.

No código PHP que observaremos em seguida, eu abstraio o acesso do banco de dados do nosso serviço Web. Em código de funcionamento real, a variável dos caminhões retornada pela função get_trucks_from_database() conteria todas as informações de que o serviço Web precisa para preencher o modelo do bloco de um CEP especificado. Simplifiquei um pouco o código de exemplo desse serviço para focalizar no XML que o serviço retornará. A implantação de um serviço Web no mundo real consideraria desempenho, escalabilidade, segurança e uma arquitetura que possa sofrer manutenção.

PHP:

 <?php

//
// set default query string parameters
// process query string and set parameters
//
if($_GET['zipcode']){
  $zipcode = $_GET['zipcode'];
}
else{
  $zipcode = 'default';
}

//
// get item info from our database
// - this is placeholder code that you need to replace in a real implementation
// - the return value is a multidimensional array of the long and short strings
//   to place in the tile template
//
$trucks = get_trucks_from_database($zipcode);

?>
<?php echo '<?xml version="1.0" encoding="utf-8" ?>'?>
<tile>
  <visual>
    <binding template="TileWideText01">
      <text id="1">Food Trucks Near You</text>
      <text id="2"><?php echo $trucks[0][0]?></text>
      <text id="3"><?php echo $trucks[0][1]?></text>
      <text id="4"><?php echo $trucks[0][2]?></text>
    </binding>
    <binding template="TileSquareText03">
      <text id="1">Near You</text>
      <text id="2"><?php echo $trucks[1][0]?></text>
      <text id="3"><?php echo $trucks[1][1]?></text>
    </binding>
  </visual>
</tile>

O próximo exemplo de código é equivalente ao código PHP que acabamos de observar. Este exemplo de Páginas da Web do ASP.NET mostra uma implementação rápida de um serviço de bloco. Para um serviço ASP.NET completo, você provavelmente desejaria usar a nova API da Web do ASP.NET. A API da Web do ASP.NET é criada especificamente para serviços HTTP como esse. Para obter mais informações sobre a API da Web do ASP.NET, consulte https://www.asp.net/web-api.

ASP.NET:

 @{
  //
  // set default query string parameters  
  // process query string and set parameters
  //
  var zipcode = Request["zipcode"] ?? "default";
  //
  // get item info from our database
  // - this is placeholder code that you need to replace in a real implementation
  // - the return value is a multidimensional array of the long and short strings
  // to place in the tile template
  var trucks = get_trucks_from_database(zipcode);
 
}<?xml version="1.0" encoding="utf-8" ?>'?>
<tile>
  <visual>
    <binding template="TileWideText01">
      <text id="1">Food Trucks Near You</text>
      <text id="2">@trucks[0,0]</text>
      <text id="2">@trucks[0,1]</text>
    </binding>
    <binding template="TileSquareText03">
      <text id="1">Near You</text>
      <text id="2">@trucks[1,0]</text>
      <text id="2">@trucks[1,1]</text>
    </binding>
  </visual>
</tile>

Caminhões de comida favoritos

Até agora observamos o conteúdo que o aplicativo mostra no bloco principal. Mas, às vezes, pode ser que um usuário queira ter um bloco em sua tela inicial para rastrear um caminhão de comida específico. Em nosso aplicativo, eu uso a barra de aplicativos para permitir que o usuário fixe um caminhão de comida específico na tela inicial. Esses blocos fixos são chamados de blocos secundários. Depois que um usuário fixa um bloco secundário, atualizamos esse bloco com informações sobre esse caminhão de comida particular, enviando notificações para esse bloco.

Fixando um bloco de caminhão de comida

Fixar blocos permite que nosso aplicativo dê acesso direto para um usuário ao conteúdo específico de nosso aplicativo a partir da tela inicial. Blocos secundários podem ser usados para iniciar nosso aplicativo diretamente na parte do aplicativo que lida com o caminhão de comida que um usuário fixou.

Blocos fixos podem ser criados apenas a partir de um aplicativo. Os usuários esperam poder fixar blocos chamando a barra de aplicativos. A barra de aplicativos inclui um ícone de pino padrão para indicar que os usuários podem fixar o conteúdo no modo de exibição.

Quando o usuário toca no botão de pino, aparece um submenu, mostrando uma visualização do bloco a ser fixado:

Submenu com a imagem do Caminhão Nom Nom Barbecue e o botão: Fixar na tela inicial

Agora precisamos:

  1. Adicionar uma barra de aplicativos ao aplicativo, incluindo o ícone de pino das ações “Fixar na tela inicial” e “Desafixar da tela inicial”
  2. Implementar um manipulador de eventos no clique do botão fixar/desafixar da barra de aplicativos
  3. Adicionar a lógica específica do aplicativo para fixar o novo bloco em resposta a uma ação fixar/desafixar

Não observaremos as duas primeiras etapas para a criação da barra de aplicativos de forma que possamos focalizar a fixação dos blocos em si. Você pode encontrar os detalhes de como implementar a barra de aplicativos aqui:

Na etapa 3, nosso aplicativo cria o bloco secundário, configurando algumas propriedades.

JavaScript:

 // Keep track of your secondary tiles with a unique ID   
var nomNomTile = "SecondaryTile.NomNom";

// Set properties on the tile
var logo = new Windows.Foundation.Uri("ms-appx:///images/NomNomTruck-Logo.png");
var smallLogo = new Windows.Foundation.Uri("ms-appx:///images/NomNomTruck-SmallLogo.png");
var wideLogo = new Windows.Foundation.Uri("ms-appx:///images/NomNomTruck-WideLogo.png");
var TileActivationArguments = "TruckName=NomNom";

// Create the secondary tile
var tile = new Windows.UI.StartScreen.SecondaryTile(nomNomTile,
                                                    "Nom Nom",
                                                    "Nom Nom Barbecue Truck",
                                                    TileActivationArguments,
                                                    Windows.UI.StartScreen.TileOptions.sh
owNameOnWideLogo,
                                                    logo,
                                                    wideLogo);

tile.foregroundText = Windows.UI.StartScreen.ForegroundText.light;
tile.smallLogo = smallLogo;

// Request the user’s permission to create the secondary tile
// - we return the promise here, assuming that this code is embedded 
//   in a larger function. 
//   See the Windows 8 SDK Secondary Tiles sample for more info:
//   https://code.msdn.microsoft.com/windowsapps/Secondary-Tiles-Sample-edf2a178
return new WinJS.Promise(function (complete, error, progress) {
  tile.requestCreateAsync().then(function (isCreated) {
    if (isCreated) {
      complete(true);
    } else {
      complete(false);
    }
  });
});

C#:

 // Keep track of your secondary tiles with a unique ID   
const string nomNomTile = "SecondaryTile.NomNom"; 

// Set properties on the tile
Uri logo = new Uri("ms-appx:///images/NomNomTruck-Logo.png");
Uri smallLogo = new Uri("ms-appx:///images/NomNomTruck-SmallLogo.png");
Uri wideLogo = new Uri("ms-appx:///images/NomNomTruck-WideLogo.png");
string tileActivationArguments = "TruckName=NomNom";

// Create the secondary tile object
SecondaryTile secondaryTile = new SecondaryTile(nomNomTile,
                                                "Nom Nom",
                                                "Nom Nom Barbecue Truck",
                                                tileActivationArguments,
                                                Windows.UI.StartScreen.TileOptions.ShowNa
meOnWideLogo,
                                                logo,
                                                wideLogo);

secondaryTile.ForegroundText = Windows.UI.StartScreen.ForegroundText.Light;
secondaryTile.SmallLogo = smallLogo;

// Request the user’s permission to create the secondary tile
// - this code assumes that this code was called within an event handler which has
//   a ‘sender’ parameter.
//   See the Windows 8 SDK Secondary Tiles sample for more info:
//   https://code.msdn.microsoft.com/windowsapps/Secondary-Tiles-Sample-edf2a178
await secondaryTile.RequestCreateForSelectionAsync(MainPage.GetElementRect((FrameworkElement)se
nder), Windows.UI.Popups.Placement.Right);

Para obter mais informações sobre como fixar blocos secundários, consulte Diretrizes e lista de verificação de blocos secundários e Exemplo do Consumer Preview de blocos secundários.

Usando notificações locais para atualizar o bloco fixo

O bloco fixo é um bloco adicional de nosso aplicativo a ser atualizado na tela inicial. Atualizar esse bloco não é diferente de atualizar o bloco principal do aplicativo. Neste aplicativo, eu uso as APIs de notificação local para atualizar os blocos secundários enquanto o aplicativo está em execução, em vez de usar um dos mecanismos de atualização na nuvem. Eu mostro as notificações locais aqui para poder demonstrar como elas funcionam. Você pode atualizar a partir da nuvem de uma forma semelhante. Também é possível implementar cenário de sondagem nesse aplicativo.

No código desta seção, eu uso a biblioteca NotificationsExtensions incluída no Exemplo de notificações e blocos de aplicativo do SDK do Windows 8. Você pode incluir essa biblioteca nos projetos de seu aplicativo para tornar mais fácil a atualização local dos blocos. A biblioteca fornece um modelo de objeto sobre as APIs de atualização de blocos do Windows que permite que você evite a manipulação do XML a partir de seus aplicativos JavaScript, C# e C++. Ele também facilita o desenvolvimento por fornecer IntelliSense.

Usando as APIs de notificação local, posso atualizar o bloco a qualquer momento em que o aplicativo esteja em execução. Em relação aos blocos fixos do caminhão de comida, quero atualizar o bloco com quaisquer transações que o caminhão de comida em particular ofereça, toda vez que um usuário inicia o aplicativo.

Enumerando blocos secundários

Como um usuário pode desafixar os blocos secundários da tela inicial enquanto o nosso aplicativo não está em execução, a primeira coisa que o aplicativo precisa fazer durante a inicialização é procurar por seus blocos secundários fixados no momento. Cada bloco enumerado contém uma tileId,que o identifica de forma exclusiva. Como o aplicativo define a tileId quando é criado, podemos usar a ID para saber como atualizar cada bloco que encontrarmos. Eis aqui como fazê-lo:

JavaScript:

 // Get secondary tile ids
Windows.UI.StartScreen.SecondaryTile.findAllAsync().done(function (tiles) {
  if (tiles) {
    tiles.forEach(function (tile) {
      switch (tile.tileId) {
        case nomNomTile:
          updateNomNomTruck();
          break;
        // add cases for all the food trucks this app supports
        default:
          break;
      }
    });
  }
});

C#:

 // Get secondary tile ids
IReadOnlyList<SecondaryTile> tilelist = await 
Windows.UI.StartScreen.SecondaryTile.FindAllAsync();

foreach (var tile in tilelist)
{
  switch (tile.TileId)
  {
    case nomNomTile:
      updateNomNomTruck();
      break;
    // add cases for all the food trucks this app supports
    default:
      break;
  }
}

Atualizações locais

Em relação a cada um dos blocos do caminhão de comida, quero atualizar o bloco com as informações atuais sobre onde esse caminhão está no dia. Se eu estivesse usando atualizações locais para atualizar o bloco principal, eu não precisaria enumerar os blocos secundários primeiro porque a ação padrão na API é atualizar o bloco principal do aplicativo de chamada. Apenas para lembrá-lo, eis aqui a aparência dos blocos do aplicativo que observamos na Parte 1.

Imagem de carne em um grelha, logotipo do caminhão e texto atualizado: Caminhão Nom Nom Barbecue, Washer Ave e 3rd até 3    Imagem de carne em um grelha, logotipo do caminhão e texto atualizado: Nom Nom @ Washer Ave e 3rd até 3

Aqui está a função que chamamos em nossa enumeração, que realmente envia a notificação para o bloco secundário.

JavaScript:       

 function updateNomNomTruck() {
  // Business logic for retrieving Nom Nom BBQ truck deals
  // ...
  var result = "Washer Ave and 3rd until 3";

  // We can send a notification only for a tile that is pinned. 
  // Lets make sure the tile is pinned before we try to send the notification.
  if (Windows.UI.StartScreen.SecondaryTile.exists(nomNomTile)) {

    // Construct the wide template
    var wideTile = 
NotificationsExtensions.TileContent.TileContentFactory.createTileWideImageAndText02();
    wideTile.image.src = "https://www.contoso.com/foodtrucks/nomnombbq.png";
    wideTile.textCaption1.text = "Nom Nom Barbecue Truck";
    wideTile.textCaption2.text = result;

    // Construct the square template
    var squareTile = 
NotificationsExtensions.TileContent.TileContentFactory.createTileSquarePeekImageAndText04
();
    squareTile.image.src = "https://www.contoso.com/foodtrucks/nomnombbq.png";
    squareTile.textBodyWrap.text = "Nom Nom @ " + result;

    // Attach the square template to the notification
    wideTile.squareContent = squareTile;

    // send the notification to the secondary tile
    Windows.UI.Notifications.TileUpdateManager.createTileUpdaterForSecondaryTile(nomNomTi
le).update(wideTile.createNotification());
  }
}

C#:    

 private void updateNomNomTruck()
{
  // Business logic for retrieving Nom Nom BBQ truck deals
  // ...
  string result = "Washer Ave and 3rd until 3";

  // We can send a notification only for a tile that is pinned. 
  // Lets make sure the tile is pinned before we try to send the notification.
  if (Windows.UI.StartScreen.SecondaryTile.Exists(nomNomTile))
  {

    // Construct the wide template
    NotificationsExtensions.TileContent.ITileWideImageAndText02 wideTile = 
NotificationsExtensions.TileContent.TileContentFactory.CreateTileWideImageAndText02();
    wideTile.Image.Src = "https://www.contoso.com/foodtrucks/nomnombbq.png";
    wideTile.TextCaption1.Text = "Nom Nom Barbecue Truck";
    wideTile.TextCaption2.Text = result;

    // Construct the square template
    NotificationsExtensions.TileContent.ITileSquarePeekImageAndText04 squareTile = 
NotificationsExtensions.TileContent.TileContentFactory.CreateTileSquarePeekImageAndText04();
    squareTile.Image.Src = "https://www.contoso.com/foodtrucks/nomnombbq.png";
    squareTile.TextBodyWrap.Text = "Nom Nom @ " + result;

    // Attach the square template to the notification
    wideTile.SquareContent = squareTile;

    // Send the notification to the secondary tile
    Windows.UI.Notifications.TileUpdateManager.CreateTileUpdaterForSecondaryTile(nomNomTi
le).Update(wideTile.CreateNotification());
  }
}

Como uso a biblioteca NotificationsExtensions, não preciso manipular o XML em meu código local. Em vez disso, uso o modelo de objeto que é fornecido por NotificationsExtensions, que me permite usar IntelliSense para descobrir as propriedades diferentes de cada modelo de notificação.

O XML da atualização do bloco se parece com este:

 <?xml version="1.0" encoding="utf-8" ?>
<tile>
  <visual>
    <binding template="TileWideImageAndText02">
      <image id="1" src="https://www.contoso.com/foodtrucks/nomnombbq.png"/>
      <text id="1">Nom Nom Barbecue Truck</text>
      <text id="1">Washer Ave and 3rd until 3</text>
    </binding>
    <binding template="TileSquarePeekImageAndText04">
      <image id="1" src=" https://www.contoso.com/foodtrucks/nomnombbq-square.png"/>
      <text id="1">Nom Nom @ Washer Ave and 3rd until 3</text>
    </binding>
  </visual>
</tile>

Se você usa a biblioteca NotificationsExtensions, poderá usar IntelliSense para descobrir as propriedades dos modelos de bloco, o que torna o desenvolvimento de atualizações de bloco locais muito mais rápido. Espero que você ache a NotificationsExtensions útil em seus projetos de JavaScript, C# e C++.

Conclusão

Quando os usuários veem coisas divertidas e interessantes em seus blocos, é mais provável que eles iniciem seu aplicativo para saber mais sobre ele. Espero que esta postagem o tenha inspirado a pensar sobre como adicionar um bloco dinâmico e mostrar o melhor dos seus aplicativos. Se você estiver interessado em aprender mais sobre notificações, consulte Visão geral de blocos e notificações e Escolhendo um método de entrega de notificação no Centro de Desenvolvimento.

-- Kevin Michael Woley, gerente de programas, Windows

Esta postagem teve a participação de um grupo de pessoas. Agradeço a Tyler Donahue, Daniel Oliver e Jon Galloway por suas contribuições.