Aprenda como cumprir estes tipos de comandos com interação por voz:
Cumprindo Comandos de Mídia
O comando relacionado à mídia pode ser dividido em três grupos diferentes:
- Fontes de mídia externas (como Spotify instalado em AAOS).
- Fontes de mídia de back-end (como música transmitida pelo VIA).
- Fontes de mídia locais (como rádio de carro).
Tratamento de comandos de fontes de mídia externa
Fontes de mídia externas são definidas como aplicativos Android que suportam APIs MediaSessionCompat
e MediaBrowseCompat
(consulte Criar aplicativos de mídia para carros para obter explicações detalhadas sobre o uso dessas APIs).
Importante. Para que um aplicativo assistente se conecte ao MediaBrowseService
de todos os aplicativos de mídia instalados no sistema, ele deve:
- Ser instalado como assinado pelo sistema (consulte as diretrizes de desenvolvimento de aplicativos de mídia para AAOS e o código de amostra
PackageValidator
). - Mantenha
android.permission.MEDIA_CONTENT_CONTROL
permissão privilegiada do sistema (consulte Conceder permissões privilegiadas do sistema ).
Além de MediaBrowserCompat e MediaControllerCompat , AAOS fornece o seguinte:
-
CarMediaService
fornece informações centralizadas sobre a fonte de mídia atualmente selecionada. Isso também é usado para retomar uma fonte de mídia reproduzida anteriormente após o desligamento e reinicialização do carro. -
car-media-common
fornece métodos convenientes para listar, conectar e interagir com aplicativos de mídia.
Abaixo estão diretrizes específicas para a implementação de comandos comuns de interação por voz.
Obtenha uma lista de fontes de mídia instaladas
As fontes de mídia podem ser detectadas usando PackageManager e filtrando serviços correspondentes a MediaBrowserService.SERVICE_INTERFACE . Em alguns carros pode haver algumas implementações especiais de serviços de navegador de mídia, que devem ser excluídas. Abaixo está um exemplo dessa lógica:
private Map<String, MediaSource> getAvailableMediaSources() { List<String> customMediaServices = Arrays.asList(mContext.getResources() .getStringArray(R.array.custom_media_packages)); List<ResolveInfo> mediaServices = mPackageManager.queryIntentServices( new Intent(MediaBrowserService.SERVICE_INTERFACE), PackageManager.GET_RESOLVED_FILTER); Map<String, MediaSource> result = new HashMap<>(); for (ResolveInfo info : mediaServices) { String packageName = info.serviceInfo.packageName; if (customMediaServices.contains(packageName)) { // Custom media sources should be ignored, as they might have a // specialized handling (e.g.: radio). continue; } String className = info.serviceInfo.name; ComponentName componentName = new ComponentName(packageName, className); MediaSource source = MediaSource.create(mContext, componentName); result.put(source.getDisplayName().toString().toLowerCase(), source); } return result; }
Esteja ciente de que as fontes de mídia podem ser instaladas ou desinstaladas a qualquer momento. Para manter uma lista precisa, é recomendado implementar um BroadcastReceiver para as seguintes ações de intenção: ACTION_PACKAGE_ADDED , ACTION_PACKAGE_CHANGED , ACTION_PACKAGE_REPLACED , ACTION_PACKAGE_REMOVED .
Conecte-se à fonte de mídia em reprodução no momento
CarMediaService fornece métodos para obter a fonte de mídia atualmente selecionada e quando essa fonte de mídia muda. Essas mudanças podem acontecer porque o usuário interagiu diretamente com a UI ou devido ao uso de botões de hardware no carro. Por outro lado, a biblioteca car-media-common oferece maneiras convenientes de conectar-se a uma determinada fonte de mídia. Aqui está um trecho simplificado sobre como se conectar ao aplicativo de mídia selecionado no momento:
public class MediaActuator implements MediaBrowserConnector.onConnectedBrowserChanged { private final Car mCar; private CarMediaManager mCarMediaManager; private MediaBrowserConnector mBrowserConnector; … public void initialize(Context context) { mCar = Car.createCar(context); mBrowserConnector = new MediaBrowserConnector(context, this); mCarMediaManager = (CarMediaManager) mCar.getCarManager(Car.CAR_MEDIA_SERVICE); mBrowserConnector.connectTo(mCarMediaManager.getMediaSource()); … } @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { // TODO: Handle connected/disconnected browser } … }
Controlar a reprodução da fonte de mídia em reprodução no momento
Com um MediaBrowserCompat conectado é muito simples enviar comandos de "controle de transporte" para o aplicativo de destino. Aqui está um exemplo simplificado:
public class MediaActuator … { … private MediaControllerCompat mMediaController; @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { if (browser != null && browser.isConnected()) { mMediaController = new MediaControllerCompat(mContext, browser.getSessionToken()); } else { mMediaController = null; } } private boolean playSongOnCurrentSource(String song) { if (mMediaController == null) { // No source selected. return false; } MediaControllerCompat.TransportControls controls = mMediaController.getTransportControls(); PlaybackStateCompat state = controller.getPlaybackState(); if (state == null || ((state.getActions() & PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH) == 0)) { // Source can't play from search return false; } controls.playFromSearch(query, null); return true; } … }
Tratamento de comandos de fonte de mídia local (rádio, CD player, Bluetooth, USB)
As fontes de mídia locais expõem sua funcionalidade ao sistema usando as mesmas APIs MediaSession e MediaBrowse detalhadas acima. Para acomodar as particularidades de cada tipo de hardware, esses serviços MediaBrowse utilizam convenções específicas para organizar suas informações e comandos de mídia.
Manuseio de rádio
O Radio MediaBrowseService pode ser identificado pelo filtro de intent ACTION_PLAY_BROADCASTRADIO . Espera-se que eles sigam os controles de reprodução e a estrutura de navegação de mídia descrita aqui: Implementando Rádio com Mídia . AAOS oferece a biblioteca car-broadcastradio-support contendo constantes e métodos para ajudar os OEMs a criar implementações MediaBrowseService para seus próprios serviços de rádio que seguem o protocolo definido e fornece suporte para aplicativos que consomem sua árvore de navegação (por exemplo, VIAs).
Manipulação de entrada auxiliar, áudio de CD e mídia USB
Não há implementação padrão dessas fontes de mídia como parte do AOSP. A abordagem sugerida é:
- Faça com que os OEMs implementem serviços de mídia para cada um deles. Para obter detalhes, consulte Criar aplicativos de mídia para carros .
- Essas implementações do MediaBrowseService seriam identificadas e respondidas nas ações de intenção definidas nas intenções do General Play .
- Esses serviços exporiam uma árvore de navegação seguindo as diretrizes descritas em Outros tipos de origem .
Lidando com Bluetooth
O conteúdo de mídia Bluetooth é exposto por meio do perfil Bluetooth AVRCP. Para facilitar o acesso a esta funcionalidade, o AAOS inclui uma implementação MediaBrowserService e MediaSession que abstrai os detalhes da comunicação (ver packages/apps/Bluetooth ).
A respectiva estrutura em árvore do navegador de mídia é definida na classe BrowseTree . Os comandos de controle de reprodução podem ser entregues de forma semelhante a qualquer outro aplicativo, usando sua implementação MediaSession.
Lidando com comandos de streaming de mídia
Para implementar o streaming de mídia no servidor, o VIA deve se tornar uma fonte de mídia, implementando a API MediaBrowse e MediaSession. Consulte Construir aplicativos de mídia para carros . Ao implementar essas APIs, um aplicativo de controle de voz seria capaz de (entre outras coisas):
- Participe perfeitamente na seleção da fonte de mídia
- Ser retomado automaticamente após a reinicialização do carro
- Forneça controle de reprodução e navegação usando a UI do Media Center
- Receba eventos de botão de mídia de hardware padrão
Cumprindo Comandos de Navegação
Não existe uma forma padronizada de interagir com todos os aplicativos de navegação. Para integrações com o Google Maps, consulte Google Maps para Android Automotive Intents . Para integrações com outros aplicativos, entre em contato diretamente com os desenvolvedores dos aplicativos. Antes de lançar uma intenção em qualquer aplicativo (incluindo o Google Maps), verifique se a intenção pode ser resolvida (consulte Solicitações de intenção ). Isso cria a oportunidade de informar o usuário caso o aplicativo de destino não esteja disponível.
Cumprindo Comandos de Veículos
O acesso às propriedades do veículo para leitura e gravação é fornecido por meio do CarPropertyManager . Os tipos de propriedades do veículo, sua implementação e outros detalhes são explicados aqui: AOSP/Develop/Automotive - Vehicle Properties . Para uma descrição precisa das propriedades suportadas pelo Android, é melhor consultar diretamente hardware/interfaces/automotive/vehicle/2.0/types.hal . O enum VehicleProperty definido contém propriedades padrão e específicas do fornecedor, tipos de dados, modo de alteração, unidades e definição de acesso de leitura/gravação.
Para acessar essas mesmas constantes em Java, você pode usar VehiclePropertyIds e suas classes complementares. Propriedades diferentes têm permissões Android diferentes controlando seu acesso. Essas permissões são declaradas no manifesto CarService e o mapeamento entre propriedades e permissões é descrito no Javadoc VehiclePropertyIds e aplicado em PropertyHalServiceIds .
Lendo a propriedade de um veículo
A seguir está um exemplo que mostra como ler a velocidade do veículo.
public class CarActuator ... { private final Car mCar; private final CarPropertyManager mCarPropertyManager; private final TextToSpeech mTTS; /** Global VHAL area id */ public static final int GLOBAL_AREA_ID = 0; public CarActuator(Context context, TextToSpeech tts) { mCar = Car.createCar(context); mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE); mTTS = tts; ... } @Nullable private void getSpeedInMetersPerSecond() { if (!mCarPropertyManager.isPropertyAvailable(VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID)) { mTTS.speak("I'm sorry, but I can't read the speed of this vehicle"); return; } // Data type and unit can be found in // automotive/vehicle/2.0/types.hal float speedInMps = mCarPropertyManager.getFloatProperty( VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID); int speedInMph = (int)(speedInMetersPerSecond * 2.23694f); mTTS.speak(String.format("Sure. Your current speed is %d miles " + "per hour", speedInUserUnit); } ... }
Definir uma propriedade de veículo
A seguir está um exemplo que mostra como ligar e desligar o AC frontal.
public class CarActuator … { … private void changeFrontAC(boolean turnOn) { List<CarPropertyConfig> configs = mCarPropertyManager .getPropertyList(new ArraySet<>(Arrays.asList( VehiclePropertyIds.HVAC_AC_ON))); if (configs == null || configs.size() != 1) { mTTS.speak("I'm sorry, but I can't control the AC of your vehicle"); return; } // Find the front area Ids for the AC property. int[] areaIds = configs.get(0).getAreaIds(); List<Integer> areasToChange = new ArrayList<>(); for (int areaId : areaIds) { if ((areaId & (VehicleAreaSeat.SEAT_ROW_1_CENTER | VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_RIGHT)) == 0) { continue; } boolean isACInAreaAlreadyOn = mCarPropertyManager .getBooleanProperty(VehiclePropertyIds.HVAC_AC_ON, areaId); if ((!isACInAreaAlreadyOn && turnOn) || (isACInAreaAlreadyOn && !turnOn)) { areasToChange.add(areaId); } } if (areasToChange.isEmpty()) { mTTS.speak(String.format("The AC is already %s", turnOn ? "on" : "off")); return; } for (int areaId : areasToChange) { mCarPropertyManager.setBooleanProperty( VehiclePropertyIds.HVAC_AC_ON, areaId, turnOn); } mTTS.speak(String.format("Okay, I'm turning your front AC %s", turnOn ? "on" : "off")); } … }
Cumprindo Comandos de Comunicação
Lidando com comandos de mensagens
Os VIAs devem lidar com mensagens recebidas seguindo o fluxo "tocar para ler" descrito em Voice Assistant Tap-to-Read , que pode opcionalmente lidar com o envio de respostas de volta ao remetente da mensagem recebida. Além disso, os VIAs podem usar o SmsManager (parte do pacote android.telephony ) para redigir e enviar mensagens SMS diretamente do carro ou via Bluetooth.
Tratamento de comandos de chamada
De maneira semelhante, os VIAs podem usar o TelephonyManager para fazer chamadas telefônicas e ligar para o número de correio de voz do usuário. Nestes casos, os VIAs irão interagir diretamente com a pilha de telefonia ou com o aplicativo Car Dialer. Em qualquer caso, o aplicativo Car Dialer deve ser aquele que exibe a interface do usuário relacionada à chamada de voz para o usuário.
Cumprindo Outros Comandos
Para uma lista de outros possíveis pontos de integração entre o VIA e o sistema, confira a lista de Android Intents bem conhecidos. Muitos comandos do usuário podem ser resolvidos no lado do servidor (por exemplo, leitura de e-mails de usuários e eventos de calendário) e não exigem nenhuma interação com o sistema além da própria interação de voz.
Ações imersivas (exibição de conteúdo visual)
Quando melhora as ações ou a compreensão do usuário, um VIA pode fornecer conteúdo visual complementar na tela do carro. Para minimizar a distração do motorista, mantenha esse conteúdo simples, breve e acionável. Para obter detalhes sobre as diretrizes de UI/UX sobre ações imersivas, consulte Assistentes pré-carregados: orientação de UX .
Para permitir a personalização e consistência com o restante do design da unidade principal (HU), os VIAs devem usar componentes da Car UI Library para a maioria dos elementos da UI. Para obter detalhes, consulte Personalização .