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