Cómo usar la Biblioteca de apps de Android para vehículos

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 ciclo de vida 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.

Nivel de API de la app para vehículos

La Biblioteca de apps para vehículos define sus propios niveles de API, a fin de que puedas saber qué características de la biblioteca son compatibles con el host de la plantilla en cada vehículo. Para recuperar el nivel máximo de API de la app para vehículos admitido por un host, usa el método getCarAppApiLevel().

En el archivo AndroidManifest.xml, debes declarar el nivel mínimo de API de la app para vehículos compatible con tu app.

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

A fin de obtener detalles sobre cómo mantener la retrocompatibilidad y declarar el nivel mínimo de API necesario para una función, consulta la documentación de la anotación de RequiresCarApi. Si quieres obtener una definición del nivel de API que se requiere para usar una característica específica de la Biblioteca de apps para vehículos, consulta la documentación de referencia de CarAppApiLevels.

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:

Kotlin

class HelloWorldService : CarAppService() {
    ...
    override fun onCreateSession(): Session {
        return HelloWorldSession()
    }
    ...
}

Java

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:

Kotlin

class HelloWorldSession : Session() {
    ...
    override fun onCreateScreen(intent: Intent): Screen {
        return HelloWorldScreen()
    }
    ...
}

Java

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!":

Kotlin

class HelloWorldScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val row = Row.Builder().setTitle("Hello world!").build()
        val pane = Pane.Builder().addRow(row).build()
        return PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build()
    }
}

Java

public class HelloWorldScreen extends Screen {
    @NonNull
    @Override
    public Template onGetTemplate() {
        Row row = new Row.Builder().setTitle("Hello world!").build();
        Pane pane = new Pane.Builder().addRow(row).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:

Kotlin

val template = MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener { screenManager.push(NextScreen(carContext)) }
            .build())
    .build()

Java

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(
                () -> getScreenManager().push(new NextScreen(getCarContext())))
            .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 interactuar con el usuario

Tu app puede interactuar con el usuario a través de patrones similares a los de tu app para dispositivos móviles.

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:

Kotlin

val action = Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(::onClickNavigate)
    .build()

Java

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:

Kotlin

private fun onClickNavigate() {
    val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address))
    carContext.startCarApp(intent)
}

Java

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:

Kotlin

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

Java

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:

Kotlin

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

Java

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 ve en este fragmento:

Kotlin

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

Java

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

Solicitará permisos

Si la app debe acceder a datos o acciones restringidos (por ejemplo, acceso a la ubicación), las reglas estándar de permisos de Android también aplican. A fin de solicitar un permiso, puedes usar el método CarContext.requestPermissions(). En Android Auto, se mostrará el diálogo de permisos al usuario en el teléfono.

El beneficio de usar CarContext.requestPermissions() en lugar de las API de Android estándar es que no tienes que iniciar tu propia Activity con el único fin de crear el diálogo de permisos. Cuando se lance la compatibilidad con el SO Android Automotive más adelante en el año, podrás usar el mismo código en Android Auto y en el SO Android Automotive, en lugar de tener que crear flujos que dependen de la plataforma.

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:

Kotlin

val notification = notificationBuilder
    ...
    .extend(
        CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(ComponentName(context, MyNotificationReceiver::class.java)),
                    0))
            .build())

Java

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:

Kotlin

class MyNotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val intentAction = intent.action
        if (ACTION_VIEW_PARKING_RESERVATION == intentAction) {
            CarContext.startCarApp(
                intent,
                Intent(Intent.ACTION_VIEW)
                    .setComponent(ComponentName(context, MyCarAppService::class.java))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)))
        }
    }
}

Java

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:

Kotlin

override fun onNewIntent(intent: Intent) {
    val screenManager = carContext.getCarService(ScreenManager::class.java)
    val uri = intent.data
    if (uri != null
        && MY_URI_SCHEME == uri.scheme
        && MY_URI_HOST == uri.schemeSpecificPart
        && ACTION_VIEW_PARKING_RESERVATION == uri.fragment
    ) {
        val top = screenManager.top
        if (top !is ParkingReservationScreen) {
            screenManager.push(ParkingReservationScreen(carContext))
        }
    }
}

Java

@Override
public void onNewIntent(@NonNull Intent intent) {
    ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
    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.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 agregar un flujo de acceso

Si tu app ofrece una experiencia de acceso a los usuarios, puedes usar plantillas como SignInTemplate y LongMessageTemplate con la app para vehículos en el nivel de API 2 y versiones posteriores a fin de administrar el acceso a tu app en la consola central del vehículo.

A fin de crear un SignInTemplate, tendrás que definir un SignInMethod. Por el momento, la Biblioteca de apps para vehículos admite los siguientes tres métodos de acceso:

Por ejemplo, a fin de implementar una plantilla que recopile la contraseña del usuario, primero debes crear un InputCallback que procese y valide la entrada del usuario:

Kotlin

val callback = object : InputCallback {
    override fun onInputSubmitted(text: String) {
        // You will receive this callback when the user presses enter on the keyboard.
    }

    override fun onInputTextChanged(text: String) {
        // You will receive this callback as the user is typing. The update frequency is determined by the host.
    }
}

Java

InputCallback callback = new InputCallback() {
    @Override
    public void onInputSubmitted(@NonNull String text) {
        // You will receive this callback when the user presses enter on the keyboard.
    }

    @Override
    public void onInputTextChanged(@NonNull String text) {
        // You will receive this callback as the user is typing. The update frequency is determined by the host.
    }
};

Se requiere un InputCallback para el Builder de InputSignInMethod.

Kotlin

val passwordInput = InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build()

Java

InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build();

Por último, usa tu InputSignInMethod nuevo para crear un SignInTemplate.

Kotlin

SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build()

Java

new SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build();

Cómo agregar variantes de strings de texto

Es posible que se muestre una cantidad de texto diferente en distintos tamaños de pantallas de vehículos. Con la app para vehículos en el nivel de API 2 y versiones posteriores, puedes especificar múltiples variantes de una string de texto que se ajusten mejor a la pantalla. Para ver dónde se admiten variantes de texto, busca plantillas y componentes que acepten un CarText.

Puedes agregar variantes de string de texto a un CarText con el método CarText.Builder.addVariant() de la siguiente manera:

Kotlin

val itemTitle = CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build()

Java

CarText itemTitle = new CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build();

De esta manera, puedes usar este CarText, por ejemplo, como texto principal de un GridItem.

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

Agrega las strings en orden de preferencia, por ejemplo, de la más larga a la más corta. El host elegirá la string de longitud que corresponda en función de la cantidad de espacio disponible en la pantalla del vehículo.

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