Nesta página, descrevemos como executar comandos com interação por voz.
Atender comandos de mídia
Os comandos relacionados a mídia podem ser divididos em três grupos diferentes:
- Fontes de mídia externas (como o Spotify instalado no AAOS).
- Fontes de mídia de back-end (como músicas reproduzidas por streaming pelo VIA).
- Fontes de mídia local (como rádio do carro).
Processar comandos de fontes de mídia externas
Fontes de mídia externas são definidas como apps Android que oferecem suporte às APIs MediaSessionCompat
e MediaBrowseCompat.
Consulte Criar apps de mídia para
carros para uma explicação detalhada sobre o uso dessas APIs.
Importante:para que um app assistente se conecte ao
MediaBrowseService de todos os apps de mídia instalados no
sistema, ele precisa:
- Ser instalado como assinado pelo sistema. Consulte as diretrizes de desenvolvimento de aplicativos de mídia para
AAOS e o exemplo de código
PackageValidator. - Ter a permissão
android.permission.MEDIA_CONTENT_CONTROLprivilegiada do sistema (consulte Conceder permissões privilegiadas do sistema).
Além de MediaBrowserCompat
e MediaControllerCompat,
o AAOS oferece o seguinte:
CarMediaServicefornece informações centralizadas sobre a fonte de mídia selecionada no momento. Isso também é usado para retomar uma fonte de mídia que estava sendo reproduzida antes de o carro ser desligado e reiniciado.- O
car-media-commonoferece métodos convenientes para listar, conectar e interagir com apps de mídia.
Confira abaixo diretrizes específicas para a implementação de comandos comuns de interação por voz.
Receber uma lista de fontes de mídia instaladas
As fontes de mídia podem ser detectadas usando PackageManager e filtrando os serviços que correspondem ao MediaBrowserService.SERVICE_INTERFACE.
Em alguns carros, pode haver implementações especiais de serviços do navegador de mídia que precisam ser excluídas. Confira 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; }
As fontes de mídia podem ser instaladas ou desinstaladas a qualquer momento. Para manter uma lista precisa, recomendamos implementar uma instância BroadcastReceiver para as ações de intent ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REPLACED e ACTION_PACKAGE_REMOVED.
Conectar à fonte de mídia em reprodução
O CarMediaService
fornece métodos para receber a fonte de mídia selecionada no momento e quando ela
muda. Essas mudanças podem acontecer porque o usuário interagiu diretamente com a
interface ou devido ao uso de botões de hardware no carro. Por outro lado, a biblioteca car-media-common oferece maneiras convenientes de se conectar a uma determinada fonte de mídia. Confira um snippet simplificado de como se conectar ao app 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 exibição
Com um MediaBrowserCompat conectado, é fácil enviar comandos de controle de transporte ao app de destino. Confira 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; } … }
Processar comandos de fontes de mídia locais (rádio, CD player, Bluetooth, USB)
As fontes de mídia locais expõem a 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 usam convenções específicas para organizar as informações e os comandos de mídia.
Manusear rádio
O Radio MediaBrowseService pode ser identificado pelo filtro de intent ACTION_PLAY_BROADCASTRADIO. Eles precisam seguir os controles de reprodução e a estrutura de navegação de mídia descritos em Implementar rádio. O AAOS oferece a biblioteca
car-broadcastradio-support
com constantes e métodos para ajudar os OEMs a criar implementações do MediaBrowseService
para os próprios serviços de rádio que seguem o protocolo definido
e oferece suporte a apps que consomem a árvore de navegação (por exemplo, VIAs).
Processar entrada auxiliar, áudio de CD e mídia USB
Não há uma implementação padrão dessas fontes de mídia como parte do AOSP. A abordagem sugerida é:
- Peça aos OEMs para implementar serviços de mídia para cada um deles. Para mais detalhes, consulte Criar apps de mídia para carros.
- Essas implementações do MediaBrowseService seriam identificadas e respondidas nas ações de intent definidas em Intents gerais de reprodução.
- Esses serviços expõem uma árvore de navegação seguindo as diretrizes descritas em Outros tipos de fontes.
Processar Bluetooth
O conteúdo de mídia Bluetooth é exposto pelo perfil Bluetooth AVRCP. Para facilitar o acesso a essa funcionalidade, o AAOS inclui uma implementação de MediaBrowserService e MediaSession que abstrai os detalhes de comunicação (consulte packages/apps/Bluetooth).
A respectiva estrutura de árvore do navegador de mídia é definida na classe BrowseTree. Os comandos de controle de reprodução podem ser entregues de maneira semelhante a qualquer outro app, usando a implementação da MediaSession.
Processar comandos de streaming de mídia
Para implementar o streaming de mídia do lado do servidor, a VIA precisa se tornar uma fonte de mídia, implementando a API MediaBrowse e MediaSession. Consulte Criar apps de mídia para carros. Ao implementar essas APIs, um app de controle de voz poderá, entre outras coisas:
- Participar da seleção de fontes de mídia sem problemas
- Ser retomada automaticamente após a reinicialização do carro
- Fornecer controle de navegação e reprodução usando a interface do Media Center
- Receber eventos padrão de botões de mídia de hardware
Executar comandos de navegação
Não há uma maneira padronizada de interagir com todos os apps de navegação. Para integrações com o Google Maps, consulte Intents do Google Maps para Android Automotive. Para integrações com outros apps, entre em contato diretamente com os desenvolvedores. Antes de iniciar uma intent para qualquer app (incluindo o Google Maps), verifique se ela pode ser resolvida. Consulte Intents do Google Maps. Isso cria a oportunidade de informar o usuário caso o app de destino não esteja disponível.
Atender comandos do veículo
O acesso às propriedades do veículo para leitura e gravação é fornecido pelo
CarPropertyManager.
Os tipos de propriedades do veículo, a implementação e outros detalhes são explicados em Configurações de propriedade. Para uma descrição precisa das propriedades compatíveis com o Android, consulte diretamente hardware/interfaces/automotive/vehicle/2.0/types.hal.
O enumerador VehicleProperty definido lá contém propriedades padrão e específicas do fornecedor, tipos de dados, modo de mudança, unidades e definição de acesso de leitura/gravação.
Para acessar essas mesmas constantes em Java, use VehiclePropertyIds e as classes complementares. Diferentes propriedades têm permissões do Android diferentes que controlam o acesso delas. 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.
Ler uma propriedade do veículo
Confira um exemplo de 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 do veículo
Confira um exemplo de como ligar e desligar o ar-condicionado dianteiro.
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"));
}
…
}Atender comandos de comunicação
Processar comandos de mensagens
Os VIAs precisam processar as mensagens recebidas seguindo o fluxo "tocar para ler" descrito em Assistente por voz: tocar para ler, que pode processar o envio de respostas ao remetente da mensagem recebida.
Além disso, os VIAs podem usar SmsManager
(parte do pacote android.telephony
) para compor e enviar mensagens SMS diretamente do carro ou por Bluetooth.
Processar comandos de chamada
Da mesma forma, os VIAs podem usar TelephonyManager
para fazer ligações e ligar para o número da caixa postal do usuário. Nesses casos, os VIAs interagem diretamente com a pilha de telefonia ou com o app Car Dialer. Em qualquer caso, o app Car Dialer é o que mostra a interface relacionada a chamadas de voz para o usuário.
Executar outros comandos
Para uma lista de outros possíveis pontos de integração entre o VIA e o sistema, confira a lista de intentos do Android conhecidos. Muitos comandos do usuário podem ser resolvidos no lado do servidor (por exemplo, ler e-mails e eventos da agenda dos usuários) e não exigem interações com o sistema além da própria interação por voz.
Ações imersivas (mostrar conteúdo visual)
Quando isso melhora as ações ou o entendimento do usuário, um VIA pode fornecer conteúdo visual complementar na tela do carro. Para minimizar a distração do motorista, mantenha esse tipo de conteúdo simples, breve e prático. Para detalhes sobre as diretrizes de UI/UX em ações imersivas, consulte Assistentes pré-carregados: orientação de UX.
Para permitir a personalização e a consistência com o restante do design da unidade principal (HU), as VIAs precisam usar componentes da Biblioteca Car UI para a maioria dos elementos da interface. Para mais detalhes, consulte Personalização.