Novidades da Web no Google Play

Publicado em 2 de dezembro de 2020

Desde que a Atividade confiável na Web foi introduzida, a equipe do Chrome facilitou o uso com o Bubblewrap. Adicionamos outros recursos, como a integração do Google Play Faturamento, e ativamos o recurso para que ele funcione em mais plataformas, como o ChromeOS.

Recursos do Bubblewrap e da Atividade Confiável na Web

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

Fluxo de configuração simplificado

Antes, o uso do Bubblewrap exigia a configuração manual do Kit de desenvolvimento Java e do SDK do Android, que são propensos a erros. Agora, a ferramenta oferece o download automático das dependências externas quando é executada pela primeira vez.

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

Assistente melhorado

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 mudar esses valores ao criar um novo projeto, mas antes o significado de cada campo não estava claro. As caixas de diálogo de inicialização foram recriadas com descrições e validação melhores para cada campo de entrada.

Mostrar suporte a 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 no 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 no momento. 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 bubblewrap init.

Saída de AppBundles

Os App Bundles são um formato de publicação para apps que delega a geração e a assinatura finais do APK ao Google Play. Na prática, isso permite que arquivos menores sejam enviados aos usuários ao fazer o download do app na loja.

O Bubblewrap agora empacota o aplicativo como um pacote de app, em um arquivo chamado app-release-bundle.aab. Use esse formato ao publicar apps na Play Store, porque ele é necessário para a loja a partir de 2021.

Delegação de geolocalização

Os usuários esperam que os aplicativos instalados nos dispositivos se comportem de maneira consistente, independente da tecnologia. Quando usada em uma atividade da Web confiável, a permissão GeoLocation agora pode ser delegada ao sistema operacional. Quando ativada, os usuários veem as mesmas caixas de diálogo dos apps criados com Kotlin ou Java e encontram controles para gerenciar a permissão no mesmo lugar.

O recurso pode ser adicionado pelo Bubblewrap e, como ele adiciona dependências extras ao projeto do Android, só ative-o 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 frequentemente preferem aplicativos menores. Os aplicativos que usam a Atividade confiável na Web produzem binários pequenos, o que reduz a ansiedade desses usuários.

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

Como atualizar um app

Um aplicativo gerado pelo Bubblewrap é composto por um aplicativo da Web e um wrapper Android leve que abre o PWA. Embora o PWA aberto em uma Atividade Confiável na Web siga os mesmos ciclos de atualização de qualquer app da Web, o wrapper nativo pode e deve ser atualizado.

Atualize o app para garantir que ele esteja usando a versão mais recente do wrapper, com as correções de bugs e recursos mais recentes. Com a versão mais recente do Bubblewrap instalada, o comando update aplica a versão mais recente do wrapper a um projeto existente:

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

Outra razão para atualizar esses aplicativos é garantir que as mudanças no manifesto da Web sejam aplicadas ao aplicativo. Use o novo comando merge para isso:

bubblewrap merge
bubblewrap update
bubblewrap build

Atualizações nos critérios de qualidade

O Chrome 86 introduziu mudanças nos critérios de qualidade das atividades confiáveis da Web, que são explicados completamente em Mudanças nos critérios de qualidade para PWAs usando atividades confiáveis da Web.

Em resumo, é necessário garantir que seus aplicativos lidem com os seguintes cenários para evitar falhas:

  • Falha na verificação de links de recursos digitais no lançamento 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 de links de recursos digitais, 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 o modelo padrão ao usar service workers. Como alternativa, use um plug-in do Workbox para lidar com esses 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,
    });
  }
}