Cómo configurar, implementar y verificar Android App Links

1. Antes de comenzar

El objetivo principal de un usuario cuando hace clic en un vínculo directo es obtener el contenido que desea ver. Todos los vínculos directos tienen la función de ayudar a los usuarios a lograr este objetivo. Android utiliza los siguientes tipos de vínculos:

  • Vínculos directos: URIs de cualquier esquema que llevan a los usuarios a una parte específica de tu app
  • Vínculos web: Vínculos directos con el esquema HTTP y HTTPS
  • Android App Links: Vínculos web y esquemas HTTP y HTTPS que incluyen el atributo android:autoVerify

Para obtener información más detallada sobre los vínculos directos, los vínculos web y Android App Links, consulta la documentación de Android y el curso rápido en YouTube y Medium.

Si ya conoces todos los detalles técnicos, consulta una implementación rápida en la entrada de blog complementaria que te ayudará con la configuración en unos pocos pasos.

Objetivo del codelab

En este codelab, aprenderás las prácticas recomendadas para el proceso de configuración, implementación y verificación de una app con Android App Links.

Una de las ventajas de Android App Links es que son vínculos seguros, de manera que las apps no autorizadas no podrán controlarlos. Para que califiquen como Android App Links, el SO Android debe verificar los vínculos con un sitio web que te pertenece. Este proceso se llama asociación con el sitio web.

Este codelab está destinado principalmente a los desarrolladores con un sitio web y una app para Android. Android App Links ofrece una mejor experiencia del usuario a través de la integración continua entre tu app y tu sitio web.

Requisitos previos

Qué aprenderás

  • Prácticas recomendadas para el diseño de URLs para Android App Links
  • Configurar todos los tipos de vínculos directos en una aplicación para Android
  • Comprender los comodines de rutas de acceso (path, pathPrefix, pathPattern, pathAdvancePattern)
  • El proceso de verificación de Android App Links, que incluye la carga del archivo de Vínculos de recursos digitales (DAL) de Google, el proceso de verificación manual de Android App Links y el panel de vínculos directos de Play Developer Console
  • Compilar una app para Android con distintos restaurantes en diferentes ubicaciones

Aspecto final de la aplicación web con restaurantes. Aspecto final de la aplicación para Android con restaurantes.

Requisitos

  • Android Studio Dolphin (2021.3.1) o una versión más reciente
  • Un dominio para alojar los archivos de Vínculos de recursos digitales (DAL) de Google (opcional: Lee esta entrada de blog, que te ayudará a tenerlo listo en minutos.)
  • Opcional: Cuenta de desarrollador de Google Play Console (de esta manera, tendrás otro enfoque para depurar tu configuración de Android App Links)

2. Código de configuración

Cómo crear una aplicación para Compose vacía

Para comenzar un proyecto de Compose, sigue estos pasos:

  1. En Android Studio, selecciona File > New > New Project.

Menú de archivo y selección de la siguiente ruta de acceso: New a New Project.

  1. Selecciona Empty Compose Activity entre las plantillas disponibles.

Modal de proyecto Android Studio 'New' con la opción 'Empty Compose Activity' seleccionada.

  1. Haz clic en Next y configura tu proyecto con el nombre Deep Links Basics. Elige una versión mínima del SDK de, el menos, nivel de API 21 (el nivel de API mínimo que admite Compose).

Modal de configuración de proyecto nuevo de Android Studio con los siguientes valores y opciones de menú. 'Deep Links Basics' para Name. 'com.devrel.deeplinksbasics' para Package Name. La opción de guardar la ubicación tiene el valor predeterminado. 'Kotlin' para Language. API 21 para Minimum SDK.

  1. Haz clic en Finish y espera a que se genere tu proyecto.
  2. Inicia la aplicación. Asegúrate de que se ejecute la app. Debería haber una pantalla en blanco con el mensaje Hello Android!.

Pantalla vacía de la app para Android de Compose en la que se muestra el siguiente texto: 'Hello Android'.

Solución del codelab

Puedes obtener el código de la solución de este codelab en GitHub:

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

También tienes la opción de descargar el repositorio como archivo ZIP:

Primero, ve al directorio de deep-links-introduction. Encontrarás la app en el directorio de solution. Te recomendamos seguir el codelab paso a paso, a tu propio ritmo, y consultar la solución si necesitas ayuda. Durante el codelab, recibirás fragmentos de código que deberás agregar al proyecto.

3. Revisa el diseño de URL orientado para vínculos directos

Diseño de la API de RESTful

Los vínculos son una parte fundamental del desarrollo web, y su diseño pasó por innumerables iteraciones, lo que generó distintos estándares. Vale la pena analizar y aplicar los estándares de diseño de vínculos para el desarrollo web, que facilitan su uso y mantenimiento.

Uno de estos estándares es REST (transferencia de estado representacional), una arquitectura que generalmente se utiliza para crear APIs para servicios web. Open API es una iniciativa que estandariza las APIs de REST. También puedes utilizar REST para diseñar tus URLs para vínculos directos.

Ten en cuenta que no estás creando un servicio web. Esta sección solo se enfoca en el diseño de URL.

Cómo diseñar las URLs

Primero, revisa las URLs de resultado en tu sitio web y, luego, interpreta qué representan en la app para Android:

  • /restaurants enumera todos los restaurantes que administras.
  • /restaurants/:restaurantName muestra los detalles de un solo restaurante.
  • /restaurants/:restaurantName/orders muestra los pedidos de un restaurante.
  • /restaurants/:restaurantName/orders/:orderNumber muestra un pedido específico en un restaurante.
  • /restaurants/:restaurantName/orders/latest muestra el pedido más reciente en un restaurante.

Por qué el diseño de la URL es importante

Android tiene filtros de intents que controlan acciones de otro componente de apps y que también se utilizan para detectar URLs. Cuando defines un filtro de intents para detectar una URL, debes utilizar una estructura que dependa de prefijos de ruta de acceso y comodines directos. El siguiente es un ejemplo de cómo se estructura a partir de una URL existente en el sitio web de tu restaurante:

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

Aunque esta URL especifica tu restaurante y su ubicación, la ruta de acceso puede generar un problema cuando se define un filtro de intents para que Android detecte la URL, ya que tu aplicación está basada en varias URLs de restaurante como estas:

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

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

Cuando defines un filtro de intents con una ruta de acceso y un comodín para detectar estas URLs, puedes usar algo como https://example.com/*, que cumplirá su objetivo. Sin embargo, no resolviste por completo el problema porque hay otras rutas de acceso existentes para distintas secciones en tu sitio web, como las siguientes:

Entrega: https://example.com/deliveries

Administración: https://example.com/admin

Quizás no sea conveniente que Android detecte estas URLs, ya que algunas de ellas podrían ser internas. Sin embargo, el filtro de intents https://example.com/* definido las detectará, incluidas las URLs que no existen. Cuando un usuario hace clic en una de estas URLs, la URL se abrirá en el navegador (> Android 12) o podría aparecer un diálogo de desambiguación (< Android 12). En este diseño, no es el comportamiento esperable.

Ahora, Android ofrece prefijos de ruta de acceso que resuelven este problema, pero sí cambia el diseño de la URL, de:

https://example.com/*

a:

https://example.com/restaurants/*

La incorporación de una estructura de anidación jerárquica hace que tus filtros de intents estén claramente definidos, y Android detecta la URL que le indiques que detecte.

Prácticas recomendadas sobre el diseño de la URL

Estas son algunas prácticas recomendadas recopiladas a partir de Open API que se aplican a una perspectiva de vínculos directos:

  • Enfoca el diseño de la URL en las entidades empresariales que exponen. Por ejemplo, en el comercio electrónico pueden ser clientes y pedidos. En el caso de viajes, pueden ser boletos y vuelos. En la app y el sitio web de tu restaurante, usarás restaurantes y pedidos.
  • La mayoría de los métodos HTTP (GET, POST, DELETE, PUT) son verbos que describen la solicitud que se realiza, pero resultaría confuso utilizar verbos para los extremos en las URLs.
  • Para describir colecciones, utiliza el plural de la entidad, como /restaurants/:restaurantName. De esta manera, la URL es más fácil de leer y mantener. El siguiente es un ejemplo con cada uno de los métodos HTTP:

GET /restaurants/pawtato

POST /restaurants

DELETE /restaurants

PUT /restaurants/pawtato

Resulta más fácil leer cada URL y entender lo que hace. Ten en cuenta que este codelab no abarca el diseño de API para servicios web ni qué hace cada método.

  • Utiliza la anidación lógica para agrupar las URLs que contengan información relacionada. Por ejemplo, una URL para uno de nuestros restaurantes puede tener los pedidos en los que se está trabajando.

/restaurants/1/orders

4. Revisa los elementos de datos

El archivo AndroidManifest.xml es una parte fundamental de Android. Describe la información de la app para las herramientas de compilación de Android, el SO Android y Google Play.

En el caso de los vínculos directos, debes definir un filtro de intents utilizando tres etiquetas principales: <action>, <category> y <data>. En esta sección, nos enfocaremos principalmente en la etiqueta <data>.

Un elemento <data> le indica al SO Android la estructura de la URL de un vínculo una vez que el usuario hace clic en él. El formato y la estructura de la URL que puedes usar en el filtro de intents es el siguiente:

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

Android lee, analiza y combina todos los elementos <data> en un filtro de intents para que se tengan en cuenta todas las variaciones de los atributos. Por ejemplo:

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>

Android detectará las siguientes URLs:

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

Atributos de la ruta de acceso

path (disponible en API 1)

Este atributo especifica una ruta de acceso completa (que comienza con /) que se compara con la ruta de acceso completa en el intent. Por ejemplo, android:path="/restaurants/pawtato" solo coincide con la ruta de acceso del sitio web /restaurants/pawtato, mientras que si tenemos /restaurant/pawtato, esta URL no coincide debido a la s que falta.

pathPrefix (disponible en API 1)

Este atributo especifica una ruta de acceso parcial que se compara solo con la parte inicial de la ruta de acceso del intent. Por ejemplo:

android:pathPrefix="/restaurants" coincidirá con las rutas de acceso de "restaurants": /restaurants/pawtato, /restaurants/pizzabus, etcétera.

pathSuffix (disponible en API 31)

Este atributo especifica una ruta de acceso que se compara exactamente con la parte final de la ruta de acceso en el intent. Por ejemplo:

android:pathSuffix="tato" coincidirá con todas las rutas de acceso de "restaurants" que terminen con tato, como /restaurants/pawtato y /restaurants/corgtato.

pathPattern (disponible en API 1)

Este atributo especifica una ruta de acceso completa que coincide con una ruta de acceso completa con comodines en el intent:

  • Un asterisco (*) coincide con una secuencia compuesta por 0 a muchas repeticiones del carácter anterior.
  • Un punto seguido de un asterisco (.*) coincide con cualquier secuencia compuesta por 0 a muchos caracteres.

Ejemplos:

  • /restaurants/piz*abus: Este patrón coincide con el restaurante de "Pizzabus", pero también coincidirá con restaurantes compuestos por 0 a más caracteres z en el nombre, como /restaurants/pizzabus, /restaurants/pizzzabus y /restaurants/pizabus.
  • /restaurants/.*: Este patrón coincide con cualquier nombre de restaurante con la ruta de acceso /restaurants, como /restaurants/pizzabus y /restaurants/pawtato, además de aquellos que la app no conoce, como /restaurants/wateriehall.

pathAdvancePattern (disponible en API 31)

Este atributo especifica una ruta de acceso completa que coincide con la ruta de acceso completa con patrones similares a una regex:

  • Un punto (.) coincide con cualquier carácter.
  • Un par de corchetes ([...]) coincide con un rango de caracteres. Este par también admite el modificador no (^).
  • Un asterisco (*) coincide con el patrón anterior 0 o más veces.
  • Un signo más (+) coincide con el patrón anterior 1 o más veces.
  • Las llaves ({...}) representan la cantidad de veces que un patrón podría coincidir.

Este atributo se puede considerar una extensión de pathPattern. Brinda más flexibilidad sobre las URLs con las que debe coincidir, por ejemplo:

  • /restaurants/[a-zA-Z]*/orders/[0-9]{3} coincide con cualquier pedido de restaurante que tenga hasta 3 dígitos de longitud.
  • /restaurants/[a-zA-Z]*/orders/latest coincide con el último pedido de cualquiera de los restaurantes de la app.

5. Crea vínculos directos y vínculos web

Los vínculos directos con esquemas personalizados son el tipo más genérico de vínculo directo y los más fáciles de implementar, lo que trae inconvenientes. Los sitios web no pueden abrir estos vínculos. Cualquier app que declare en su manifiesto que admite ese esquema podrá abrir el vínculo.

Puedes usar cualquier esquema en el elemento <data>. Por ejemplo, este codelab utiliza la URL food://restaurants/keybabs.

  1. En Android Studio, agrega el siguiente filtro de intents al archivo de manifiesto:

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 verificar que tu aplicación pueda abrir vínculos con esquemas personalizados, cópiala en la pantalla principal y agrega lo siguiente a la actividad 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 probar que se reciba el intent, utiliza Android Debug Bridge (adb) con el siguiente comando:
adb shell am start -W -a android.intent.action.VIEW -d "food://restaurants/keybabs"

Este comando comienza un intent con la acción de VIEW y utiliza la URL suministrada como datos. Cuando ejecutas este comando, la app se lanza y recibe el intent. Observa los cambios en las secciones de texto de la pantalla principal. La primera sección muestra el mensaje Hello Android!, la segunda muestra a qué acción llamó el intent, y la tercera muestra a qué URL llamó el intent.

En la siguiente imagen, observa que en la sección inferior de Android Studio se ejecutó el comando adb mencionado. A la derecha, la app muestra la información sobre el intent en la pantalla principal, lo que indica que se lo recibió. Pantalla completa de Android Studio con las siguientes pestañas abiertas: 'code view', 'emulator' y 'terminal'. La vista de código muestra el archivo MainActivity.kt básico. El emulador muestra un campo de texto de vínculo directo que confirma que se recibió correctamente. La terminal muestra el comando adb que se acaba de tratar en el codelab.

Los vínculos web son vínculos directos que usan http y https en lugar de esquemas personalizados.

Para la implementación de vínculos web, utiliza la ruta de acceso /restaurants/keybabs/order/latest.html, que representa el último pedido que se recibió en el restaurante.

  1. Ajusta el archivo de manifiesto utilizando el filtro de intents existente.

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 se comparten ambas rutas de acceso (/restaurants/keybabs), conviene ubicarlas dentro del mismo filtro de intents, ya que esto facilita la implementación y la lectura del archivo de manifiesto.

  1. Antes de probar el vínculo web, reinicia la app para aplicar los nuevos cambios.
  2. Utiliza el mismo comando adb para lanzar el intent, aunque, en este caso, actualizaremos la URL.
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/keybabs/orders/latest.html"

En la captura de pantalla, observa que, al recibirse el intent, el navegador web se abre para mostrar el sitio web, una función de las versiones posteriores a Android 12. La vista completa de Android Studio con las siguientes etiquetas: 'Code view' muestra el archivo AndroidManifest.xml con el filtro de intents mencionado; 'Emulator view' muestra la página web que se abre gracias a vínculos web, la página web dirige a la app web de Restaurant; y la etiqueta 'Terminal view' muestra el comando adb para vínculos web.

6. Configura Android App Links

Estos vínculos ofrecen la experiencia del usuario más fluida, ya que cuando un usuario hace clic en un vínculo, se garantiza que el vínculo lo llevará a la app sin un diálogo de desambiguación. Android App Links se implementó en Android 6.0 y son el tipo más específico de vínculo directo. Son vínculos web que utilizan el esquema http/https y el atributo android:autoVerify, que hace que la app sea el controlador predeterminado para cualquier vínculo coincidente. Los dos pasos principales para implementar Android App Links son los siguientes:

  1. Actualiza el archivo de manifiesto con el filtro de intents apropiado.
  2. Agrega la asociación con el sitio web para la verificación.

Cómo actualizar el archivo de manifiesto

  1. Para brindar compatibilidad con Android App Links, en el archivo de manifiesto, reemplaza la configuración anterior con lo siguiente:

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 intents agrega el atributo android:autoVerify y lo establece en verdadero. Esto permite que el SO Android verifique el dominio cuando se instala la aplicación, y en cada actualización nueva.

Asociación con el sitio web

Para verificar un Android App Link, crea una asociación entre la aplicación y el sitio web. Es necesario publicar un archivo JSON de Vínculos de recursos digitales (DAL) de Google en el sitio web para que se produzca la validación.

Google DAL es un protocolo y una API que define sentencias verificables sobre otras apps y sitios web. En este codelab, crearás una sentencia sobre la app para Android en el archivo assetlinks.json. A continuación, se muestra un ejemplo:

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 archivo puede almacenar una lista de sentencias, pero en este ejemplo se muestra solo un elemento. Cada sentencia debe incluir los siguientes campos:

  • Relación: Describe una o más relaciones declaradas sobre el objetivo.
  • Objetivo: El recurso al que se aplica esta sentencia. Puede ser uno de dos objetivos disponibles: web o android_app.

La propiedad target de la sentencia de Android incluye los siguientes campos:

  • namespace: La android_app para todas las apps para Android
  • package_name: El nombre del paquete completamente calificado (com.devrel.deeplinksbasics)
  • sha256_cert_fingerprints: La huella digital del certificado para la app (aprenderás cómo generar este certificado en la próxima sección)

Huella digital del certificado

Hay varios métodos para obtener la huella digital del certificado. Este codelab utiliza dos métodos: uno para la compilación de depuración de la aplicación y el otro para ayudar a lanzar la app en Google Play Store.

Configuración de depuración

La primera vez que Android Studio ejecute tu proyecto, firmará automáticamente la app con un certificado de depuración. La ubicación de este certificado es: $HOME/.android/debug.keystore. Puedes usar un comando de Gradle para obtener esta huella digital del certificado de SHA-256. Los pasos que debes seguir son los siguientes:

  1. Presiona dos veces Control y debería aparecer el menú Run anything. Si no aparece, puedes encontrarlo en el menú de Gradle de la barra lateral derecha y, luego, hacer clic en el ícono de Gradle.

La pestaña de menú de Gradle de Android Studio con el ícono de Gradle seleccionado.

  1. Escribe gradle signingReport y presiona Enter. El comando se ejecuta en la consola y muestra la información sobre la huella digital de la variante de la app de depuración.

Venta de terminal en la que se muestran los resultados del informe de firma de Gradle.

  1. Para completar la asociación con el sitio web, copia la huella digital del certificado de SHA-256, actualiza el archivo JSON y súbelo a tu sitio web en la ubicación https://<domain>/.well-know/assetlinks.json. Esta entrada de blog sobre Android App Links te ayudará a configurarla.
  2. Si tu app sigue ejecutándose, presiona Stop para detenerla.
  3. Para volver a lanzar el proceso de verificación, quita la app del simulador. En el simulador, mantén presionado el ícono de la app de DeepLinksBasics y selecciona App Info. Haz clic en Uninstall y, luego, en Confirm en el modal. A continuación, ejecuta la aplicación para que Android Studio pueda verificar la asociación.

f112e0d252c5eb48.gif

  1. Asegúrate de seleccionar la configuración en ejecución app. De lo contrario, el informe de firma de Gradle volverá a ejecutarse. Menú de configuraciones en ejecución de Android Studio con la configuración 'app' seleccionada.
  2. Reinicia la aplicación y lanza el intent con la URL de Android App Link:
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/"
  1. Observa que se lanza la app y el intent aparece en la pantalla principal.

Pantalla principal de Android Emulator con campos de texto en la que se muestra que el Android App Link se implementó correctamente.

Felicitaciones, ¡acabas de crear tu primer Android App Link!

Configuración de lanzamiento

Ahora, para poder subir tu aplicación con Android App Links a Play Store, debes usar una compilación de lanzamiento con la huella digital del certificado apropiada. Para generarla y subirla, sigue esto pasos.

  1. En el menú principal de Android Studio, haz clic en Build > Generate Signed Bundle/APK.
  2. En el siguiente diálogo, selecciona Android App Bundle para la firma de apps de Play o APK si estás realizando la implementación a un dispositivo directamente.
  3. En el siguiente diálogo, en Key store path, haz clic en Create new. Se abrirá una nueva ventana.
  4. Selecciona una ruta de acceso para tu almacén de claves y asígnale el nombre basics-keystore.jks.
  5. Crea y confirma una contraseña para el almacén de claves.
  6. Deja el valor predeterminado para el alias de la clave.
  7. Asegúrate de que la contraseña y la confirmación sean las mismas que en el almacén de claves. Deben coincidir.
  8. Completa la información sobre el certificado y haz clic en OK.

Nuevo modal de almacén de claves de Android Studio con los siguientes valores y elementos de menú: directorio seleccionado en 'key store path', contraseña seleccionada en 'password' y 'confirm', key0 en 'alias', la misma contraseña en 'password' y 'confirm', valor predeterminado en 'validity', sabs sabs en 'first and last name', Android en 'organizational unit', my org en 'organization', my city en 'city or locality', my state en 'state or province', y US en 'country code'.

  1. Asegúrate de que la opción para exportar las claves encriptadas esté marcada para la firma de apps de Play y haz clic en Next.

Genera un paquete de firma o un modal de menú de APK con los siguientes valores y elementos de menú: Valor predeterminado en 'Module', ruta de acceso generada en 'key store path', contraseña generada anteriormente en 'key store password', key0 en 'key alias', contraseña generada anteriormente en 'key password', casilla seleccionada en 'export encrypted key for enrolling published apps in Google Play App Signing' y el valor predeterminado en 'Encrypted key export path'.

  1. En este diálogo, selecciona la variante de compilación de lanzamiento y haz clic en Finish. Ahora puedes subir la app a Google Play Store y utilizar la firma de apps de Play.

Firma de apps de Play

Con la firma de apps de Play, Google te ayuda a administrar y proteger la clave de firma de tu app. Solo debes subir el paquete firmado de la app, que creaste en el paso anterior.

Para recuperar la huella digital del certificado del archivo assetlinks.json y tener tus Android App Links en la compilación de la variante de lanzamiento, sigue estos pasos:

  1. En Google Play Console, haz clic en Create app.
  2. Escribe Deep Links Basics como nombre de la app.
  3. Selecciona App y Free en las siguientes dos opciones. Crea un menú de la app con los siguientes valores actualizados: Deep Links Basics en 'app name', app seleccionada en 'app or game', la opción "free" seleccionada en 'free or paid' y las dos declaraciones aceptadas.
  4. Acepta las Declarations y haz clic en Create app.
  5. Para subir el paquete y probar los Android App Links, en el menú de la izquierda, selecciona Testing > internal testing.
  6. Haz clic en Create new release.

Sección 'internal testing' de Play Console, en la que se muestra el botón 'create new release'.

  1. En la siguiente pantalla, haz clic en Upload y, luego, selecciona el paquete generado en la última sección. Encontrarás el archivo app-release.aab en DeepLinksBascis > app > release. Haz clic en Open y espera hasta que se suba el paquete.
  2. Una vez que termine de subirse, deja el resto de los campos con los valores predeterminados por ahora. Haz clic en Save.

Sección de lanzamiento de pruebas internas de Play Console con app básica de vínculos directos subida. Los valores predeterminados están completados.

  1. Para prepararte para la próxima sección, haz clic en Review release y, luego, en la próxima pantalla, selecciona Start rollout to Internal testing. Ignora las advertencias, ya que la publicación en Play Store no está dentro del alcance de este codelab.
  2. Haz clic en Rollout en el modal.
  3. Para obtener la huella digital del certificado SHA-256 que creó la firma de apps de Play, ve a la pestaña Deep links en el menú de la izquierda y, luego, consulta el panel de vínculos directos.

Panel de vínculos directos en Play Console, en el que se muestra toda la información sobre el vínculo directo recién subido.

  1. En la sección Domains, haz clic en el dominio del sitio web. Google Play Console te indicará que no validaste el dominio con tu app (asociación con el sitio web).
  2. En la sección Fix Domain Issues, haz clic en la flecha Show More.
  3. En esta pantalla, Google Play Console muestra cómo actualizar el archivo assetlinks.json con la huella digital del certificado. Copia el fragmento de código y actualiza el archivo assetlinks.json.

Sección de verificación del dominio del panel de vínculos directo en la que se muestra cómo actualizar el domino con la huella digital del certificado.

  1. Una vez que el archivo assetlinks.json esté actualizado, haz clic en Recheck verification. Si todavía no se completó la verificación, espera hasta cinco minutos para que el servicio de verificación detecte nuevos cambios.
  2. Si vuelves a cargar la página del panel Deep links verás que no habrá más errores de verificación.

Verificación de la app de carga

Ya sabes cómo verificar una app alojada en el simulador. Ahora, aprenderás a verificar una app que se cargó a Play Store.

Para instalar la aplicación en el emulador y asegurarte de que el Android App Link esté verificado, sigue estos pasos:

  1. En la barra lateral de la izquierda, haz clic en Releases Overview y, luego, selecciona la versión más reciente que acabas de subir. Debería ser la versión 1 (1.0).
  2. Haz clic en Release details (flecha azul derecha) para ver los detalles de la versión.
  3. Haz clic en el mismo botón de la flecha azul derecha para obtener información sobre el paquete de aplicación.
  4. En este modal, selecciona la pestaña Downloads y, luego, haz clic en download en el recurso Signed, universal APK.
  5. Antes de instalar este paquete en el simulador, borra la aplicación anterior que instaló Android Studio.
  6. En el simulador, mantén presionado el ícono de la app de DeepLinksBasics y selecciona App Info. Haz clic en Uninstall y, luego, en Confirm en el modal.

f112e0d252c5eb48.gif

  1. Para instalar el paquete descargado, arrastra y suelta el archivo 1.apk en la pantalla del simulador y espera a que se instale.

8967dac36ae545ee.gif

  1. Para probar la validación, abre el terminal en Android Studio y ejecuta el proceso de verificación con los siguientes dos comandos:
adb shell pm verify-app-links --re-verify com.devrel.deeplinksbasics
adb shell pm get-app-links com.devrel.deeplinksbasics
  1. Después del comando get-app-links, deberías ver un mensaje verified en la consola. Si ves un mensaje legacy_failure, asegúrate de que la huella digital del certificado coincida con la que subiste para el sitio web. Si coincide y sigues viendo el mensaje de verificación, intenta completar los pasos 6, 7 y 8 nuevamente.

Resultado de la consola.

7. Implementa Android App Links

Ahora que ya tienes todo configurado, es momento de implementar la app.

Para la implementación, utilizaremos Jetpack Compose. Si deseas obtener más información sobre Jetpack Compose, consulta Compila mejores apps más rápido con Jetpack Compose.

Dependencias de código

Para incluir y actualizar algunas dependencias que necesitas para este proyecto, completa este paso:

  • Además, agrega lo siguiente a los archivos de Gradle Module y Project:

build.gradle (proyecto)

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

build.gradle (módulo)

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"
}

El archivo project zip incluye una directorio de imágenes con 10 imágenes libres de regalías que se pueden utilizar para cada restaurante. No dudes en utilizarlas o incluye tus propias imágenes.

Para agregar el punto de entrada principal para HiltAndroidApp, completa este paso:

  • Crea un nuevo archivo o clase de Kotlin con el nombre DeepLinksBasicsApplication.kt y, luego, actualiza el archivo de manifiesto con el nuevo nombre de aplicación.

DeepLinksBasicsApplication.kt

package com.devrel.deeplinksbasics

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

@HiltAndroidApp
class DeepLinksBasicsApplication : Application() {}

AndroidManifest.xml

<?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"
        ...

Datos

Debes crear una capa de datos para los restaurantes con una fuente de datos locales, un repositorio y una clase Restaurant. Todo estará alojado en un paquete data que deberás crear. Para hacerlo, sigue estos pasos:

  1. En el archivo Restaurant.kt, crea una clase Restaurant con el siguiente fragmento de código:

Restaurant.kt

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. En el archivo RestaurantLocalDataSource.kt, agrega algunos restaurantes en la clase de fuente de datos. No te olvides de actualizar los datos con tu propio dominio. Consulta el siguiente fragmento de código:

RestaurantLocalDataSource.kt

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. Recuerda importar las imágenes a tu proyecto.
  2. A continuación, en el archivo RestaurantRepository.kt, agrega el repositorio Restaurant con una función para encontrar un restaurante por su nombre, como en el siguiente fragmento de código:

RestaurantRepository.kt

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 poder seleccionar un restaurante a través de la app y un Android App Link, debes crear un ViewModel que cambie el valor del restaurante seleccionado. Completa el siguiente paso:

  • En el archivo RestaurantViewModel.kt, agrega el siguiente fragmento de código:

RestaurantViewModel.kt

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

Ahora que tienes la lógica del viewmodel y las capas de datos, es momento de agregar una capa de IU. Gracias a la biblioteca de Jetpack Compose, puedes hacerlo en pocos pasos. Para esta app, te recomendamos renderizar los restaurantes en una grilla de tarjetas. El usuario puede hacer clic en cada tarjeta para ver los detalles del restaurante. Necesitas tres funciones de componibilidad principales y un componente de navegación que enrute al restaurante correspondiente.

Android Emulator mostrando la app de restaurantes completada.

Para agregar una capa de IU, sigue estos pasos:

  1. Comienza con la función de componibilidad que renderiza los detalles de cada restaurante. En el archivo RestaurantCardDetails.kt, agrega el siguiente fragmento de código:

RestaurantCardDetails.kt

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. A continuación, implementa la celda de cuadrícula y la cuadrícula. En el archivo RastaurantCell.kt, agrega el siguiente fragmento de código:

RestaurantCell.kt

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. En el archivo RestaurantGrid.kt, agrega el siguiente fragmento de código:

RestaurantGrid.kt

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. A continuación, debes implementar el estado de la aplicación y la lógica de navegación y, luego, actualizar MainActivity.kt. Puede dirigir a un restaurante específico cuando un usuario hace clic en una tarjeta de restaurante. En el archivo RestaurantAppState.kt, agrega el siguiente fragmento de código:

RestaurantAppState.kt

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 la navegación, debes crear el NavHost y usar las rutas componibles para dirigir a cada restaurante. En el archivo RestaurantApp.kt, agrega el siguiente fragmento de código:

RestaurantApp.kt

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. Ahora ya puedes actualizar MainActivity.kt con la instancia de aplicación. Reemplaza el archivo con el siguiente código:

MainActivity .kt

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. Ejecuta la aplicación para navegar a la cuadrícula y selecciona un restaurante en particular. Verás que, cuando seleccionas un restaurante, la app muestra ese restaurante y sus detalles.

fecffce863113fd5.gif

Ahora, agrega Android App Links a la cuadrícula y a cada restaurante. Ya tienes la sección AndroidManifest.xml para la cuadrícula en /restaurants. La ventaja es que puedes usar la misma cuadrícula para todos los restaurantes. Debes agregar una nueva configuración de ruta a tu lógica. Para hacerlo, sigue estos pasos:

  1. Actualiza el archivo de manifiesto con el filtro de intents para recibir /restaurants como una ruta de acceso y recuerda incluir tu dominio como host. En el archivo AndroidManifest.xml, agrega el siguiente fragmento de código:

AndroidManifest.xml

...
<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. En el archivo RestaurantApp.kt, agrega el siguiente fragmento de código:

RestaurantApp.kt

...
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
                )
            }
        }
  }
}

De forma interna, el NavHost coincide con los datos de Uri del intent de datos con las rutas componibles. Cuando una ruta coincide, se renderiza composable.

El componente composable acepta un parámetro deepLinks, que contiene una lista de las URIs que se recibieron del filtro de intents. En este codelab, agregarás la URL del sitio web creado y definirás el parámetro de ID para recibir y enviar al usuario a ese restaurante en particular.

  1. Para asegurarte de que la lógica de la app envíe a un usuario al restaurante correspondiente después de hacer clic en un Android App Link, utiliza adb:
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/gyrosAndMoar"

Observa que la app muestra el restaurante correspondiente.

App de restaurantes de Android Emulator en la que se muestra la pantalla del restaurante 'gyros and moar'

8. Consulta el panel de Play Developer Console

Ya conoces el panel de vínculos directos. Este panel brinda toda la información que necesitas para garantizar que los vínculos directos funcionen correctamente. Incluso puedes ver cómo será la versión para apps. Podrás ver los dominios, vínculos y vínculos personalizados que se agregaron en el archivo de manifiesto. También aprenderás dónde actualizar el archivo assetlinks.json en caso de problemas.

Panel de vínculos directos de Play Console con un Android App Link verificado.

9. Conclusión

Felicitaciones, creaste correctamente tu primera aplicación de Android App Links.

Ahora conoces el proceso necesario para diseñar, configurar, crear y probar tus Android App Links. Este proceso tiene diferentes etapas. Por eso, este codelab agrupa todos estos detalles para que puedas desarrollar correctamente con el SO Android.

Ahora conoces los pasos clave para que funcionen los Android App Links.

Lecturas adicionales

Documentos de referencia