Novidades da Web no Google Play

Desde que a Atividade confiável na Web foi lançada no ano passado, a equipe do Chrome continua trabalhando no produto, facilitando o uso com o Bubblewrap, adicionando novos recursos, como a futura integração do Google Play Faturamento, e permitindo que ele funcione em mais plataformas, como o ChromeOS. Este artigo resume as atualizações mais recentes e futuras relacionadas à Atividade confiável na Web.

Novos recursos de Bubblewrap e Atividade confiável na Web

O Bubblewrap ajuda a criar apps que iniciam seus PWAs dentro de uma atividade confiável na Web, sem exigir conhecimento de ferramentas específicas da plataforma.

Fluxo de configuração simplificado

Anteriormente, o uso do Bubblewrap exigia a configuração manual do Kit de desenvolvimento em Java e do SDK do Android, ambos propensos a erros. A ferramenta agora oferece o download automático das dependências externas quando executada pela primeira vez.

Você ainda pode optar por usar uma instalação existente das dependências, se preferir fazer isso, e o novo comando doctor ajuda a encontrar problemas e recomenda correções para a configuração, que agora pode ser atualizada na linha de comando usando o comando updateConfig.

Assistente aprimorado

Ao criar um projeto com init, o Bubblewrap precisa de informações para gerar o app Android. A ferramenta extrai valores do manifesto do app da Web e fornece padrões sempre que possível.

É possível alterar esses valores ao criar um novo projeto, mas anteriormente o significado de cada campo não era claro. As caixas de diálogo de inicialização foram recriadas com descrições e validação melhores para cada campo de entrada.

tela: suporte para tela cheia e orientação

Em alguns casos, talvez você queira que o aplicativo use o máximo possível da tela. Ao criar PWAs, isso é implementado definindo o campo display do manifesto do app da Web como fullscreen.

Quando o Bubblewrap detecta a opção de tela cheia no manifesto do app da Web, ele configura o aplicativo Android para ser iniciado em tela cheia, ou modo imersivo, em termos específicos do Android.

O campo orientation do manifesto do app da Web define se o aplicativo precisa ser iniciado no modo retrato, paisagem ou na orientação que o dispositivo está usando. O Bubblewrap agora lê o campo de manifesto do app da Web e o usa como padrão ao criar o app Android.

É possível personalizar as duas configurações como parte do fluxo de bubblewrap init.

Saída AppBundles

Os pacotes de app são um formato de publicação para apps que delegam a geração e a assinatura finais do APK no Google Play. Na prática, isso permite que arquivos menores sejam exibidos aos usuários quando fizerem o download do app da loja.

O Bubblewrap agora empacota o aplicativo como um pacote de apps, em um arquivo chamado app-release-bundle.aab. Você precisa dar preferência a esse formato ao publicar apps na Play Store, já que ele será exigido pela loja a partir do segundo semestre de 2021.

Delegação de geolocalização

Os usuários esperam que os aplicativos instalados no dispositivo tenham um comportamento consistente, independentemente da tecnologia. Quando usada em uma Atividade confiável na Web, a permissão GeoLocation agora pode ser delegada ao sistema operacional e, quando ativada, os usuários verão as mesmas caixas de diálogo que apps criados com Kotlin ou Java, além de encontrar controles para gerenciar a permissão no mesmo lugar.

O recurso pode ser adicionado via Bubblewrap e, como ele adiciona dependências extras ao projeto Android, você só precisa ativá-lo quando o app da Web estiver usando a permissão de geolocalização.

Binários otimizados

Dispositivos com armazenamento limitado são comuns em determinadas áreas do mundo, e os proprietários desses dispositivos geralmente preferem apps menores. Os aplicativos que usam o Trusted Web Activity produzem pequenos binários, o que remove um pouco da ansiedade desses usuários.

O Bubblewrap foi otimizado reduzindo a lista de bibliotecas Android necessárias, resultando em binários gerados com 800 mil menos arquivos. Na prática, isso é menos da metade do tamanho médio gerado pelas versões anteriores. Para aproveitar os binários menores, você só precisa atualizar o app usando a versão mais recente do Bubblewrap.

Como atualizar um app existente

Um aplicativo gerado pelo Bubblewrap é composto por um aplicativo da Web e um wrapper leve do Android que abre o PWA. Mesmo que o PWA seja aberto dentro de uma atividade confiável na Web siga os mesmos ciclos de atualização de qualquer app da Web, o wrapper nativo pode e precisa ser atualizado.

Atualize seu app para garantir que ele use a versão mais recente do wrapper, com as últimas correções de bugs e os recursos mais recentes. Com a versão mais recente do Bubblewrap instalada, o comando update vai aplicar a versão mais recente do wrapper a um projeto existente:

npm update -g @bubblewrap/cli
bubblewrap update
bubblewrap build

Outro motivo para atualizar esses aplicativos é garantir que as mudanças no manifesto da Web sejam aplicadas a eles. Para isso, use o novo comando merge:

bubblewrap merge
bubblewrap update
bubblewrap build

Atualizações nos critérios de qualidade

O Chrome 86 introduziu mudanças nos critérios de qualidade da Atividade na Web confiável, que são explicados na íntegra em Mudanças nos critérios de qualidade para PWAs que usam atividades na Web confiáveis.

Um resumo rápido é que você precisa garantir que seus aplicativos lidem com os seguintes cenários para evitar falhas:

  • Falha ao verificar o Digital Asset Links na inicialização do aplicativo
  • Falha ao retornar HTTP 200 para uma solicitação de recurso de rede off-line
  • Retorno de um erro HTTP 404 ou 5xx no aplicativo.

Além de garantir que o aplicativo passe na validação do Digital Asset Links, os cenários restantes podem ser processados por um service worker:

self.addEventListener('fetch', event => {
  event.respondWith((async () => {
    try {
      return await fetchAndHandleError(event.request);
    } catch {
      // Failed to load from the network. User is offline or the response
      // has a status code that triggers the Quality Criteria.
      // Try loading from cache.
      const cachedResponse = await caches.match(event.request);
      if (cachedResponse) {
        return cachedResponse;
      }
      // Response was not found on the cache. Send the error / offline
      // page. OFFLINE_PAGE should be pre-cached when the service worker
      // is activated.
      return await caches.match(OFFLINE_PAGE);
    }
  })());
});

async function fetchAndHandleError(request) {
  const cache = await caches.open(RUNTIME_CACHE);
  const response = await fetch(request);

  // Throw an error if the response returns one of the status
  // that trigger the Quality Criteria.
  if (response.status === 404 ||
      response.status >= 500 && response.status < 600) {
    throw new Error(`Server responded with status: ${response.status}`);
  }

  // Cache the response if the request is successful.
  cache.put(request, response.clone());
  return response;
}

O Workbox incorpora práticas recomendadas e remove código boilerplate ao usar service workers. Outra opção é usar um plug-in do Workbox para processar estes cenários:

export class FallbackOnErrorPlugin {
  constructor(offlineFallbackUrl, notFoundFallbackUrl, serverErrorFallbackUrl) {
    this.notFoundFallbackUrl = notFoundFallbackUrl;
    this.offlineFallbackUrl = offlineFallbackUrl;
    this.serverErrorFallbackUrl = serverErrorFallbackUrl;
  }

  checkTrustedWebActivityCrash(response) {
    if (response.status === 404 || response.status >= 500 && response.status <= 600) {
      const type = response.status === 404 ? 'E_NOT_FOUND' : 'E_SERVER_ERROR';
      const error = new Error(`Invalid response status (${response.status})`);
      error.type = type;
      throw error;
    }
  }

  // This is called whenever there's a network response,
  // but we want special behavior for 404 and 5**.
  fetchDidSucceed({response}) {
    // Cause a crash if this is a Trusted Web Activity crash.
    this.checkTrustedWebActivityCrash(response);

    // If it's a good response, it can be used as-is.
    return response;
  }

  // This callback is new in Workbox v6, and is triggered whenever
  // an error (including a NetworkError) is thrown when a handler runs.
  handlerDidError(details) {
    let fallbackURL;
    switch (details.error.details.error.type) {
      case 'E_NOT_FOUND': fallbackURL = this.notFoundFallbackUrl; break;
      case 'E_SERVER_ERROR': fallbackURL = this.serverErrorFallbackUrl; break;
      default: fallbackURL = this.offlineFallbackUrl;
    }

    return caches.match(fallbackURL, {
      // Use ignoreSearch as a shortcut to work with precached URLs
      // that have _WB_REVISION parameters.
      ignoreSearch: true,
    });
  }
}

Google Play Faturamento

Além de permitir que seu app venda assinaturas e produtos e softwares digitais na Play Store, o Google Play Faturamento oferece ferramentas para gerenciar seu catálogo, preços e assinaturas, relatórios úteis e um fluxo de finalização de compra com a tecnologia da Play Store que já é conhecida pelos seus usuários. Ele também é um requisito para apps publicados na Play Store que vendem produtos e softwares digitais.

O Chrome 88 será lançado com um teste de origem no Android que permite a integração de Atividades confiáveis na Web, a API Payment Request e a API Digital Goods para implementar fluxos de compra pelo Google Play Faturamento. Esperamos que esse teste de origem também esteja disponível para o ChromeOS na versão 89.

Importante:a API Google Play Billing tem a própria terminologia e inclui componentes de cliente e back-end. Esta seção aborda apenas uma pequena parte da API específica para usar a API Digital Goods e a Atividade na Web confiável. Leia a documentação do Google Play Faturamento e entenda os conceitos relacionados antes de fazer a integração a um aplicativo de produção.

O fluxo básico

Menu do Play Console

Para fornecer produtos e softwares digitais pela Play Store, você precisa configurar seu catálogo na Play Store, além de conectar a Play Store como uma forma de pagamento do seu PWA.

Quando estiver tudo pronto para configurar o catálogo, encontre a seção "Produtos" no menu do lado esquerdo do Play Console:

Aqui você encontra a opção de conferir seus produtos e assinaturas no app existentes e o botão "Criar" para adicionar novos.

Produtos no app

Detalhes do produto

Para criar um novo produto no app, você vai precisar de um ID, um nome, uma descrição e um preço do produto. É importante criar IDs de produtos significativos e fáceis de lembrar. Você vai precisar deles mais tarde, e os IDs não poderão ser alterados depois de criados.

Ao criar assinaturas, você também precisará especificar um período de faturamento. Você tem a opção de listar os benefícios da assinatura e adicionar recursos, como um teste sem custo financeiro, um preço inicial, um período de carência e uma opção de renovação da assinatura.

Depois de criar os produtos, ative-os para disponibilizá-los no app.

Se preferir, adicione seus produtos usando a API Play Developers.

Depois de configurar o catálogo, a próxima etapa é configurar o fluxo de finalização de compra do PWA. Você vai usar uma combinação da API Digital Goods e da API Payment Request para isso.

Buscar o preço de um produto com a API Digital Goods

Ao usar o Google Play Faturamento, garanta que o preço exibido aos usuários corresponda ao preço na página "Detalhes do app". Não é possível manter esses preços sincronizados manualmente. Por isso, a API Digital Goods oferece uma maneira de o aplicativo da Web consultar os preços ao provedor de pagamento:

// The SKU for the product, as defined in the Play Store interface
async function populatePrice(sku) {
  try {
    // Check if the Digital Goods API is supported by the browser.
    if (window.getDigitalGoodsService) {
      // The Digital Goods API can be supported by other Payments provider.
      // In this case, we're retrieving the Google Play Billing provider.
      const service =
          await window.getDigitalGoodsService("https://play.google.com/billing");

      // Fetch product details using the `getDetails()` method.
      const details = await service.getDetails([sku]);

      if (details.length === 0) {
        console.log(`Could not get SKU: "${sku}".`);
        return false;
      }

      // The details will contain both the price and the currenncy.
      item = details[0];
      const value = item.price.value;
      const currency = item.price.currency;

      const formattedPrice = new Intl.NumberFormat(navigator.language, {
        style: 'currency', currency: currency }).format(value);

      // Display the price to the user.
      document.getElementById("price").innerHTML = formattedPrice;
    } else {
      console.error("Could not get price for SKU \"" + sku + "\".");
    }
  } catch (error) {
    console.log(error);
  }
  return false;
}

Para detectar o suporte à API Digital Goods, verifique se getDigitalGoodsService() está disponível no objeto window.

Em seguida, chame window.getDigitalGoodsService() com o identificador do Google Play Faturamento como parâmetro. Isso retornará uma instância de serviço para o Google Play Faturamento, e outros fornecedores poderão implementar o suporte para a API Digital Goods e terão identificadores diferentes.

Por fim, chame getDetails() na referência ao objeto do Google Play Faturamento transmitindo a SKU do item como um parâmetro. O método retornará um objeto de detalhes que contém o preço e a moeda do item que pode ser exibido ao usuário.

Iniciar o fluxo de compra

A API Payment Request permite fluxos de compra na Web e também é usada para a integração com o Google Play Faturamento. Confira Como a API Payment Request funciona para saber mais se você é iniciante na API Payment Request.

Para usar a API com o Google Play Faturamento, você precisará adicionar um instrumento de pagamento que tenha um metod compatível chamado https://play.google.com/billing e adicionar o SKU como parte dos dados do instrumento:

const supportedInstruments = [{
  supportedMethods: "https://play.google.com/billing",
  data: {
    sku: sku
  }
}];

Em seguida, crie um objeto PaymentRequest e use a API normalmente.

const request = new PaymentRequest(supportedInstruments, details);

Confirmar a compra

Depois que a transação for concluída, você precisará usar a API Digital Goods para confirmar o pagamento. O objeto de resposta do PaymentRequest conterá um token que você usará para confirmar a transação:

const response = await request.show();
const token = response.details.token;
const service =
          await window.getDigitalGoodsService("https://play.google.com/billing");
await service.acknowledge(token, 'onetime');

As APIs Digital Goods e Payment Request não têm conhecimento sobre a identidade do usuário. Como resultado, cabe a você associar a compra ao usuário no back-end e garantir que ele tenha acesso aos itens comprados. Ao associar a compra a um usuário, salve o token de compra, porque ele pode ser necessário para verificar se a compra foi cancelada ou reembolsada, ou se uma assinatura ainda está ativa. Confira a API Real Time Developer Notifications e a API Google Play Developer, que fornecem endpoints para processar esses casos no seu back-end.

Verificar se há direitos

É possível que um usuário tenha resgatado um código promocional ou já tenha uma assinatura do seu produto. Para validar se o usuário tem os direitos apropriados, chame o comando listPurchases() no serviço de produtos e softwares digitais. Isso retornará todas as compras que o cliente fez no app. Esse também seria o lugar para confirmar todas as compras não confirmadas para garantir que o usuário resgate corretamente os direitos.

const purchases = await itemService.listPurchases();
for (p of purchases) {
  if (!p.acknowledged) {
    await itemService.acknowledge(p.purchaseToken, 'onetime');
  }
}

Fazer upload para a Play Store do ChromeOS

Atividades confiáveis na Web também estão disponíveis desde o Chrome 85 na Play Store do ChromeOS. O processo para listar seu app na loja é o mesmo para o ChromeOS e para o Android.

Depois de criar o pacote de apps, o Play Console vai guiar você pelas etapas necessárias para listar o app na Play Store. Na documentação do Play Console, você pode encontrar ajuda para criar a página "Detalhes do app", gerenciar arquivos .apk e outras configurações, bem como instruções para testar e lançar o app com segurança.

Para restringir seu aplicativo apenas a Chromebooks, adicione a sinalização --chromeosonly ao inicializar o aplicativo em Bubblewrap:

bubblewrap init --manifest="https://example.com/manifest.json" --chromeosonly

Ao criar seu aplicativo manualmente, sem o Bubblewrap, adicione uma sinalização uses-feature ao manifesto do Android:

<uses-feature  android:name="org.chromium.arc" android:required="true"/>

Se a página de detalhes for compartilhada com um app Android, a versão do pacote exclusiva para o ChromeOS sempre precisará ser mais recente do que a do pacote do app Android. Você pode configurar a versão do pacote do ChromeOS com um número muito maior do que a versão do Android para não precisar atualizar as duas versões a cada lançamento.