Cómo compilar apps de carga, navegación y estacionamiento para Android Auto

La Biblioteca de apps de Android para vehículos te permite usar tus apps de navegación, estacionamiento y carga en el vehículo. A tal efecto, proporciona un conjunto de plantillas diseñadas para cumplir con los estándares de distracción del conductor y tener en cuenta los detalles, como la variedad de factores de pantalla del vehículo y las modalidades de entrada.

En esta guía, se proporciona una descripción general de las funciones y los conceptos clave de la biblioteca, y se te guiará en el proceso de configuración de una app simple.

Antes de comenzar

  1. Revisa los lineamientos de diseño de la biblioteca de apps de Android para vehículos.
  2. Revisa los términos y conceptos clave que se indican en esta sección.
  3. Familiarízate con la IU del sistema de Android Auto.
  4. Revisa las Notas de la versión.
  5. Revisa las Muestras.
  6. [Solo para la biblioteca de código cerrado] Revisa las Condiciones de Uso de la biblioteca de apps de Android para vehículos.

Términos y conceptos clave

Modelos y plantillas
La interfaz de usuario se representa con un gráfico de objetos modelo que pueden organizarse de manera diferente según lo que permita la plantilla a la que pertenecen. Las plantillas son un subconjunto de los modelos que pueden actuar como una raíz en esos gráficos. Los modelos incluyen la información que se le mostrará al usuario, en forma de imágenes y texto, así como atributos para configurar aspectos de la apariencia visual de esa información (por ejemplo, colores de texto o tamaños de imagen). El host convierte los modelos en vistas que están diseñadas para cumplir con los estándares de distracción del conductor y se encarga de detalles como la variedad de modalidades de entrada y la de los factores de pantalla de los vehículos.
Host
El host es el componente del backend que implementa la funcionalidad que ofrecen las API de la biblioteca para que tu app se ejecute en el vehículo. Las responsabilidades del host van desde descubrir tu app y administrar su ciclo de vida hasta convertir tus modelos en vistas y notificar a tu app de las interacciones del usuario. En dispositivos móviles, Android Auto implementa este host.
Restricciones de plantillas
Varias plantillas aplican restricciones en el contenido de sus modelos. Por ejemplo, las plantillas de listas tienen una cantidad limitada de elementos que se pueden presentar al usuario. Las plantillas también tienen restricciones en relación con la forma en que se pueden conectar a fin de formar el flujo de una tarea. Por ejemplo, la app solo puede enviar hasta 5 plantillas a la pila de pantalla. Consulta Restricciones de plantillas para obtener más detalles.
Pantalla
Una Screen es una clase proporcionada por la biblioteca que las apps implementan a los efectos de administrar la interfaz de usuario que se presenta a este último. Una Screen tiene un lifecycle y proporciona el mecanismo necesario de modo que la app envíe la plantilla que se mostrará cuando la pantalla sea visible. Las instancias de Screen también se pueden insertar en una pila de pantallas y sacarse de ella, lo que garantiza que cumplan con las restricciones del flujo de plantilla.
CarAppService
Un CarAppService es una clase de Service abstracta que tu app debe implementar y exportar a los efectos de que el host la descubra y la administre. El CarAppService de tu app es responsable de validar que se pueda confiar en una conexión de host mediante CarAppService.createHostValidator. y, luego, de proporcionar instancias de Session a cada conexión mediante CarAppService.onCreateSession.
Sesión
Una Session es una clase abstracta que tu app debe implementar y mostrar por medio de CarAppService.onCreateSession. Funciona como punto de entrada para mostrar información en la pantalla del vehículo y tiene un ciclo de vida que informa el estado actual de tu app en la pantalla del vehículo, por ejemplo, si la app está visible u oculta.

Cuando se inicia una Session (por ejemplo, cuando se inicia la app por primera vez), el host solicita la Screen inicial que se mostrará por medio del método Session.onCreateScreen.

Cómo instalar la biblioteca

Si deseas obtener instrucciones para agregar la biblioteca a tu app, visita la página de versiones de la biblioteca de Jetpack.

Cómo configurar los archivos del manifiesto de tu app

Antes de crear una app para vehículos, debes configurar sus archivos del manifiesto.

Declara tu CarAppService

El host se conecta a tu app a través de tu implementación de CarAppService. Debes declarar este servicio en el manifiesto a fin de permitir que el host descubra tu app y se conecte a ella.

También debes declarar la categoría de tu app en el elemento category del filtro de intents de la app. Si quieres conocer los valores permitidos para este elemento, consulta la lista de categorías de apps compatibles.

En el siguiente fragmento de código, se muestra cómo declarar un servicio de app para vehículos correspondiente a una app de estacionamiento en tu manifiesto:

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService"/>
        <category android:name="androidx.car.app.category.PARKING"/>
      </intent-filter>
    </service>

    ...
<application>

Categorías de app compatibles

A fin de que tu app aparezca en Play Store para Android Auto, debe pertenecer a una de las categorías de apps compatibles. Para declarar la categoría de tu app, agrega uno o más de los siguientes valores de categoría compatibles en el filtro de intents cuando declaras el servicio de tu app para vehículos:

  • androidx.car.app.category.NAVIGATION: Una app que proporciona instrucciones de navegación paso a paso
  • androidx.car.app.category.PARKING: Una app que proporciona funciones relevantes a los efectos de encontrar lugares para estacionar
  • androidx.car.app.category.CHARGING: Una app que proporciona funciones relevantes en relación con encontrar estaciones de carga de vehículos eléctricos

Consulta Calidad de las apps para Android para vehículos a fin de obtener una descripción detallada y los criterios de categorización de las apps.

Especifica el nombre y el ícono de la app

Debes especificar un ícono y un nombre de app que pueda usar el host a fin de representar tu app en la IU del sistema.

Puedes especificar el nombre de la app y el ícono que representarán a tu app mediante los elementos label y icon de tu CarAppService:

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

Si no se declaran la etiqueta ni el ícono en el elemento de service, el host recurrirá a los valores especificados para la aplicación.

Establece el valor de minSdkVersion de tu app

Android Auto requiere que tu app se oriente a Android 6.0 (nivel de API 23) o una versión posterior.

Para especificar este valor en tu proyecto, establece el atributo minSdkVersion del elemento uses-sdk en 23 o un valor superior en el archivo AndroidManifest.xml del módulo de tu aplicación para teléfonos, como se muestra en el siguiente ejemplo:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
    ...
</manifest>

Declara la compatibilidad con Android Auto

El host de Android Auto verifica que se haya declarado la compatibilidad de la app con este producto. Para habilitar esta compatibilidad, incluye la siguiente entrada en el manifiesto de tu app:

<application>
    ...
    <meta-data
        android:name="com.google.android.gms.car.application"
        android:resource="@xml/automotive_app_desc"/>
    ...
</application>

Esta entrada de manifiesto hace referencia a otro archivo en formato XML que debes crear con la ruta de acceso YourAppProject/app/src/main/res/xml/automotive_app_desc.xml, en la que declaras las capacidades de Android Auto que son compatibles con tu app.

Las apps que usan la biblioteca de la app de Android para vehículos deben declarar la capacidad de la template en el archivo automotive_app_desc.xml:

<automotiveApp>
    <uses name="template" />
</automotiveApp>

Cómo crear tu CarAppService y Session

La app necesita extender la clase CarAppService e implementar el método CarAppService.onCreateSession, lo que muestra una instancia de Session correspondiente a la conexión actual al host:

public final class HelloWorldService extends CarAppService {
  ...
  @Override
  @NonNull
  public Session onCreateSession() {
    return new HelloWorldSession();
  }
  ...
}

La instancia de Session es responsable de mostrar la instancia de Screen que se usará la primera vez que se inicie la app:

public final class HelloWorldSession extends Session {
  ...
  @Override
  @NonNull
  public Screen onCreateScreen(@NonNull Intent intent) {
    return new HelloWorldScreen();
  }
  ...
}

Para manejar situaciones en las que la app para vehículos deba iniciarse desde una pantalla que no sea la pantalla de inicio o la de destino de tu app (como el manejo de vínculos directos), puedes completar previamente una pila de actividades de pantallas mediante ScreenManager.push antes de mostrarlas desde onCreateScreen. Esta acción previa permite a los usuarios volver a las pantallas anteriores desde la primera pantalla que muestre tu app.

Cómo crear tu pantalla de inicio

A fin de crear las pantallas que muestra tu app, define las clases que extienden la clase Screen e implementa el método Screen.onGetTemplate, que muestra la instancia de la Template que representa el estado de la IU que se mostrará en la pantalla del vehículo.

En el siguiente fragmento, se muestra cómo declarar una Screen que usa una plantilla PaneTemplate para mostrar una string simple de "Hello World!":

public class HelloWorldScreen extends Screen {
  @NonNull
  @Override
  public Template onGetTemplate() {
    Pane pane = new Pane.Builder()
        .addRow(new Row.Builder()
            .setTitle("Hello world!")
            .build())
        .build();
    return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
  }
}

La clase CarContext

La clase CarContext es una subclase de ContextWrapper que puede acceder a tus instancias de Session y Screen, lo cual brinda acceso a servicios para vehículos, como el ScreenManager a los efectos de administrar la pila de pantallas, el AppManager para funciones generales relacionadas con la app, como acceder al objeto Surface a fin de dibujar el mapa de tu app de navegación, y el NavigationManager usado en apps de navegación paso a paso a los efectos de comunicar al host metadatos de navegación y otros eventos relacionados con la navegación. Si quieres obtener una lista completa de las funciones de la biblioteca disponibles para las apps de navegación, consulta Accede a las plantillas de navegación.

El CarContext también ofrece otras funciones, como permitir la carga de recursos de elementos de diseño mediante la configuración de la pantalla del vehículo, iniciar una app en el vehículo usando intents e indicar si tu app de navegación debe mostrar su mapa en modo oscuro.

Cómo implementar la navegación en pantalla

A menudo, las apps presentan diferentes pantallas, cada cual con plantillas posiblemente distintas, que el usuario puede navegar, ya que interactúan con la interfaz que se muestra en la pantalla.

La clase ScreenManager proporciona una pila de pantallas que puedes usar para enviar pantallas que se puedan mostrar automáticamente cuando el usuario seleccione un botón Atrás en la pantalla del vehículo o use el botón Atrás de hardware disponible en algunos automóviles.

En el siguiente fragmento, se muestra cómo agregar una acción de regreso a la plantilla de mensajes y una que envíe una pantalla nueva cuando el usuario la seleccione:

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(() -> getScreenManager().push(new NextScreen()))
            .build())
    .build();

El objeto Action.BACK es una Action estándar que invoca automáticamente ScreenManager.pop. Este comportamiento se puede anular con la instancia OnBackPressedDispatcher disponible en CarContext.

Para garantizar que la app sea segura mientras conduces, la pila de pantallas podrá tener una profundidad máxima de 5 pantallas. Si deseas obtener más detalles, consulta Restricciones de las plantillas.

Cómo actualizar el contenido de una plantilla

Tu app puede solicitar que se invalide el contenido de una Screen mediante una llamada al método Screen.invalidate. Luego, el host volverá a llamar al método Screen.onGetTemplate de tu app para recuperar la plantilla con el contenido nuevo.

Cuando se actualiza una Screen, es importante comprender el contenido específico de la plantilla que se puede actualizar a fin de que el host no descuente la plantilla nueva de la cuota de plantillas. Obtén más detalles en Restricciones de las plantillas.

Te recomendamos que estructures las pantallas de modo que haya una asignación uno a uno entre una Screen y el tipo de plantilla que muestra a través de su implementación Screen.onGetTemplate.

Cómo controlar las entradas del usuario

Tu app puede responder a las entradas del usuario pasando los objetos de escucha adecuados a los modelos compatibles. En el siguiente fragmento, se muestra cómo crear un modelo de Action que establece un OnClickListener que realiza una devolución de llamada a un método definido por el código de tu app:

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

Luego, el método onClickNavigate podrá iniciar la app predeterminada de navegación para vehículos mediante el método CarContext.startCarApp:

private void onClickNavigate() {
  Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
  getCarContext().startCarApp(intent);
}

Si deseas obtener más información para iniciar apps, incluido el formato del intent ACTION_NAVIGATE, consulta Cómo iniciar una app para vehículos con un intent.

Algunas acciones, como las que requieren que se dirija al usuario a fin de continuar la interacción en sus dispositivos móviles, solo se permiten cuando el automóvil está estacionado. Puedes usar el ParkedOnlyOnClickListener para implementar esas acciones. Si el automóvil no está estacionado, el host mostrará una indicación al usuario de que no se permite la acción en ese caso. Si el automóvil está estacionado, el código se ejecutará con normalidad. En el siguiente fragmento, se muestra cómo usar el ParkedOnlyOnClickListener para abrir una pantalla de configuración en el dispositivo móvil:

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(
        ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)
    .build();

Visualización de notificaciones.

Las notificaciones enviadas al dispositivo móvil solo se mostrarán en la pantalla del vehículo si se extienden con un CarAppExtender. Algunos atributos de notificación, como el título, el texto, el ícono y las acciones, se pueden configurar en el CarAppExtender, el cual anulará esos atributos cuando aparezcan en la pantalla del vehículo.

En el siguiente fragmento, se muestra cómo enviar una notificación a la pantalla del vehículo que muestra un título diferente al que se muestra en el dispositivo móvil:

Notification notification =
    new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
        .setContentTitle(titleOnThePhone)
        .extend(
            new CarAppExtender.Builder()
                .setContentTitle(titleOnTheCar)
                ...
                .build())
        .build();

Las notificaciones pueden afectar las siguientes partes de la interfaz de usuario:

  • Es posible que se muestre una notificación emergente (HUN, por su sigla en inglés) al usuario.
  • Se puede agregar una entrada en el centro de notificaciones, opcionalmente con un distintivo visible en el riel.
  • En el caso de las apps de navegación, la notificación puede mostrarse en el widget de riel como se describe en Notificaciones paso a paso.

Las aplicaciones pueden elegir cómo configurar sus notificaciones para afectar a cada uno de esos elementos de la interfaz de usuario mediante la prioridad de la notificación, como se describe en la documentación de CarAppExtender.

Si se llama a NotificationCompat.Builder.setOnlyAlertOnce con un valor de true, se mostrará una notificación de prioridad alta como una HUN solo una vez.

Si deseas obtener más información para diseñar las notificaciones de tu app para vehículos, consulta Notificaciones.

Cómo mostrar avisos

Tu app puede mostrar un aviso mediante CarToast, como se muestra en este fragmento:

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

Cómo iniciar una app para vehículos con un intent

Puedes llamar al método CarContext.startCarApp a fin de realizar una de las siguientes acciones:

  • Abrir el marcador a los efectos de realizar una llamada
  • Iniciar la navegación paso a paso a una ubicación mediante la app de navegación predeterminada
  • Iniciar tu propia app con un intent

En el siguiente ejemplo, se muestra cómo crear una notificación por medio de una acción que abre tu app en una pantalla que muestra los detalles de una reserva de estacionamiento. Extiende la instancia de notificación con un intent de contenido que tenga un PendingIntent uniendo un intent explícito a la acción de tu app:

Notification notification =
    notificationBuilder.
        …
        .extend(
            new CarAppExtender.Builder()
                .setContentIntent(
                    PendingIntent.getBroadcast(
                        context,
                        ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                        new Intent(ACTION_VIEW_PARKING_RESERVATION)
                            .setComponent(
                                new ComponentName(context, MyNotificationReceiver.class)),
                        0))
                .build())

La app también debe declarar un BroadcastReceiver que se invoque para procesar el intent cuando el usuario seleccione la acción en la interfaz de notificación y, luego, invoque CarContext.startCarApp con un intent que incluya el URI de datos:

public class MyNotificationReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    String intentAction = intent.getAction();
    if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) {
      CarContext.startCarApp(
         intent,
          new Intent(Intent.ACTION_VIEW)
              .setComponent(new ComponentName(context, MyCarAppService.class))
              .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)));
    }
  }
}

Por último, el método Session.onNewIntent de tu app controlará este intent mostrando la pantalla de reserva de estacionamiento de la pila, si es que no está en la parte superior:

@Override
public void onNewIntent(@NonNull Intent intent) {
  Uri uri = intent.getData();
  if (uri != null
      && MY_URI_SCHEME.equals(uri.getScheme())
      && MY_URI_HOST.equals(uri.getSchemeSpecificPart())
      && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment())) {

    Screen top = screenManager.getTop();
    if (!(top instanceof ParkingReservationScreen)) {
      ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
      screenManager.push(new ParkingReservationScreen(getCarContext()));
    }
  }
}

Si quieres obtener más información para manejar las notificaciones de la app para vehículos, consulta Visualización de notificaciones.

Restricciones de plantillas

El host limita la cantidad de plantillas que se muestran para una tarea determinada a un máximo de 5, de las cuales la última debe ser de uno de los siguientes tipos:

  1. NavigationTemplate
  2. PaneTemplate
  3. MessageTemplate

Ten en cuenta que este límite se aplica al número de plantillas y no al número de instancias de Screen de la pila. Por ejemplo, si mientras está en la pantalla A una app envía 2 plantillas y, luego, muestra la pantalla B, la app podrá enviar 3 plantillas más. De manera alternativa, si se estructura cada pantalla para enviar una sola plantilla, la app podrá enviar 5 instancias de pantalla a la pila ScreenManager.

Existen casos especiales en función de estas restricciones: actualizaciones de plantillas y operaciones de retroceso y restablecimiento.

Actualizaciones de plantillas

Algunas actualizaciones de contenido no se toman en cuenta para el límite de plantillas. En general, siempre que una app envíe una plantilla nueva que sea del mismo tipo y contenga el mismo contenido principal que la plantilla anterior, la nueva no se descontará de la cuota. Por ejemplo, actualizar el estado de activación de una fila en una ListTemplate no descuenta de la cuota. Si quieres obtener más información sobre los tipos de modificaciones de contenido que pueden considerarse una actualización, consulta la documentación de las plantillas individuales.

Operaciones de retroceso

Para habilitar subflujos dentro de una tarea, el host detecta el envío por parte de la app de una Screen de la pila de ScreenManager y actualiza la cuota restante según la cantidad de plantillas que la app está retrocediendo.

Por ejemplo, si mientras está en la pantalla A, la app envía 2 plantillas y, luego, muestra la pantalla B y envía 2 plantillas más, la app tendrá 1 cuota restante. Si ahora la app regresa a la pantalla A, el host restablecerá la cuota a 3, porque la app retrocedió 2 plantillas.

Ten en cuenta que, cuando vuelves a una pantalla, la app deberá enviar una plantilla del mismo tipo que la que la pantalla envió por última vez. Enviar cualquier otro tipo de plantilla generaría un error. Sin embargo, siempre que el tipo se mantenga igual durante una operación de retroceso, la app podrá modificar libremente el contenido de la plantilla sin afectar la cuota.

Operaciones de restablecimiento

Algunas plantillas tienen una semántica especial que indica el final de una tarea. Por ejemplo, la NavigationTemplate es una vista que se espera que permanezca en pantalla y se actualice con instrucciones nuevas paso a paso para el consumo del usuario. Cuando se llega a una de estas plantillas, el host restablece la cuota de plantillas y trata esa plantilla como si fuera el primer paso de una tarea nueva, lo que permite que la app inicie una tarea nueva. Consulta la documentación de las plantillas individuales para ver cuáles activan un restablecimiento en el host.

Si el host recibe un intent para iniciar la app desde una acción de notificación o desde el selector, la cuota también se restablecerá. Este mecanismo permite que una app inicie un nuevo flujo de tareas a partir de notificaciones, y se aplica incluso si una app ya está vinculada y en primer plano.

Consulta Visualización de notificaciones a fin de obtener más información para mostrar las notificaciones de tu app en la pantalla del vehículo y Cómo iniciar una app para vehículos con un intent de modo que comprendas cómo iniciar tu app desde una acción de notificación.

Cómo compilar una app de estacionamiento o carga

En esta sección, se detallan las diferentes funciones de la biblioteca que puedes usar a fin de implementar la funcionalidad de tu app de estacionamiento o carga.

Declara la compatibilidad de categoría en tu manifiesto

Tu app debe declarar la categoría de app para vehículos androidx.car.app.category.PARKING o androidx.car.app.category.CHARGING en el filtro de intents de su CarAppService. Por ejemplo:

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.PARKING"/>
      </intent-filter>
    </service>
    ...
<application>

Accede a la plantilla de mapas

Las apps pueden acceder a la PlaceListMapTemplate, específicamente diseñada para mostrar una lista de lugares de interés junto con un mapa renderizado por el host.

Para poder acceder a esta plantilla, tu app debe declarar el permiso androidx.car.app.MAP_TEMPLATES en su AndroidManifest.xml:

<uses-permission android:name="androidx.car.app.MAP_TEMPLATES"/>

Cómo compilar una app de navegación

En esta sección, se detallan las diferentes funciones de la biblioteca que puedes usar a fin de implementar la funcionalidad de tu app de navegación paso a paso.

Declara la compatibilidad de navegación en tu manifiesto

Tu app de navegación debe declarar la categoría de app para vehículos androidx.car.app.category.NAVIGATION en el filtro de intents de su CarAppService:

<application>
    ...
   <service
       ...
        android:name=".MyNavigationCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.NAVIGATION"/>
      </intent-filter>
    </service>
    ...
<application>

Admite intents de navegación

A los efectos de admitir intents de navegación en tu app, incluidos aquellos que provienen de Asistente de Google mediante una consulta por voz, tu app debe controlar el intent CarContext.ACTION_NAVIGATE en su Session.onCreateScreen y Session.onNewIntent.

Si deseas obtener detalles sobre el formato del intent, consulta la documentación de CarContext.startCarApp.

Accede a las plantillas de navegación

Las apps de navegación pueden acceder a las siguientes plantillas diseñadas específicamente para ellas. Todas estas plantillas muestran una superficie en segundo plano a la que tu app puede acceder con el fin de dibujar el mapa, junto con otra información proporcionada por la app que varía según la plantilla.

  • NavigationTemplate: Muestra el mapa junto con un mensaje informativo opcional o las instrucciones sobre cómo llegar y las estimaciones de viaje durante la navegación activa.
  • PlaceListNavigationTemplate: Muestra una lista de lugares que pueden tener marcadores correspondientes en el mapa.
  • RoutePreviewNavigationTemplate: Muestra una lista de rutas que se pueden seleccionar y destacar en el mapa.

Obtén más información para diseñar la interfaz de usuario de tu app de navegación con esas plantillas. Consulta los lineamientos de diseño de la biblioteca de la app de Android para vehículos.

A los efectos de obtener acceso a las plantillas de navegación, tu app debe declarar el permiso androidx.car.app.NAVIGATION_TEMPLATES en su AndroidManifest.xml:

<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>

Cómo dibujar el mapa

Las aplicaciones de navegación pueden acceder a una Surface para dibujar el mapa en plantillas relevantes.

Se puede acceder a un objeto SurfaceContainer configurando una instancia de SurfaceCallback al servicio para vehículos de AppManager:

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

La SurfaceCallback proporciona una devolución de llamada cuando el SurfaceContainer está disponible junto con otras devoluciones de llamada cuando cambian las propiedades de la Surface.

Para obtener acceso a la superficie, tu app debe declarar el permiso androidx.car.app.ACCESS_SURFACE en su AndroidManifest.xml:

<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>

El área visible del mapa

El host puede dibujar elementos de la interfaz de usuario para las diferentes plantillas ubicadas en la parte superior del mapa. El host comunicará el área que no está oculta y resulta completamente visible para el usuario mediante una llamada a SurfaceCallback.onVisibleAreaChanged. Además, a fin de minimizar la cantidad de cambios, el host también llamará al método SurfaceCallback.onStableAreaChanged con el rectángulo más grande que estará visible en función de la plantilla actual.

Por ejemplo, cuando una app de navegación usa la NavigationTemplate con una barra de acción en la parte superior, esa barra podrá ocultarse cuando el usuario no haya interactuado con la pantalla durante un tiempo a fin de dejar más espacio para el mapa. En este caso, habrá una devolución de llamada a onStableAreaChanged y onVisibleAreaChanged con el mismo rectángulo. Cuando la barra de acciones esté oculta, solo se llamará a onVisibleAreaChanged con el área más grande. En caso de que el usuario interactúe con la pantalla, solo se llamará a onVisibleAreaChanged con el primer rectángulo.

Modo oscuro

Las aplicaciones de navegación deben rediseñar su mapa en la instancia de Surface con los colores oscuros apropiados cuando el host determine que las condiciones lo requieren, como se describe en los lineamientos de calidad de las apps para Android Auto.

A los efectos de decidir si debes dibujar un mapa oscuro, puedes usar el método CarContext.isDarkMode. Cuando cambie el estado del modo oscuro, recibirás una llamada a Session.onCarConfigurationChanged.

Las aplicaciones de navegación deben comunicar metadatos de navegación adicionales al host. El host usa esa información con el fin de proporcionarla a la consola central del vehículo y evitar conflictos de las aplicaciones de navegación en relación con los recursos compartidos.

Los metadatos de navegación se proporcionan a través del servicio para automóviles de NavigationManager, al que se puede acceder desde el CarContext:

NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
Cómo iniciar, finalizar y detener la navegación

A los efectos de que el host administre varias apps de navegación, notificaciones de rutas y datos del clúster del vehículo, debe conocer el estado actual de la navegación. Cuando un usuario inicia la navegación, la app debe llamar a NavigationManager.navigationStarted. Del mismo modo, cuando finaliza la navegación (por ejemplo, cuando el usuario llega a su destino o cancela la navegación), la app debe llamar a NavigationManager.navigationEnded.

Solo debes llamar a NavigationManager.navigationEnded cuando el usuario termine de usar la navegación. En cambio, si necesitas volver a calcular la ruta en medio de un viaje, usa Trip.Builder.setLoading(true).

En ocasiones, el host necesitará una app para detener la navegación y llamará a stopNavigation en un objeto NavigationManagerListener proporcionado por tu app a través de NavigationManager.setListener. En este caso, la app deberá dejar de emitir la información de próximo paso en la pantalla del clúster, las notificaciones de navegación y las indicaciones por voz.

Información del viaje

Durante la navegación activa, la app deberá llamar a NavigationManager.updateTrip. La información que se brinde en esta llamada se usará en las pantallas del clúster y de aviso del vehículo. Según el vehículo en cuestión, es posible que no se muestre toda la información al usuario. Por ejemplo, la consola central de escritorio muestra el Step agregado a Trip, pero no muestra la información de Destination.

A fin de probar que la información llega al clúster, se puede configurar la herramienta de consola central de escritorio (DHU) de modo que muestre una pantalla de clúster simple. Crea un archivo cluster.ini con el siguiente contenido:

[general]
instrumentcluster = true

Luego, puedes invocar la DHU con un parámetro de línea de comandos adicional:

dhu -c cluster.ini

Notificaciones paso a paso

Se pueden proporcionar instrucciones de navegación paso a paso (TBT) por medio de una notificación de navegación actualizada con frecuencia. Para que se la considere una notificación de navegación en la pantalla del vehículo, el compilador de la notificación deberá hacer lo siguiente:

  1. Marcar la notificación como en curso mediante el método NotificationCompat.Builder.setOngoing
  2. Establecer la categoría de la notificación en Notification.CATEGORY_NAVIGATION
  3. Extender la notificación con un CarAppExtender

Se mostrará una notificación de navegación en el widget de riel, en la parte inferior de la pantalla del vehículo. Si el nivel de importancia de la notificación se establece en IMPORTANCE_HIGH, también se mostrará como una notificación emergente (HUN). Si no se establece la importancia mediante el método CarAppExtender.Builder.setImportance, se usará la importancia del canal de notificaciones.

La app puede establecer un PendingIntent en el CarAppExtender que se le enviará cuando el usuario presione la HUN o el widget de riel.

Si se llama a NotificationCompat.Builder.setOnlyAlertOnce con un valor true, la HUN tendrá una única alerta de notificación de importancia alta.

En el siguiente fragmento, se muestra cómo compilar una notificación de navegación:

new NotificationCompat.Builder(context, myNotificationChannelId)
    ...
    .setOnlyAlertOnce(true)
    .setOngoing(true)
    .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(carScreenTitle)
            ...
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_OPEN_APP.hashCode(),
                    new Intent(ACTION_OPEN_APP)
                        .setComponent(
                            new ComponentName(context, MyNotificationReceiver.class)),
                    0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
    .build())
Lineamientos para las notificaciones paso a paso

Las apps de navegación deben actualizar la notificación TBT con frecuencia sobre los cambios de distancia, lo que actualizará el widget de riel y solo mostrará la notificación como una HUN. Para controlar el comportamiento de las HUN, las apps establecen la importancia de la notificación mediante el método CarAppExtender.Builder.setImportance. Establecer la importancia en IMPORTANCE_HIGH mostrará una HUN. Si lo estableces en cualquier otro valor, solo se actualizará el widget de riel.

Indicaciones por voz

A fin de reproducir las indicaciones de navegación en las bocinas del vehículo, tu app deberá solicitar foco de audio. Como parte de tu AudioFocusRequest, deberás establecer el uso en AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE. También deberás establecer la ganancia del foco en AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Cómo simular la navegación

Para verificar la funcionalidad de navegación de tu app cuando la envíes a Google Play Store, esta deberá implementar la devolución de llamada NavigationManagerCallback.onAutoDriveEnabled. Cuando se realice esta devolución de llamada, la app deberá simular la navegación al destino seleccionado cuando el usuario inicie la navegación. Tu app podrá salir de este modo cuando el ciclo de vida de la Session actual alcance el estado Lifecycle.Event#ON_DESTROY.

Puedes probar si se llama a tu implementación de onAutoDriveEnabled ejecutando lo siguiente desde una línea de comandos:

adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE

Por ejemplo:

adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE

App de vehículo de navegación predeterminada

En Android Auto, la app de vehículo de navegación predeterminada corresponde a la última app de navegación que inició el usuario. Esta es la app que, por ejemplo, recibirá intents de navegación cuando el usuario invoque comandos de navegación mediante Asistente o cuando otra app envíe un intent para iniciar la navegación.

Los ciclos de vida de CarAppService, Session y Screen

Las clases Session y Screen implementan la interfaz LifecycleOwner. A medida que el usuario interactúe con la app, se invocarán las devoluciones de llamada de ciclo de vida de tus objetos Session y Screen, como se describe en los siguientes diagramas.

Ciclos de vida de un CarAppService y una Session

Figura 1: El ciclo de vida de Session

Si deseas obtener información detallada, consulta la documentación del método Session.getLifecycle.

Ciclo de vida de una Screen

Figura 2: El ciclo de vida de Screen

Si deseas obtener información detallada, consulta la documentación de Screen.getLifecycle.

Biblioteca de pruebas

La biblioteca de pruebas de Android para vehículos brinda clases auxiliares que puedes usar a fin de validar el comportamiento de tus apps en un entorno de pruebas. Por ejemplo, el elemento SessionController te permite simular una conexión al host, y verificar que se crean y se muestran los objetos Screen y Template correctos.

Consulta las Muestras para ver ejemplos de uso.

Cómo ejecutar la app en una consola central real

A fin de que se ejecute en una consola central real (no en la consola central de escritorio que proporcionamos), tu app deberá distribuirse mediante Google Play Store. Esto garantizará que tu aplicación se haya probado de modo que se confirme que cumple con nuestros lineamientos. Estos lineamientos garantizan que tu aplicación es relevante para el entorno del vehículo y que pasó nuestras pruebas de distracción del conductor.

Para la prueba en desarrollo, existen tres opciones:

  • Usar la consola central de escritorio
  • Enviar tu aplicación a internal test track de Google Play Store (el segmento de pruebas internas te permitirá agregar a tu equipo de forma manual, lo que permitirá que se realicen dichas pruebas. Los lanzamientos en este segmento no requieren revisiones de Play Store.)
  • Compartir tu aplicación mediante internal app sharing en Google Play Console, que al igual que el segmento de pruebas internas, no requiere opiniones sobre Play Store

Cuando publiques tu APK en cualquier otro segmento, incluidos los cerrados, tu app se someterá a un proceso de revisión antes de que se apruebe su inclusión en ese segmento de Play Store. Si la aplicación no pasa el proceso de revisión, recibirás información sobre el motivo correspondiente. Este proceso te permitirá corregir cualquier problema de modo que se cumplan nuestros lineamientos.

Cómo informar un problema en la biblioteca de apps de Android para vehículos

Si encuentras un problema en la biblioteca, infórmalo mediante la herramienta de seguimiento de errores de Google. Asegúrate de completar toda la información solicitada en la plantilla del problema.

Cómo crear un error nuevo

Antes de informar un problema nuevo, revisa si aparece en las notas de la versión de la biblioteca o en la lista de problemas. Para suscribirte a un problema o votarlo, haz clic en el ícono de estrella que aparece en la herramienta de seguimiento. Si deseas obtener más información, consulta Cómo suscribirte a un problema.