La Biblioteca de apps de Android para vehículos te permite usar tus apps de navegación, lugares de interés e Internet de las cosas (IoT) 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 básica.
Antes de comenzar
- Consulta las páginas de Diseño para la conducción que abordan la Biblioteca de apps para vehículos
- Apps de navegación y descripciones generales de la categoría de otras apps relacionadas con la conducción
- Descripción general de la Compilación de apps con plantillas
- Componentes básicos que abordan Plantillas y Componentes de plantillas
- Flujos de ejemplo que demuestran patrones comunes de UX
- Requisitos de apps con plantillas
- Revisa los términos y conceptos clave que se indican en la sección siguiente.
- Familiarízate con la IU del sistema de Android Auto y el diseño del SO Android Automotive.
- Revisa las Notas de versión.
- Revisa las Muestras.
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 APIs 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. En el SO Android Automotive, este host se instala como una app del sistema.
- 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 para formar el flujo de una tarea. Por ejemplo, la app solo puede enviar hasta cinco plantillas a la pila de pantallas. Obtén más detalles en Restricciones de las plantillas.
Screen
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. UnaScreen
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 deScreen
también se pueden insertar en una pila deScreen
y quitarse de ella, lo que garantiza que cumplan con las restricciones del flujo de plantillas.CarAppService
CarAppService
es una clase abstracta deService
que tu app debe implementar y exportar para que el host la descubra y la administre. ElCarAppService
de tu app es responsable de validar que se pueda confiar en una conexión de host usandocreateHostValidator
y, luego, proporcionando instancias deSession
a cada conexión cononCreateSession
.Session
Session
es una clase abstracta que tu app debe implementar y mostrar usandoCarAppService.onCreateSession
. Funciona como punto de entrada para mostrar información en la pantalla del vehículo. 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 laScreen
inicial que se mostrará usando el métodoonCreateScreen
.
Cómo instalar la Biblioteca de apps para vehículos
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
Para poder crear tu app para vehículos, configura sus archivos de manifiesto como se indica a continuación.
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 para 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 para una app de lugares de interés 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.POI"/>
</intent-filter>
</service>
...
<application>
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 en el filtro de intents cuando declares tu CarAppService
como se describe en la sección anterior:
androidx.car.app.category.NAVIGATION
: Es una app que proporciona instrucciones de navegación paso a paso. Consulta Cómo compilar apps de navegación para automóviles.androidx.car.app.category.POI
: Es una app que brinda funcionalidad relevante para encontrar lugares de interés como estacionamientos, estaciones de carga y gasolineras. Consulta Cómo compilar apps de lugares de interés para autos.androidx.car.app.category.IOT
: Es una app que permite a los usuarios realizar acciones relevantes en dispositivos conectados desde el vehículo. Consulta Cómo compilar apps de Internet para autos.androidx.car.app.category.WEATHER
: Es una app que permite a los usuarios ver información del clima relevante relacionada con su ubicación actual o a lo largo de su ruta. Consulta Cómo compilar apps del clima para vehículos.
Consulta Calidad de las apps para Android para vehículos si deseas obtener descripciones detalladas de cada categoría y los criterios que cumplen las apps para pertenecer a ellas.
Especifica el nombre y el ícono de la app
Debes especificar un ícono y un nombre de app que pueda usar el host para representar tu app en la IU del sistema.
Puedes especificar el nombre de la app y el ícono que representarán a tu app con los atributos 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 <service>
, el host recurrirá a los valores especificados para el elemento <application>
.
Cómo establecer un tema predeterminado
Para establecer un tema predeterminado para tu app para vehículos, agrega un elemento <meta-data>
en tu archivo de manifiesto, de la siguiente manera:
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
Luego, declara tu recurso de estilo para configurar los siguientes atributos para el tema de tu app para vehículos:
<resources> <style name="MyCarAppTheme"> <item name="carColorPrimary">@layout/my_primary_car_color</item> <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item> <item name="carColorSecondary">@layout/my_secondary_car_color</item> <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
Nivel de API de la app para vehículos
La Biblioteca de apps para vehículos define sus propios niveles de API, para que puedas saber qué funciones 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
, declara 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>
Si deseas obtener detalles para 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 función 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 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(carContext) } ... }
Java
public final class HelloWorldSession extends Session { ... @Override @NonNull public Screen onCreateScreen(@NonNull Intent intent) { return new HelloWorldScreen(getCarContext()); } ... }
Para manejar situaciones en las que la app para vehículos deba iniciarse desde una pantalla que no sea la 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 usando 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
Para crear las pantallas que muestra tu app, define las clases que extienden la clase Screen
e implementa el método 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 cadena 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
a la que pueden acceder tus instancias de Session
y Screen
. Proporciona acceso a los servicios para vehículos, como ScreenManager
para administrar la pila de pantallas, AppManager
para la funcionalidad general relacionada con la app, como acceder al objeto Surface
para dibujar mapas y NavigationManager
, que usan las apps de navegación paso a paso para comunicar metadatos de navegación y otros eventos relacionados con la navegación con el host.
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.
CarContext
también ofrece otras funciones, como permitir que cargues recursos de elementos de diseño usando la configuración de la pantalla del vehículo, iniciar una app en el vehículo por medio de intents e indicar si tu app 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 selecciona un botón Atrás en la pantalla del vehículo o usa 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 de usar durante la conducción, la pila de pantallas puede tener un máximo de cinco pantallas. Consulta la sección de Restricciones de plantillas para obtener más detalles.
Cómo actualizar el contenido de una plantilla
Tu app puede solicitar que se invalide el contenido de una Screen
a través de 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 para que el host no descuente la plantilla nueva de la cuota de plantillas.
Consulta la sección de Restricciones de plantillas para obtener más detalles.
Te recomendamos que estructures tus 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 de onGetTemplate
.
Cómo dibujar mapas
Las apps de navegación y lugares de interés (POI) que usan las siguientes plantillas pueden dibujar mapas si acceden a un Surface
:
Plantilla | Permiso de plantilla | Orientación sobre las categorías |
---|---|---|
NavigationTemplate |
androidx.car.app.NAVIGATION_TEMPLATES |
Navegación |
MapWithContentTemplate |
androidx.car.app.NAVIGATION_TEMPLATES O androidx.car.app.MAP_TEMPLATES |
Navegación, lugares de interés, clima |
MapTemplate (obsoleto) |
androidx.car.app.NAVIGATION_TEMPLATES |
Navegación |
PlaceListNavigationTemplate (obsoleto) |
androidx.car.app.NAVIGATION_TEMPLATES |
Navegación |
RoutePreviewNavigationTemplate (obsoleto) |
androidx.car.app.NAVIGATION_TEMPLATES |
Navegación |
Declara el permiso de la plataforma
Además del permiso requerido para la plantilla que usa tu app, esta debe declarar el permiso androidx.car.app.ACCESS_SURFACE
en su archivo AndroidManifest.xml
para obtener acceso a la plataforma:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
...
</manifest>
Accede a la plataforma
Para acceder al Surface
que proporciona el host, debes implementar un SurfaceCallback
y proporcionar esa implementación al servicio de automóviles AppManager
. El Surface
actual se pasa a tu SurfaceCallback
en el parámetro SurfaceContainer
de las devoluciones de llamada onSurfaceAvailable()
y onSurfaceDestroyed()
.
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
Comprende el área visible de la superficie
El host puede dibujar elementos de la interfaz de usuario para las plantillas en la parte superior del mapa. El host comunica el área de la superficie que no tiene obstrucciones y es completamente visible para el usuario cuando se llama al método SurfaceCallback.onVisibleAreaChanged
. Además, para minimizar la cantidad de cambios, el host llama al método SurfaceCallback.onStableAreaChanged
con el rectángulo más pequeño, que siempre está 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 para dejar más espacio para el mapa. En este caso, hay una devolución de llamada a onStableAreaChanged
y onVisibleAreaChanged
con el mismo rectángulo. Cuando la barra de acciones está oculta, solo se llama a onVisibleAreaChanged
con el área más grande. Si el usuario interactúa con la pantalla, solo se llamará a onVisibleAreaChanged
con el primer rectángulo.
Cómo admitir el tema oscuro
Las apps deben volver a dibujar 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 Calidad de las apps para Android para vehículos.
Para decidir si quieres dibujar un mapa oscuro, puedes usar el método CarContext.isDarkMode
. Cuando cambie el estado del tema oscuro, recibirás una llamada a Session.onCarConfigurationChanged
.
Permite que los usuarios interactúen con tu mapa
Cuando uses las siguientes plantillas, puedes agregar compatibilidad para que los usuarios interactúen con los mapas que dibujas, por ejemplo, permitiéndoles ver diferentes partes de un mapa con el zoom y el desplazamiento lateral.
Plantilla | Compatible con la interactividad desde el nivel de API de la app para vehículos |
---|---|
NavigationTemplate |
2 |
PlaceListNavigationTemplate (obsoleto) |
4 |
RoutePreviewNavigationTemplate (obsoleto) |
4 |
MapTemplate (obsoleto) |
5 (introducción de la plantilla) |
MapWithContentTemplate |
7 (introducción de la plantilla) |
Implementa devoluciones de llamada de interactividad
La interfaz de SurfaceCallback
tiene varios métodos de devolución de llamada que puedes implementar para agregar interactividad a los mapas compilados con las plantillas de la sección anterior:
Interacción | Método SurfaceCallback |
Compatible desde el nivel de API de la app para vehículos |
---|---|---|
Presionar | onClick |
5 |
Pellizcar para acercar | onScale |
2 |
Arrastrar con un toque | onScroll |
2 |
Deslizar con un toque | onFling |
2 |
Presionar dos veces | onScale (con factor de escala determinado por el host de la plantilla) |
2 |
Rotar ligeramente en modo de desplazamiento lateral | onScroll (con factor de distancia determinado por el host de la plantilla) |
2 |
Agrega una barra de acciones en mapa
Estas plantillas pueden tener una barra de acciones relacionadas con el mapa, como acercar y alejar la imagen, volver a centrar, mostrar una brújula y otras acciones que decidas mostrar. La barra de acciones del mapa puede tener hasta cuatro botones de ícono que pueden actualizarse sin afectar la profundidad de la tarea. Se oculta durante el estado inactivo y vuelve a aparecer durante el estado activo.
Para recibir devoluciones de llamada de interactividad del mapa, debes agregar un botón Action.PAN
en la barra de acciones del mapa. Cuando el usuario presiona el botón de desplazamiento lateral, el host ingresa al modo de desplazamiento, como se describe en la siguiente sección.
Si tu app omite el botón Action.PAN
en la barra de acciones en mapa, no recibirás entradas del usuario de los métodos SurfaceCallback
, por lo que el host saldrá del modo de desplazamiento lateral que se haya activado antes.
En una pantalla táctil, no se mostrará el botón de desplazamiento lateral.
Información sobre el modo de desplazamiento lateral
En el modo de desplazamiento lateral, el host de la plantilla convierte la entrada del usuario proveniente de dispositivos de entrada no táctiles, como controladores rotativos y paneles táctiles, en los métodos SurfaceCallback
correspondientes. Puedes responder a la acción del usuario de ingresar o salir del modo de desplazamiento lateral con el método setPanModeListener
en el NavigationTemplate.Builder
. El host puede ocultar otros componentes de la IU en la plantilla mientras el usuario se encuentra en el modo de desplazamiento lateral.
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 con 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 la sección Cómo iniciar una app para vehículos con un intent.
Algunas acciones, como las que requieren que se dirija al usuario para continuar la interacción en sus dispositivos móviles, solo se permiten cuando el automóvil está estacionado.
Puedes usar el elemento ParkedOnlyOnClickListener
para implementar esas acciones. Si el vehículo no está estacionado, el host muestra una indicación al usuario de que no se permite la acción en ese caso. Si el vehículo está estacionado, el código se ejecuta 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 muestran 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.
Puedes elegir la configuración de las notificaciones de tu app de modo que afecten cada uno de los elementos de la interfaz usando 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 true
, se muestra una única notificación de prioridad alta como una notificación de atención.
Si deseas obtener más información sobre el diseño de las notificaciones de tu app para vehículos, consulta la guía de Google Design para conducción, en la sección sobre las notificaciones.
Cómo mostrar avisos
Tu app puede mostrar un aviso a través de 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();
Solicita permisos
Si la app debe acceder a datos o acciones restringidos (por ejemplo, a la ubicación), se aplican las reglas estándar de permisos de Android. A fin de solicitar un permiso, puedes usar el método CarContext.requestPermissions()
.
El beneficio de usar CarContext.requestPermissions()
, en lugar de usar las APIs estándar de Android, es que no necesitas iniciar tu propia Activity
para crear el diálogo de permisos. Además, 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.
Aplica diseño al diálogo de permisos en Android Auto
En Android Auto, se mostrará el diálogo de permisos al usuario en el teléfono.
De forma predeterminada, no habrá fondo detrás del diálogo. Para establecer un fondo personalizado, declara un tema para apps para vehículos en tu archivo AndroidManifest.xml
y configura el atributo carPermissionActivityLayout
para el tema de tu app.
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
Luego, configura el atributo carPermissionActivityLayout
para el tema de tu app para vehículos:
<resources> <style name="MyCarAppTheme"> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
Cómo iniciar una app para vehículos con un intent
Puedes llamar al método CarContext.startCarApp
para 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 con 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())); } } }
Consulta la sección Visualización de notificaciones para obtener más información sobre el control de notificaciones de la app para vehículos.
Restricciones de plantillas
El host limita la cantidad de plantillas que se muestran para una tarea determinada a un máximo de cinco, de las cuales la última debe ser de uno de los siguientes tipos:
Ten en cuenta que este límite se aplica a la cantidad de plantillas y no a la de instancias de Screen
de la pila. Por ejemplo, si una app envía dos plantillas mientras muestra la pantalla A y, luego, envía la pantalla B, podrá enviar tres plantillas más. De manera alternativa, si se estructura cada pantalla para enviar una sola plantilla, la app podrá enviar cinco 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, si una app envía 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 la app envía dos plantillas mientras muestra la pantalla A y, luego, envía la pantalla B y dos plantillas adicionales, tendrá una cuota remanente. Si luego la app regresa a la pantalla A, el host restablecerá la cuota a tres, porque la app retrocedió dos plantillas.
Ten en cuenta que, cuando vuelvas a una pantalla, la app deberá enviar una plantilla del mismo tipo que la que la pantalla envió por última vez. Si envía cualquier otro tipo de plantilla, se mostrará 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 alcance una de estas plantillas, el host restablecerá la cuota de plantillas y tratará esa plantilla como si fuera el primer paso de una tarea nueva. Esto permitirá que la app comience 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 la sección Visualización de notificaciones para obtener más detalles para mostrar las notificaciones de tu app en la pantalla del vehículo. Consulta la sección Cómo iniciar una app para vehículos con un intent si deseas obtener información para iniciar tu app desde una acción de notificación.
API de Connection
Puedes determinar si tu app está ejecutándose en Android Auto o en el SO Android Automotive usando la API de CarConnection
para recuperar la información de conexión durante el tiempo de ejecución.
Por ejemplo, en la Session
de tu app, inicializa una CarConnection
y suscríbete a las actualizaciones de LiveData
:
Kotlin
CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)
Java
new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);
En el observador, puedes reaccionar a los cambios en el estado de conexión:
Kotlin
fun onConnectionStateUpdated(connectionState: Int) { val message = when(connectionState) { CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit" CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS" CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto" else -> "Unknown car connection type" } CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show() }
Java
private void onConnectionStateUpdated(int connectionState) { String message; switch(connectionState) { case CarConnection.CONNECTION_TYPE_NOT_CONNECTED: message = "Not connected to a head unit"; break; case CarConnection.CONNECTION_TYPE_NATIVE: message = "Connected to Android Automotive OS"; break; case CarConnection.CONNECTION_TYPE_PROJECTION: message = "Connected to Android Auto"; break; default: message = "Unknown car connection type"; break; } CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show(); }
API de Constraints
Los vehículos pueden admitir distintas cantidades de instancias de Item
que se mostrarán al usuario en un determinado momento. Usa el ConstraintManager
para verificar el límite de contenido en el tiempo de ejecución y establece la cantidad apropiada de elementos en tus plantillas.
Comienza por obtener un ConstraintManager
de CarContext
:
Kotlin
val manager = carContext.getCarService(ConstraintManager::class.java)
Java
ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);
Luego, puedes consultar el objeto recuperado ConstraintManager
para el límite de contenido relevante. Por ejemplo, para obtener la cantidad de elementos que pueden mostrarse en una cuadrícula, llama a getContentLimit
con CONTENT_LIMIT_TYPE_GRID
:
Kotlin
val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)
Java
int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);
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 el nivel de API 2 y posteriores de la app para vehículos para administrar el acceso a tu app en la consola central del vehículo.
Para crear una SignInTemplate
, define un SignInMethod
. Por el momento, la Biblioteca de apps para vehículos admite los siguientes métodos de acceso:
InputSignInMethod
para el acceso con nombre de usuario y contraseñaPinSignInMethod
para el acceso con código PIN, en el que el usuario vincula su cuenta desde el teléfono con un PIN que aparece en la consola centralProviderSignInMethod
para el acceso de proveedor, como el Acceso con Google y One Tap.QRCodeSignInMethod
para el acceso con código QR, en el que el usuario escanea un código QR para completar el acceso en su teléfono (esto está disponible con el nivel 4 de la API de Car y posteriores)
Por ejemplo, para 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();
Usa AccountManager
Las apps del SO Android Automotive que tienen autenticación deberán usar AccountManager por los siguientes motivos:
- Mejor UX y administración sencilla de la cuenta: Los usuarios podrán administrar fácilmente todas sus cuentas desde el menú de cuentas en la configuración del sistema, lo que incluye el acceso y el cierre de sesión.
- Experiencias de "invitado": Como los automóviles son dispositivos compartidos, los OEM pueden habilitar las experiencias de "invitado" en el vehículo, cuando no se puedan agregar cuentas.
Cómo agregar variantes de cadenas 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 cadena 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 cadenas en orden de preferencia, por ejemplo, de la más larga a la más corta. El host elegirá la cadena de longitud que corresponda en función de la cantidad de espacio disponible en la pantalla del vehículo.
Cómo agregar CarIcons intercalados para filas
Puedes agregar íconos intercalados con texto para enriquecer el atractivo visual de tu app con CarIconSpan
.
Consulta la documentación de CarIconSpan.create
si deseas obtener más información para crear estos intervalos. Consulta Cómo usar intervalos para dar un estilo increíble al texto para ver una descripción general del funcionamiento de los estilos de texto con intervalos.
Kotlin
val rating = SpannableString("Rating: 4.5 stars") rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) val row = Row.Builder() ... .addText(rating) .build()
Java
SpannableString rating = new SpannableString("Rating: 4.5 stars"); rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars new CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ); Row row = new Row.Builder() ... .addText(rating) .build();
APIs de Car Hardware
A partir del nivel de API 3 de la app para vehículos, la Biblioteca de apps para vehículos tiene APIs que puedes usar para acceder a propiedades y sensores del vehículo.
Requisitos
Para usar las APIs con Android Auto, comienza agregando una dependencia en androidx.car.app:app-projected
en el archivo build.gradle
para tu módulo de Android Auto. Para el SO Android Automotive, agrega una dependencia en androidx.car.app:app-automotive
en el archivo build.gradle
para tu módulo del SO Android Automotive.
Además, en tu archivo AndroidManifest.xml
, debes declarar los permisos relevantes y necesarios para requerir los datos del vehículo que quieres usar. Ten en cuenta que el usuario debe otorgarte estos permisos también. 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. Sin embargo, los permisos necesarios son diferentes.
CarInfo
En esta tabla, se describen las propiedades que mostrarán las APIs de CarInfo
APIs y los permisos que debes solicitar para usarlas:
Métodos | Propiedades | Permisos de Android Auto | Permisos del SO Android Automotive | Compatible desde el nivel de API de la app para vehículos |
---|---|---|---|---|
fetchModel |
Marca, modelo, año | android.car.permission.CAR_INFO |
3 | |
fetchEnergyProfile |
Tipos de conectores de VE, tipos de combustible | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_INFO |
3 |
fetchExteriorDimensions
Estos datos solo están disponibles en algunos vehículos con el SO Android Automotive que ejecutan el nivel de API 30 o versiones posteriores. |
Dimensiones exteriores | N/A | android.car.permission.CAR_INFO |
7 |
addTollListener
removeTollListener |
Estado de tarjeta de peaje, tipo de tarjeta de peaje | 3 | ||
addEnergyLevelListener
removeEnergyLevelListener |
Nivel de batería, nivel de combustible, nivel de combustible bajo, rango remanente | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_ENERGY ,android.car.permission.CAR_ENERGY_PORTS ,android.car.permission.READ_CAR_DISPLAY_UNITS
|
3 |
addSpeedListener
removeSpeedListener |
Velocidad sin procesar, velocidad en pantalla (se muestra en la pantalla de clúster del vehículo) | com.google.android.gms.permission.CAR_SPEED |
android.car.permission.CAR_SPEED ,android.car.permission.READ_CAR_DISPLAY_UNITS |
3 |
addMileageListener
removeMileageListener |
Distancia del odómetro | com.google.android.gms.permission.CAR_MILEAGE |
Estos datos no están disponibles en el SO Android Automotive OS para apps instaladas desde Play Store. | 3 |
Por ejemplo, para obtener el rango remanente, crea una instancia del objeto CarInfo
y, luego, crea y registra un OnCarDataAvailableListener
:
Kotlin
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo val listener = OnCarDataAvailableListener<EnergyLevel> { data -> if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) { val rangeRemaining = data.rangeRemainingMeters.value } else { // Handle error } } carInfo.addEnergyLevelListener(carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener)
Java
CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo(); OnCarDataAvailableListener<EnergyLevel> listener = (data) -> { if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) { float rangeRemaining = data.getRangeRemainingMeters().getValue(); } else { // Handle error } }; carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener);
No asumas que los datos del vehículo estarán disponibles en todo momento.
Si recibes un error, verifica el estado del valor que solicitaste para entender mejor por qué no se pudieron recuperar los datos requeridos. Consulta la documentación de referencia para obtener la definición completa de la clase CarInfo
.
CarSensors
La clase CarSensors
te brinda acceso al acelerómetro, al giroscopio, a la brújula y a los datos de ubicación del vehículo. La disponibilidad de estos valores podría depender del OEM. El formato de los datos del acelerómetro, del giroscopio y de la brújula es igual al que obtendrías de la API de SensorManager
. Por ejemplo, para verificar la orientación del vehículo, haz lo siguiente:
Kotlin
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors val listener = OnCarDataAvailableListener<Compass> { data -> if (data.orientations.status == CarValue.STATUS_SUCCESS) { val orientation = data.orientations.value } else { // Data not available, handle error } } carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener)
Java
CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors(); OnCarDataAvailableListener<Compass> listener = (data) -> { if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) { List<Float> orientations = data.getOrientations().getValue(); } else { // Data not available, handle error } }; carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener);
Para acceder a los datos de ubicación del vehículo, también debes declarar y requerir el permiso android.permission.ACCESS_FINE_LOCATION
.
Prueba
Para simular los datos de sensores cuando realizas pruebas en Android Auto, consulta las secciones Sensores y Configuración de sensores de la guía de la consola central de escritorio. Para simular los datos de sensores cuando realizas pruebas en el SO Android Automotive, consulta la sección Cómo emular el estado del hardware de la guía del emulador del SO Android Automotive.
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
Si deseas obtener información detallada, consulta la documentación del método Session.getLifecycle
.
Ciclo de vida de una Screen
Si deseas obtener información detallada, consulta la documentación del método Screen.getLifecycle
.
Cómo grabar con el micrófono del vehículo
Usando el elemento CarAppService
de tu app y la API de CarAudioRecord
, puedes otorgarle a la app el acceso al micrófono del vehículo del usuario. Los usuarios deben otorgar a la app el permiso para acceder al micrófono del vehículo. Tu app puede grabar y procesar la entrada del usuario dentro de tu app.
Permiso de grabación
Antes de grabar audio, debes declarar el permiso de grabación en tu archivo AndroidManifest.xml
y requerir al usuario que lo otorgue.
<manifest ...>
...
<uses-permission android:name="android.permission.RECORD_AUDIO" />
...
</manifest>
Debes solicitar el permiso de grabación en el tiempo de ejecución. Consulta la sección Solicita permisos si deseas obtener información detallada para solicitar un permiso en tu app para vehículos.
Grabación de audio
Luego de que el usuario otorgue el permiso para grabar, podrás grabar el audio y procesar la grabación.
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) carAudioRecord.startRecording() val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording()
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); carAudioRecord.startRecording(); byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE]; while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording();
Foco de audio
Para grabar con el micrófono del vehículo, primero debes obtener el foco de audio para asegurarte de que se detenga todo contenido multimedia en reproducción. Si pierdes el foco de audio, deja de grabar.
Este es un ejemplo de la forma de obtener el foco de audio:
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) // Take audio focus so that user's media is not recorded val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build() val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener { state: Int -> if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording() } } .build() if (carContext.getSystemService(AudioManager::class.java) .requestAudioFocus(audioFocusRequest) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED ) { // Don't record if the focus isn't granted return } carAudioRecord.startRecording() // Process the audio and abandon the AudioFocusRequest when done
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); // Take audio focus so that user's media is not recorded AudioAttributes audioAttributes = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build(); AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener(state -> { if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording(); } }) .build(); if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest) != AUDIOFOCUS_REQUEST_GRANTED) { // Don't record if the focus isn't granted return; } carAudioRecord.startRecording(); // Process the audio and abandon the AudioFocusRequest when done
Biblioteca de pruebas
La biblioteca de pruebas de Android para vehículos brinda clases auxiliares que puedes usar para 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 a través de la herramienta de seguimiento de errores de Google. Asegúrate de completar toda la información solicitada en la plantilla de problemas.
Antes de informar un error nuevo, revisa si aparece en las notas de la versión de la biblioteca o en la lista de errores. 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.