Configurar, implementar e confirmar Links do app Android

1. Antes de começar

O principal objetivo de um usuário ao seguir um link direto é acessar o conteúdo que ele quer conferir. Os links diretos têm todas as funcionalidades para que os usuários consigam fazer isso. O Android processa estes tipos de links:

  • Links diretos: são URIs de qualquer esquema que levam os usuários diretamente a uma parte específica do seu app.
  • Links da web: são links diretos com o esquema HTTP e HTTPS.
  • Links do app Android: são links da web com esquemas HTTP e HTTPS que contêm o atributo android:autoVerify.

Para informações mais detalhadas sobre links diretos, da web e Links do app Android, consulte a documentação do Android e o curso intensivo no YouTube (vídeo em inglês) e o artigo no Medium (link em inglês).

Se você já conhece todos os detalhes técnicos, confira uma implementação rápida na postagem de blog (link em inglês) complementar para conseguir configurar o recurso em apenas algumas etapas.

Objetivo do codelab

Este codelab mostra as práticas recomendadas para a configuração, implementação e verificação de um app com os Links do app Android.

Um dos benefícios dos Links do app Android é que eles são seguros, o que significa que nenhum app não autorizado pode processar seus links. O SO Android precisa verificar os links com um site de sua propriedade para qualificá-los como Links do app Android. Esse processo é chamado associação de sites.

Este codelab tem como foco os desenvolvedores que têm um site e um app Android. Os Links do app Android fornecem uma experiência melhor do usuário, permitindo uma integração total entre seu app e seu site.

Pré-requisitos

O que você vai aprender

  • Práticas recomendadas sobre a criação de URLs para Links do app Android.
  • Como configurar todos os tipos de links diretos em um app Android.
  • Sobre caracteres curinga de caminhos (path, pathPrefix, pathPattern, pathAdvancePattern).
  • Sobre o processo de verificação dos Links do app Android, que inclui o upload do arquivo do Digital Asset Links (DAL) do Google, o processo de verificação manual dos Links do app Android e o painel de links diretos do Google Play Console.
  • Como criar um app Android com vários restaurantes em diferentes locais.

Aparência final do aplicativo da Web de restaurantes. Aparência final do app Android de restaurantes.

O que é necessário

  • Android Studio Dolphin (2021.3.1) ou uma versão mais recente.
  • Um domínio para hospedar arquivos do Google Digital Asset Links (DAL). Opcional: confira esta postagem de blog (em inglês) que ajuda a configurar esse domínio rapidamente.
  • Opcional: uma conta de desenvolvedor do Google Play Console, o que permite uma outra abordagem para depurar a configuração dos Links do app Android.

2. Configurar o código

Criar um aplicativo vazio no Compose

Para começar um projeto no Compose, siga estas etapas:

  1. No Android Studio, selecione File > New > New Project.

Menu "File" e seleção do próximo caminho: "New" para "New Project".

  1. Selecione a opção Empty Compose Activity nos modelos disponíveis.

Modelo de projeto "New" do Android Studio com a opção "Empty Compose Activity" selecionada.

  1. Clique em Next e configure seu projeto com o nome Deep Links Basics. Para o SDK mínimo escolha pelo menos o nível 21 da API, que é o mínimo com suporte do Compose.

Modelos da configuração do novo projeto do Android Studio com estes valores e opções de menu: "Deep Links Basics" para o nome. "com.devrel.deeplinksbasics" para o nome do pacote. O local para salvar os itens tem o valor padrão. "Kotlin" para a linguagem. API 21 para o SDK mínimo.

  1. Clique em Finish e aguarde até que seu projeto seja gerado.
  2. Inicie o aplicativo. Verifique se o app é executado. Uma tela em branco com a mensagem Hello Android! deve aparecer.

Tela vazia do app Android no Compose mostrando o texto "Hello Android".

Solução para o codelab

O código da solução deste codelab está disponível no GitHub:

git clone https://github.com/android/deep-links

Se preferir, faça o download do repositório como um arquivo ZIP:

Primeiro, acesse o diretório deep-links-introduction. O app vai estar no diretório solution. Recomendamos que você siga todas as etapas do codelab no seu próprio ritmo e consulte a solução só caso precise de ajuda. Durante o codelab, vamos ensinar os snippets de código que precisam ser adicionados ao projeto.

3. Analisar o design de URLs orientados por links diretos

Design da API RESTful

Os links são uma parte essencial do desenvolvimento da Web, e a criação de links tem passado por inúmeras iterações, resultando em vários padrões. Vale a pena conferir e aplicar os padrões de criação de links para desenvolvimento da Web, o que os torna mais fáceis de usar e manter.

Um desses padrões é o REST, que significa Representational State Transfer (Transferência de Estado Representacional, em inglês), uma arquitetura que geralmente é usada para criar APIs para serviços da Web. A OpenAPI (link em inglês) é uma iniciativa que padroniza APIs REST. Você também pode usar a REST para criar URLs de links diretos.

Não estamos criando um serviço da Web. Esta seção foca apenas na criação de URLs.

Como criar os URLs

Primeiro, analise os URLs resultantes no seu site e entenda o que eles representam no app Android:

  • /restaurants lista todos os restaurantes que você gerencia.
  • /restaurants/:restaurantName mostra todos os detalhes de um restaurante específico.
  • /restaurants/:restaurantName/orders mostra os pedidos de um restaurante.
  • /restaurants/:restaurantName/orders/:orderNumber mostra um pedido específico de um restaurante.
  • /restaurants/:restaurantName/orders/latest mostra o pedido mais recente de um restaurante.

Por que a criação de um URL é importante

O Android possui filtros de intent que processam ações de outro componente do app e também são usados para capturar URLs. Quando você define um filtro de intent para capturar um URL, precisa seguir uma estrutura que conte com prefixos de caminhos e caracteres curinga claros. Confira um exemplo de como isso é estruturado usando um URL já existente no site de um restaurante:

https://example.com/pawtato-3140-Skinner-Hollow-Road

Embora esse URL especifique seu restaurante e a localização dele, o caminho pode criar um problema ao definir um filtro de intent para o Android capturar o URL, porque seu app tem como base diversos URLs de restaurantes como estes:

https://example.com/rawrbucha-2064-carriage-lane

https://example.com/pizzabus-1447-davis-avenue

Ao definir um filtro de intent com um caminho e um caractere curinga para capturar esses URLs, você pode usar algo como https://example.com/*, que funciona no geral. No entanto, você não resolve de fato o problema, porque há outros caminhos para diferentes seções do seu site, como:

Entrega: https://example.com/deliveries

Administrador: https://example.com/admin

Você pode não querer que o Android capture esses URLs, porque alguns deles podem ser internos, mas o filtro de intent definido, https://example.com/*, captura esses e outros caminhos, incluindo URLs inexistentes. E, quando um usuário clica em um deles, o URL é aberto no navegador (com o Android 12 ou mais recente), ou uma caixa de diálogo de desambiguação pode aparecer (com uma versão anterior ao Android 12). Para nosso design, esse não é um comportamento esperado.

Agora o Android oferece prefixos de planos que resolvem esse problema, sem precisar de um novo design de URL. De:

https://example.com/*

Para:

https://example.com/restaurants/*

A adição de uma estrutura de transição hierárquica define claramente seus filtros de intent, e o Android captura o URL que você pedir.

Práticas recomendadas para a criação de URLs

Confira algumas práticas recomendadas pela OpenAPI e aplicadas sob uma perspectiva para links diretos:

  • Concentre o foco do design do URL nas entidades comerciais que ele expõe. Por exemplo, em um e-commerce, pode haver clientes e pedidos. Para o setor de viagens, pode ser passagens e voos. No app e site do seu restaurante, você usará restaurantes e pedidos.
  • A maioria dos nomes dos métodos HTTP (GET, POST, DELETE, PUT) usa verbos que descrevem a solicitação que está sendo feita, mas pode ser confuso usar verbos para endpoints em URLs.
  • Para descrever coleções, use o plural da entidade, por exemplo, /restaurants/:restaurantName. Isso deixa o URL mais fácil de ler e manter. Confira um exemplo com cada um dos métodos HTTP:

GET /restaurants/pawtato

POST /restaurants

DELETE /restaurants

PUT /restaurants/pawtato

Fica mais fácil ler cada URL e entender o que ele faz. Este codelab não aborda o design da API de serviços da Web e o que cada método faz.

  • Use a transição lógica para agrupar URLs que contêm informações relacionadas. Por exemplo, o URL de um dos restaurantes pode ter os pedidos que estão sendo preparados:

/restaurants/1/orders

4. Analisar elementos de dados

O arquivo AndroidManifest.xml é uma parte essencial do Android. Ele descreve as informações do app para as ferramentas de build do Android, para o SO Android e para o Google Play.

No caso de links diretos, você precisa definir um filtro de intent usando três tags principais: <action>, <category> e <data>. Nosso foco principal nesta seção é a tag <data>.

Um elemento <data> informa ao SO Android a estrutura do URL de um link quando um usuário clicar no link. O formato e a estrutura do URL que você pode usar nos filtros de intents são os seguintes:

<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>|<pathAdvancedPattern>|<pathSuffix>]

O Android lê, analisa e reúne todos os elementos <data> de um filtro de intent a serem considerados para todas as variações dos atributos. Por exemplo:

AndroidManifest.xml

<intent-filter>
  ...
  <data android:scheme="http" />
  <data android:scheme="https" />
  <data android:host="example.com" />
  <data android:path="/restaurants" />
  <data android:pathPrefix="/restaurants/orders" />
</intent-filter>

O Android captura os seguintes URLs:

  • http://example.com/restaurants
  • https://example.com/restaurants
  • http://example.com/restaurants/orders/*
  • https://example.com/restaurants/orders/*

Atributos de caminho

path (disponível na API 1)

Este atributo especifica um caminho completo, começando com /, que corresponde ao caminho completo na intent. Por exemplo, android:path="/restaurants/pawtato" só corresponde ao caminho do site /restaurants/pawtato, e se tivermos /restaurant/pawtato, esse URL não será associado, por causa do s que está faltando.

pathPrefix (disponível na API 1)

Esse atributo especifica um caminho parcial que corresponde apenas à parte inicial do caminho da intent. Por exemplo:

android:pathPrefix="/restaurants" vai corresponder aos caminhos de restaurantes /restaurants/pawtato, /restaurants/pizzabus e assim por diante.

pathSuffix (disponível na API 31)

Esse atributo especifica um caminho que corresponde exatamente à parte final do caminho da intent. Por exemplo:

android:pathSuffix="tato" corresponderá a todos os caminhos de restaurantes que terminam como tato, por exemplo, /restaurants/pawtato e /restaurants/corgtato.

pathPattern (disponível na API 1)

Esse atributo especifica um caminho completo que corresponde ao caminho completo com caracteres curinga na intent.

  • Um asterisco (*) corresponde a uma sequência de zero a várias ocorrências do caractere anterior
  • Um ponto seguido por um asterisco (.*) corresponde a qualquer sequência de zero a vários caracteres.

Exemplos:

  • /restaurants/piz*abus: este padrão faz a correspondência ao restaurante "pizzabus", mas também corresponde a restaurantes que tenham zero a vários caracteres z no nome, por exemplo, /restaurants/pizzabus, /restaurants/pizzzabus e /restaurants/pizabus.
  • /restaurants/.*: este padrão corresponde a qualquer nome de restaurante que tenha o caminho /restaurants, por exemplo, /restaurants/pizzabus e /restaurants/pawtato, bem como aqueles que o app não conhece, como /restaurants/wateriehall.

pathAdvancePattern (disponível na API 31)

Esse atributo especifica um caminho completo que corresponde ao caminho completo com padrões do estilo regex:

  • Um ponto (.) faz correspondência com qualquer caractere.
  • Um conjunto de colchetes ([...]) faz a correspondência de uma faixa de caracteres e também oferece suporte ao modificador "not" (^).
  • Um asterisco (*) faz a correspondência do padrão anterior zero ou mais vezes.
  • Um sinal de mais (+) faz a correspondência do padrão anterior uma ou mais vezes.
  • As chaves ({...}) representam quantas correspondências um padrão pode ter.

Esse atributo pode ser considerado uma extensão de pathPattern. Ele oferece mais flexibilidade em relação a quais URLs devem ser associados, por exemplo:

  • /restaurants/[a-zA-Z]*/orders/[0-9]{3} faz a correspondência de qualquer pedido de restaurante que tenha até três dígitos de comprimento.
  • /restaurants/[a-zA-Z]*/orders/latest faz a correspondência do pedido mais recente de qualquer um dos restaurantes do app

5. Criar links diretos e da web

Links diretos com esquemas personalizados são os tipos mais genéricos de links diretos e são os mais fáceis de implementar, mas há alguns problemas. Esses links não podem ser abertos por sites da Web. Qualquer app que declare oferecer suporte a esse esquema no manifesto pode abrir o link.

É possível usar qualquer esquema no elemento <data>. Por exemplo, este codelab usa o URL food://restaurants/keybabs.

  1. No Android Studio, adicione o filtro de intent abaixo ao arquivo de manifesto:

AndroidManifest.xml

<activity ... >
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="food"/>
    <data android:path="/restaurants/keybabs"/>
  </intent-filter>
</activity>
  1. Para conferir se o app pode abrir links com esquemas personalizados, mostre um link na página inicial, adicionando o seguinte à atividade principal:

MainActivity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Receive the intent action and data
        val action: String? = intent?.action;
        val data: Uri? = intent?.data;

        setContent {
            DeepLinksBasicsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    // Add a Column to print a message per line
                    Column {
                        // Print it on the home screen
                        Greeting("Android")
                        Text(text = "Action: $action")
                        Text(text = "Data: $data")
                    }
                }
            }
        }
    }
}
  1. Para testar se a intent é recebida, use o Android Debug Bridge (adb) com este comando:
adb shell am start -W -a android.intent.action.VIEW -d "food://restaurants/keybabs"

Esse comando inicia uma intent com a ação "VIEW" (visualizar) e usa o URL fornecido como dados. Quando você executa este comando, o app é iniciado e recebe a intent. Observe as mudanças feitas nas seções de texto da tela principal. Uma delas mostra a mensagem Hello Android!, a segunda, mostra a ação que a intent chamou, e a terceira o URL que a intent chamou.

Na imagem abaixo, observe que na parte de baixo da seção do Android Studio, o comando adb mencionado foi executado. À direita, o app mostra as informações da intent na tela inicial, o que significa que elas foram recebidas. A tela cheia do Android Studio tem estas guias abertas: "code view", "emulator" e "terminal". A visualização do código mostra o arquivo MainActivity.kt básico. O emulador mostra o campo de texto do link direto, confirmando que ele foi recebido. O terminal mostra o comando adb que acabamos de discutir no codelab.

Links da web são links diretos que usam http e https, em vez de esquemas personalizados.

Para implementar links da web, use o caminho /restaurants/keybabs/order/latest.html, que representa o pedido mais recente recebido no restaurante.

  1. Ajuste o arquivo de manifesto usando o filtro de intent já criado.

AndroidManifest.xml

<intent-filter>
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="food"/>
  <data android:path="/restaurants/keybabs"/>

  <!-- Web link configuration -->
  <data android:scheme="http"/>
  <data android:scheme="https"/>
  <data android:host="sabs-deeplinks-test.web.app"/>
  <data android:path="/restaurants/keybabs/order/latest.html"/>
</intent-filter>

Como ambos os caminhos estão sendo compartilhados (/restaurants/keybabs), uma prática recomendada é colocá-los no mesmo filtro de intent, porque isso facilita a implementação e a leitura do arquivo de manifesto.

  1. Antes de testar o link da web, reinicie o app para que as novas mudanças sejam aplicadas.
  2. Use o mesmo comando adb para iniciar a intent. Porém, vamos atualizar o URL nesse caso.
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/keybabs/orders/latest.html"

Na captura de tela, observe que a intent é recebida e que o navegador da Web é aberto para mostrar o site, um recurso do Android 12 e versões mais recentes. Visualização completa do Android Studio com as guias: "Code view", que mostra o arquivo AndroidManifest.xml com o filtro de intent discutido; "Emulator view", que mostra a página da Web aberta graças aos links da web (a página da Web aponta para o app Restaurant); e "Terminal view", que mostra o comando adb para links da web.

6. Configurar Links do app Android

Esses links oferecem a melhor experiência do usuário, porque quando um usuário clica em um link, ele com certeza é direcionado ao app sem caixas de diálogo de desambiguação. O recurso de Links do app Android foi implementado no Android 6.0 e é o tipo mais específico de link direto. Alguns links da web usam o esquema http/https e o atributo android:autoVerify, que torna o app o gerenciador padrão de qualquer link correspondente. Há duas etapas principais para implementar os Links do app Android:

  1. Atualizar o arquivo de manifesto com o filtro de intent adequado.
  2. Adicionar a associação de sites para verificação.

Atualizar o arquivo de manifesto

  1. Para oferecer suporte aos Links do app Android, no arquivo de manifesto, substitua a configuração antiga por esta:

AndroidManifest.xml

<!-- Replace deep link and web link configuration with this -->
<!-- Please update the host with your own domain -->
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="https"/>
  <data android:host="example.com"/>
  <data android:pathPrefix="/restaurants"/>
</intent-filter>

Este filtro de intent adiciona o atributo android:autoVerify e o define como "true". Dessa forma, o SO Android pode verificar o domínio quando o aplicativo está instalado e a cada nova atualização.

Associação de sites

Para validar um Link do app Android, crie uma associação entre o aplicativo e o site. Um arquivo JSON Google Digital Asset Links (DAL) precisa ser publicado no site para que a validação ocorra.

Google DAL é um protocolo e uma API que define instruções verificáveis sobre outros apps e sites. Neste codelab, você criará uma instrução sobre o app Android no arquivo assetlinks.json. Confira um exemplo:

assetlinks.json

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.devrel.deeplinksbasics",
    "sha256_cert_fingerprints":
   ["B0:4E:29:05:4E:AB:44:C6:9A:CB:D5:89:A3:A8:1C:FF:09:6B:45:00:C5:FD:D1:3E:3E:12:C5:F3:FB:BD:BA:D3"]
  }
}]

Este arquivo pode armazenar uma lista de instruções, mas o exemplo mostra apenas um item. Cada instrução precisa conter os campos abaixo:

  • Relation: descreve uma ou mais relações que estão sendo declaradas sobre o valor desejado.
  • Target. O recurso a que a instrução se aplica. Pode ser um de dois valores disponíveis: web ou android_app.

A propriedade target da instrução do Android contém estes campos:

  • namespace: o android_app de todos os apps Android.
  • package_name: o nome totalmente qualificado do pacote (com.devrel.deeplinksbasics).
  • sha256_cert_fingerprints: a impressão digital do certificado do app. Você aprenderá como gerar esse certificado na próxima seção.

Impressão digital do certificado

Há vários métodos para extrair a impressão digital do certificado. Este codelab usa dois métodos: um para o build de depuração do aplicativo e outro para ajudar a lançar o app na Google Play Store.

Configuração de depuração

Na primeira vez que o Android Studio executa seu projeto, ele assina automaticamente o app com um certificado de depuração. Este certificado fica em $HOME/.android/debug.keystore. Você pode usar um comando do Gradle para extrair essa impressão digital de certificado SHA-256, seguindo estas etapas:

  1. Pressione Control duas vezes. O menu Run anything vai aparecer. Se ele não aparecer, você poderá encontrá-lo no menu do Gradle na barra lateral à direita e, em seguida, clicar no ícone do Gradle.

Guia do menu do Gradle no Android Studio com o ícone do Gradle selecionado.

  1. Digite gradle signingReport e pressione Enter. O comando é executado no console e mostra as informações de impressão digital da variante do app de depuração.

A janela "Terminal" mostra os resultados do relatório de assinaturas do Gradle.

  1. Para concluir a associação do site, copie a impressão digital do certificado SHA-256, atualize o arquivo JSON e faça o upload no seu site, em https://<domain>/.well-know/assetlinks.json. Esta postagem do blog sobre Links do app Android (em inglês) ajudará você com a configuração.
  2. Se o app ainda estiver em execução, pressione Stop para interrompê-lo.
  3. Para iniciar o processo de verificação de novo, remova o app do simulador. No simulador, clique e mantenha pressionado o ícone do app DeepLinksBasics e selecione App Info. Clique em Uninstall e em Confirm no modal. Em seguida, execute o aplicativo para que o Android Studio possa verificar a associação.

f112e0d252c5eb48.gif

  1. Selecione a configuração de execução app. Caso contrário, o relatório de assinaturas do Gradle será executado novamente. Android Studio com o menu de configurações de execução com a opção "app" selecionada.
  2. Reinicie o app e a intent com o URL do Link do app Android:
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/"
  1. Observe que o app é iniciado, e a intent aparece na tela inicial.

Tela inicial do Android Emulator com campos de texto mostrando que o Link do app Android foi implementado.

Parabéns, você acabou de criar seu primeiro Link do app Android!

Configuração de lançamento

Agora, para fazer o upload do seu aplicativo com os Links do app Android na Play Store, você precisa usar um build de lançamento com a impressão digital do certificado adequada. Para gerar e fazer o upload do build, siga estas etapas:

  1. No menu principal do Android Studio, clique em Build > Generate Signed Bundle/APK.
  2. Na caixa de diálogo que aparecer em seguida, selecione Android App Bundle para usar a Assinatura de apps do Google, ou APK, se você estiver fazendo a implantação diretamente em um dispositivo.
  3. Na próxima caixa de diálogo, em Key store path, clique em Create new. Uma nova janela vai aparecer.
  4. Selecione um caminho para seu keystore e dê a ele o nome de basics-keystore.jks.
  5. Crie e confirme uma senha para o keystore.
  6. Deixe o padrão para a chave Alias.
  7. Verifique se a senha e a confirmação são as mesmas no keystore. Elas precisam ser iguais.
  8. Preencha as informações em Certificate e clique em OK.

Modal de "New Key Store" do Android Studio com estes valores e itens de menu: diretório selecionado em "Key store path"; senha escolhida nos campos "Password" e "Confirm"; key0 para "Alias"; mesma senha para "Password" e "Confirm"; valor padrão para "Validity"; "Sabs sabs" para o campo "First and Last Name"; Android para "Organizational Unit"; "MyOrg" para "Organization"; "MyCity para "City or Locality"; "MyState para "State or Province" e "US" para "Country Code".

  1. A opção para exportar as chaves criptografadas precisa estar selecionada para a Assinatura de apps do Google Play. Clique em Next.

Modal do menu "Generate Sign Bundle or APK" com estes valores e itens de menu: padrão para "Module"; caminho gerado para "Key store path"; senha gerada anteriormente para "Key store password"; "key0" para "Key alias"; senha gerada anteriormente para "Key password"; opção "Export encrypted key for enrolling published apps in Google Play App Signing" selecionada e o valor padrão para "Encrypted key export path".

  1. Nesta caixa de diálogo, selecione a variante do build de lançamento e clique em Finish. Agora você pode fazer o upload do seu app na Google Play Store e usar o recurso de Assinatura de apps do Google Play.

Assinatura de apps do Google Play

Com o recurso de Assinatura de apps do Google Play, o Google ajuda você a gerenciar e proteger as chaves de assinaturas do seu app. Você só precisa fazer o upload do pacote assinado do app, que você aprendeu na etapa anterior.

Para extrair a impressão digital do certificado do arquivo assetlinks.json e usar seus Links do app Android na variante do build de lançamento, siga estas etapas:

  1. No Google Play Console, clique em Criar app.
  2. Para o nome do app, digite Deep Links Basics.
  3. Selecione App e Gratuito para as duas próximas opções. Menu "Criar app" com estes valores atualizados: "Deep Links Basics" para "Nome do app", "App" selecionado em "App ou jogo", "Gratuito" selecionado em "Gratuito ou pago" e as duas declarações aceitas.
  4. Aceite as Declarações e clique em Criar app.
  5. Para fazer o upload do pacote e poder testar os Links do app Android, selecione Testes > Teste interno.
  6. Clique em Criar nova versão.

Seção "Teste interno" do Play Console, mostrando o botão "Criar nova versão".

  1. Na próxima tela, clique em Upload e selecione o pacote gerado na seção anterior. Acesse o arquivo app-release.aab em DeepLinksBasics > app > release. Clique em Abrir e espere até que o pacote seja enviado.
  2. Depois de enviado, deixe os arquivos restantes com os respectivos padrões por enquanto. Clique em Salvar.

Seção de lançamento de testes internos do Play Console com o upload do app DeepLinksBasics. Os valores padrões estão preenchidos.

  1. Para se preparar para a próxima seção, clique em Avaliar versão e, em seguida, na tela seguinte, clique em Começar lançamento para testes internos. Ignore os avisos porque a atividade de publicação na Play Store está fora do escopo deste codelab.
  2. Clique em Lançar no modal.
  3. Para extrair a impressão digital do certificado SHA-256 que a Assinatura de apps do Google Play criou, acesse a guia Links diretos, no menu à esquerda e, em seguida, confira o painel de links diretos.

Painel "Links diretos" no Play Console, que mostra todas as informações sobre os links diretos enviados recentemente.

  1. Na seção Domínios, clique no domínio do site. O Google Play Console informará que você não validou o domínio com seu app (associação de sites).
  2. Na seção Corrigir problemas de domínio, clique na seta Mostrar mais.
  3. Nessa tela, o Google Play Console mostra como atualizar o arquivo assetlinks.json com a impressão digital do certificado. Copie o snippet de código e atualize o arquivo assetlinks.json.

Seção de verificação de domínio do painel de links diretos mostrando como atualizar o domínio com a impressão digital do certificado correta.

  1. Após atualizar o arquivo assetlinks.json, clique em Repetir verificação. Caso a verificação ainda não tenha sido aprovada, aguarde cinco minutos para que o serviço de verificação possa detectar as novas mudanças.
  2. Se você recarregar a página do painel Links diretos, vai notar que não há mais erros de verificação.

Verificação de um app enviado

Você já sabe como verificar um app no simulador. Agora, você vai verificar seu app que foi enviado à Play Store.

Para instalar o aplicativo no emulador e garantir que os Links do app Android sejam aprovados, siga estas etapas:

  1. No painel esquerdo, clique em Visão geral das versões e selecione a versão mais recente que você acabou de enviar. Deve ser a versão 1 (1.0).
  2. Clique em Detalhes da versão (seta azul à direita) para conferir os detalhes.
  3. Clique no mesmo botão da seta azul à direita para acessar as informações sobre o pacote do app.
  4. Neste modal, selecione a guia Downloads e, em seguida, clique em download para o recurso APK universal e assinado.
  5. Antes de instalar este pacote no simulador, exclua o aplicativo anterior instalado pelo Android Studio.
  6. No simulador, clique e mantenha pressionado o ícone do app DeepLinksBasics e selecione App Info. Clique em Uninstall e em Confirm no modal.

f112e0d252c5eb48.gif

  1. Para instalar o pacote, arraste o arquivo 1.apk transferido por download e solte na tela do simulador e espere até que ele seja instalado.

8967dac36ae545ee.gif

  1. Para testar a validação, abra o terminal no Android Studio e execute o processo de verificação usando estes dois comandos:
adb shell pm verify-app-links --re-verify com.devrel.deeplinksbasics
adb shell pm get-app-links com.devrel.deeplinksbasics
  1. Depois do comando get-app-links, uma mensagem verified vai aparecer no console. Se você receber uma mensagem legacy_failure, verifique se a impressão digital do certificado é igual à que você enviou para o site. Se forem iguais e a mensagem de verificação ainda não aparecer, tente executar as etapas 6, 7 e 8 de novo.

Saída do console.

7. Implementar Links do app Android

Agora que tudo está configurado, é hora de implementar o app.

O Jetpack Compose será usado para a implementação. Para saber mais sobre o Jetpack Compose, consulte Crie apps melhores com mais rapidez usando o Jetpack Compose.

Dependência de códigos

Para incluir e atualizar algumas dependências que você precisa para este projeto:

  • Adicione o seguinte aos arquivos Module e Project do Gradle:

build.gradle Projeto (link em inglês)

buildscript {
  ...
  dependencies {
    classpath "com.google.dagger:hilt-android-gradle-plugin:2.43"
  }
}

build.gradle Módulo (link em inglês)

plugins {
  ...
  id 'kotlin-kapt'
  id 'dagger.hilt.android.plugin'
}
...
dependencies {
  ...
  implementation 'androidx.compose.material:material:1.2.1'
  ...
  implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
  implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
  implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
  implementation "com.google.dagger:hilt-android:2.43"
  kapt "com.google.dagger:hilt-compiler:2.43"
}

O arquivo zip do projeto contém um diretório com 10 imagens livres de royalties que podem ser usadas para cada restaurante. Você pode usá-las ou incluir imagens próprias.

Para adicionar o ponto de entrada principal para o HiltAndroidApp, siga esta etapa:

  • Crie um novo arquivo de classe do Kotlin com o nome DeepLinksBasicsApplication.kt e atualize o arquivo de manifesto com o nome do novo app.

DeepLinksBasicsApplication.kt (link em inglês)

package com.devrel.deeplinksbasics

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class DeepLinksBasicsApplication : Application() {}

AndroidManifest.xml (link em inglês)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!-- Update name property -->
    <application
        android:name=".DeepLinksBasicsApplication"
        ...

Dados

Você precisa criar uma camada de dados para os restaurantes com uma classe Restaurant, um repositório e uma origem de dados locais. Todos os componentes precisam ficar em um pacote data que você terá que criar. Para isso, siga estas etapas:

  1. No arquivo Restaurant.kt, crie uma classe Restaurant com este snippet de código:

Restaurant.kt (link em inglês)

package com.devrel.deeplinksbasics.data

import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable

@Immutable
data class Restaurant(
    val id: Int = -1,
    val name: String = "",
    val address: String = "",
    val type: String = "",
    val website: String = "",
    @DrawableRes val drawable: Int = -1
)
  1. No arquivo RestaurantLocalDataSource.kt, adicione alguns restaurantes na classe de origem de dados. Não se esqueça de atualizar os dados com seu domínio. Confira este snippet de código:

RestaurantLocalDataSource.kt (link em inglês)

package com.devrel.deeplinksbasics.data

import com.devrel.deeplinksbasics.R
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class RestaurantLocalDataSource @Inject constructor() {
    val restaurantList = listOf(
        Restaurant(
            id = 1,
            name = "Pawtato",
            address = "3140 Skinner Hollow Road, Medford, Oregon 97501",
            type = "Potato and gnochi",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/pawtato/",
            drawable = R.drawable.restaurant1,
        ),
        Restaurant(
            id = 2,
            name = "Rawrbucha",
            address = "2064 Carriage Lane, Mansfield, Ohio 44907",
            type = "Kombucha",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/rawrbucha/",
            drawable = R.drawable.restaurant2,
        ),
        Restaurant(
            id = 3,
            name = "Pizzabus",
            address = "1447 Davis Avenue, Petaluma, California 94952",
            type = "Pizza",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/pizzabus/",
            drawable = R.drawable.restaurant3,
        ),
        Restaurant(
            id = 4,
            name = "Keybabs",
            address = "3708 Pinnickinnick Street, Perth Amboy, New Jersey 08861",
            type = "Kebabs",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/keybabs/",
            drawable = R.drawable.restaurant4,
        ),
        Restaurant(
            id = 5,
            name = "BBQ",
            address = "998 Newton Street, Saint Cloud, Minnesota 56301",
            type = "BBQ",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/bbq/",
            drawable = R.drawable.restaurant5,
        ),
        Restaurant(
            id = 6,
            name = "Salades",
            address = "4522 Rockford Mountain Lane, Oshkosh, Wisconsin 54901",
            type = "salads",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/salades/",
            drawable = R.drawable.restaurant6,
        ),
        Restaurant(
            id = 7,
            name = "Gyros and moar",
            address = "1993 Bird Spring Lane, Houston, Texas 77077",
            type = "Gyro",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/gyrosAndMoar/",
            drawable = R.drawable.restaurant7,
        ),
        Restaurant(
            id = 8,
            name = "Peruvian ceviche",
            address = "2125 Deer Ridge Drive, Newark, New Jersey 07102",
            type = "seafood",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/peruvianCeviche/",
            drawable = R.drawable.restaurant8,
        ),
        Restaurant(
            id = 9,
            name = "Vegan burgers",
            address = "594 Warner Street, Casper, Wyoming 82601",
            type = "vegan",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/veganBurgers/",
            drawable = R.drawable.restaurant9,
        ),
        Restaurant(
            id = 10,
            name = "Taquitos",
            address = "1654 Hart Country Lane, Blue Ridge, Georgia 30513",
            type = "mexican",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/taquitos/",
            drawable = R.drawable.restaurant10,
        ),
    )
}
  1. Não se esqueça de importar imagens para seu projeto.
  2. Em seguida, no arquivo RestaurantRepository.kt, adicione o repositório Restaurant com uma função para acessar um restaurante pelo nome, como neste snippet de código:

RestaurantRepository.kt (link em inglês)

package com.devrel.deeplinksbasics.data

import javax.inject.Inject

class RestaurantRepository @Inject constructor(
    private val restaurantLocalDataSource: RestaurantLocalDataSource
){
    val restaurants: List<Restaurant> = restaurantLocalDataSource.restaurantList

    // Method to obtain a restaurant object by its name
    fun getRestaurantByName(name: String): Restaurant ? {
        return restaurantLocalDataSource.restaurantList.find {
            val processedName = it.name.filterNot { it.isWhitespace() }.lowercase()
            val nameToTest = name.filterNot { it.isWhitespace() }.lowercase()
            nameToTest == processedName
        }
    }
}

ViewModel

Para selecionar um restaurante pelo app e com um Link do app Android, você precisa criar um ViewModel que mude o valor do restaurante selecionado. Faça o seguinte:

  • No arquivo RestaurantViewModel.kt, adicione este snippet de código:

RestaurantViewModel.kt (link em inglês)

package com.devrel.deeplinksbasics.ui.restaurant

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.devrel.deeplinksbasics.data.Restaurant
import com.devrel.deeplinksbasics.data.RestaurantRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class RestaurantViewModel @Inject constructor(
    private val restaurantRepository: RestaurantRepository,
) : ViewModel() {
    // restaurants and selected restaurant could be used as one UIState stream
    // which will scale better when exposing more data.
    // Since there are only these two, it is okay to expose them as separate streams
    val restaurants: List<Restaurant> = restaurantRepository.restaurants

    private val _selectedRestaurant = MutableStateFlow<Restaurant?>(value = null)
    val selectedRestaurant: StateFlow<Restaurant?>
        get() = _selectedRestaurant

    // Method to update the current restaurant selection
    fun updateSelectedRestaurantByName(name: String) {
        viewModelScope.launch {
            val selectedRestaurant: Restaurant? = restaurantRepository.getRestaurantByName(name)
            if (selectedRestaurant != null) {
                _selectedRestaurant.value = selectedRestaurant
            }
        }
    }
}

Compose

Agora que você tem a lógica do modelo de visualização e as camadas de dados, precisa adicionar uma camada de interface. Graças à biblioteca do Jetpack, é possível fazer isso em apenas algumas etapas. Neste app, você quer renderizar os restaurantes em grades modulares. O usuário pode clicar nos cards e conferir detalhes de cada restaurante. Você precisa de três funções combináveis principais e um componente de navegação que leve até o restaurante correspondente.

Android Emulator mostrando o app de restaurante completo.

Para adicionar uma camada de interface, siga estas etapas:

  1. Comece com a função combinável que renderiza detalhes de cada restaurante. No arquivo RestaurantCardDetails.kt, adicione este snippet de código:

RestaurantCardDetails.kt (link em inglês)

package com.devrel.deeplinksbasics.ui

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantCardDetails (
    restaurant: Restaurant,
    onBack: () -> Unit,
) {
    BackHandler() {
       onBack()
    }
    Scaffold(
        topBar = {
            TopAppBar(
                backgroundColor = Color.Transparent,
                elevation = 0.dp,
            ) {
                Row(
                    horizontalArrangement = Arrangement.Start,
                    modifier = Modifier.padding(start = 8.dp)
                ) {
                    Icon(
                        imageVector = Icons.Default.ArrowBack,
                        contentDescription = "Arrow Back",
                       modifier = Modifier.clickable {
                            onBack()
                        }
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Text(text = restaurant.name)
                }
            }
        }
    ) { paddingValues ->
        Card(
            modifier = Modifier
                .padding(paddingValues)
                .fillMaxWidth(),
            elevation = 2.dp,
            shape = RoundedCornerShape(corner = CornerSize(8.dp))
        ) {
            Column(
                modifier = Modifier
                    .padding(16.dp)
                    .fillMaxWidth()
            ) {
                Text(text = restaurant.name, style = MaterialTheme.typography.h6)
                Text(text = restaurant.type, style = MaterialTheme.typography.caption)
                Text(text = restaurant.address, style = MaterialTheme.typography.caption)
                SelectionContainer {
                    Text(text = restaurant.website, style = MaterialTheme.typography.caption)
                }
                Image(painter = painterResource(id = restaurant.drawable), contentDescription = "${restaurant.name}")
            }
        }
    }
}
  1. Em seguida, implemente a grade e as células dela. No arquivo RastaurantCell.kt, adicione este snippet de código:

RestaurantCell.kt (link em inglês)

package com.devrel.deeplinksbasics.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantCell(
    restaurant: Restaurant
){
    Card(
        modifier = Modifier
            .padding(horizontal = 8.dp, vertical = 8.dp)
            .fillMaxWidth(),
        elevation = 2.dp,
        shape = RoundedCornerShape(corner = CornerSize(8.dp))
    ) {
        Column(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text(text = restaurant.name, style = MaterialTheme.typography.h6)
            Text(text = restaurant.address, style = MaterialTheme.typography.caption)
            Image(painter = painterResource(id = restaurant.drawable), contentDescription = "${restaurant.name}")
        }
    }
}
  1. No arquivo RestaurantGrid.kt, adicione este snippet de código:

RestaurantGrid.kt (link em inglês)

package com.devrel.deeplinksbasics.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantGrid(
    restaurants: List<Restaurant>,
    onRestaurantSelected: (String) -> Unit,
    navigateToRestaurant: (String) -> Unit,
) {
    Scaffold(topBar = {
        TopAppBar(
            backgroundColor = Color.Transparent,
            elevation = 0.dp,
        ) {
            Text(text = "Restaurants", fontWeight = FontWeight.Bold)
        }
    }) { paddingValues ->
        LazyVerticalGrid(
            columns = GridCells.Adaptive(minSize = 200.dp),
            modifier = Modifier.padding(paddingValues)
        ) {
            items(items = restaurants) { restaurant ->
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable(onClick = {
                            onRestaurantSelected(restaurant.name)
                            navigateToRestaurant(restaurant.name)
                        })
                ) {
                    RestaurantCell(restaurant)
                }
            }
        }
    }
}
  1. Agora, você precisa implementar o estado do aplicativo e a lógica de navegação e atualizar o arquivo MainActivity.kt. Isso pode direcionar para um restaurante específico quando um usuário clicar no card do restaurante. No arquivo RestaurantAppState.kt, adicione este snippet de código:

RestaurantAppState.kt (link em inglês)

package com.devrel.deeplinksbasics.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController

sealed class Screen(val route: String) {
   object Grid : Screen("restaurants")
   object Name : Screen("restaurants/{name}") {
       fun createRoute(name: String) = "restaurants/$name"
   }
}

@Composable
fun rememberRestaurantAppState(
    navController: NavHostController = rememberNavController(),
) = remember(navController) {
    RestaurantAppState(navController)
}

class RestaurantAppState(
    val navController: NavHostController,
) {
    fun navigateToRestaurant(restaurantName: String) {
        navController.navigate(Screen.Name.createRoute(restaurantName))
    }

    fun navigateBack() {
        navController.popBackStack()
    }
}
  1. Para a navegação, você precisa criar o NavHost e usar as rotas combináveis para fazer o direcionamento a cada restaurante. No arquivo RestaurantApp.kt, adicione este snippet de código:

RestaurantApp.kt (link em inglês)

package com.devrel.deeplinksbasics.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.devrel.deeplinksbasics.ui.restaurant.RestaurantViewModel

@Composable
fun RestaurantApp(
   viewModel: RestaurantViewModel = viewModel(),
   appState: RestaurantAppState = rememberRestaurantAppState(),
) {
    val selectedRestaurant by viewModel.selectedRestaurant.collectAsState()
    val onRestaurantSelected: (String) -> Unit = { viewModel.updateSelectedRestaurantByName(it) }

    NavHost(
        navController = appState.navController,
        startDestination = Screen.Grid.route,
    ) {
        // Default route that points to the restaurant grid
        composable(Screen.Grid.route) {
            RestaurantGrid(
                restaurants = viewModel.restaurants,
                onRestaurantSelected = onRestaurantSelected,
                navigateToRestaurant = { restaurantName ->
                    appState.navigateToRestaurant(restaurantName)
                },
            )
        }
        // Route for the navigation to a particular restaurant when a user clicks on it
        composable(Screen.Name.route) {
            RestaurantCardDetails(restaurant = selectedRestaurant!!, onBack = appState::navigateBack)
        }
    }
}
  1. Agora está tudo pronto para você atualizar o arquivo MainActivity.kt com a instância do app. Substitua o arquivo por este código:

MainActivity.kt (link em inglês)

package com.devrel.deeplinksbasics

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.ui.Modifier
import com.devrel.deeplinksbasics.ui.RestaurantApp
import com.devrel.deeplinksbasics.ui.theme.DeepLinksBasicsTheme
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DeepLinksBasicsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    RestaurantApp()
                }
            }
        }
    }
}
  1. Execute o aplicativo para navegar pela grade e selecionar um restaurante específico. Quando você selecionar um restaurante, ele vai aparecer no app com os respectivos dados.

fecffce863113fd5.gif

Agora, adicione os Links do app Android à grade e a cada restaurante. Você já tem a seção AndroidManifest.xml para a grade em /restaurants. Você pode usar essa mesma base para cada restaurante, basta adicionar uma nova configuração de rota à sua lógica. Para isso, siga estas etapas:

  1. Atualize o arquivo de manifesto com o filtro de intent para receber /restaurants como um caminho e não se esqueça de incluir seu domínio como um host. No arquivo AndroidManifest.xml, adicione este snippet de código:

AndroidManifest.xml (link em inglês)

...
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="http"/>
  <data android:scheme="https"/>
  <data android:host="your.own.domain"/>
  <data android:pathPrefix="/restaurants"/>
</intent-filter>
  1. No arquivo RestaurantApp.kt, adicione este snippet de código:

RestaurantApp.kt (link em inglês)

...
import androidx.navigation.NavType
import androidx.navigation.navArgument
import androidx.navigation.navDeepLink

fun RestaurantApp(...){
  NavHost(...){
    ...
    //  Route for the navigation to a particular restaurant when a user clicks on it
    //  and for an incoming deep link
    // Update with your own domain
        composable(Screen.Name.route,
            deepLinks = listOf(
                navDeepLink { uriPattern = "https://your.own.domain/restaurants/{name}" }
            ),
            arguments = listOf(
                navArgument("name") {
                    type = NavType.StringType
                }
            )
        ) { entry ->
            val restaurantName = entry.arguments?.getString("name")
            if (restaurantName != null) {
                LaunchedEffect(restaurantName) {
                    viewModel.updateSelectedRestaurantByName(restaurantName)
                }
            }
            selectedRestaurant?.let {
                RestaurantCardDetails(
                    restaurant = it,
                    onBack = appState::navigateBack
                )
            }
        }
  }
}

Internamente, o NavHost associa os dados do Uri da intent do Android a rotas combináveis. Se uma rota corresponder à intent, o composable será renderizado.

O componente composable pode receber um parâmetro deepLinks, que contém uma lista dos URIs recebidos do filtro da intent. Neste codelab, você vai adicionar o site criado e definir o parâmetro de identificação a ser recebido e encaminhar o usuário a esse restaurante específico.

  1. Para garantir que a lógica do app encaminhe o usuário ao restaurante correspondente depois dele clicar em um Link do app Android, use adb:
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/gyrosAndMoar"

O app mostrará o restaurante correspondente.

App de restaurantes no Android Emulator mostrando a tela do restaurante "Gyros and moar".

8. Analisar o painel do Google Play Console

Você já analisou o painel de links diretos. Esse painel fornece todas as informações necessárias para garantir que seus links diretos estejam funcionando corretamente Você pode até mesmo analisar por versão do app. Ele mostra o domínio e links comuns e personalizados que foram adicionados ao seu arquivo de manifesto. Também mostra onde atualizar o arquivo assetlinks.json, caso haja algum problema.

Painel de links diretos do Play Console com um Link do app Android verificado.

9. Conclusão

Parabéns! Você criou seu primeiro app com os Links do app Android.

Agora você conhece o processo para desenvolver, configurar, criar e testar os Links do app Android. Esse processo possui muitas etapas. Este codelab reúne todos esses detalhes para que você tenha sucesso ao desenvolver para o SO Android.

Agora você conhece as principais etapas necessárias para que os Links do app Android funcionem.

Leia mais (links em inglês)

Documentos de referência