O Android Automotive considera a voz um componente crucial para
interações seguras ao dirigir e uma das maneiras mais seguras de os usuários
interagiarem com o Android Automotive OS enquanto dirigem. Como resultado, ampliamos as
APIs do Assistente por voz do Android (incluindo VoiceInteractionSession
)
para permitir que os assistentes por voz realizem tarefas para os usuários
que podem ser difíceis de realizar enquanto dirigem.
O recurso Tocar para ler permite que os assistentes de voz leiam e respondam a mensagens de texto em
nome do usuário quando ele interage com as notificações de mensagens. Para oferecer
essa funcionalidade, é possível integrar um assistente de voz com
CarVoiceInteractionSession
.
No Automotive, as notificações postadas na Central de notificações identificadas
como INBOX
ou INBOX_IN_GROUP
(por exemplo, mensagens SMS) incluem um
botão Tocar. O usuário pode clicar em Play para que o assistente de voz selecionado
leia a notificação em voz e, opcionalmente, responda por voz.
Figura 1. Notificação "Toque para ler" com botão "Abrir".
Integrar com CarVoiceInteractionSession
As próximas seções descrevem como integrar um assistente de voz com
CarVoiceInteractionSession
.
Oferecer suporte a interações por voz
Os apps que oferecem serviços de interação por voz no carro precisam
se integrar às interações por voz atuais do Android. Para saber mais, consulte Google Assistente para Android
(com exceção de VoiceInteractionSession
). Embora todos os elementos da API de interação por voz
continuem os mesmos implementados em dispositivos móveis, CarVoiceInteractionSession
(descrito em Implementar CarVoiceInteractionSession) substitui
VoiceInteractionSession
. Para ver mais informações, consulte estas páginas:
Implementar a CarVoiceInteractionSession
CarVoiceInteractionSession
expõe APIs que podem ser usadas para permitir que assistentes de voz leiam mensagens de texto em voz alta e
respondam a essas mensagens em nome do usuário.
A principal diferença entre as classes CarVoiceInteractionSession
e
VoiceInteractionSession
é que
CarVoiceInteractionSession
transmite a ação em onShow
para que o assistente de voz possa detectar o contexto da solicitação do usuário assim que
CarVoiceInteractionSession
inicia uma sessão. Os parâmetros de onShow
para cada classe estão listados na tabela a seguir:
CarVoiceInteractionSession | VoiceInteractionSession |
---|---|
onShow usa estes três parâmetros:
|
onShow usa estes dois parâmetros:
|
Mudanças no Android 10
A partir do Android 10, a plataforma chama VoiceInteractionService.onGetSupportedVoiceActions
para detectar quais ações são compatíveis. O assistente de voz substitui e
implementa VoiceInteractionService.onGetSupportedVoiceActions
,
conforme mostrado no exemplo a seguir:
public class MyInteractionService extends VoiceInteractionService { private static final ListSUPPORTED_VOICE_ACTIONS = Arrays.asList( CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION); @Override public Set onGetSupportedVoiceActions(@NonNull Set voiceActions) { Set result = new HashSet<>(voiceActions); result.retainAll(SUPPORTED_VOICE_ACTIONS); return result; } }
As ações válidas são descritas na tabela a seguir. Para saber mais sobre cada ação, consulte Diagramas de sequência.
Ação | Payload esperado | Ação de interação por voz esperada |
---|---|---|
VOICE_ACTION_READ_NOTIFICATION |
Leia as mensagens em voz alta para o usuário e, em seguida, envie a intent "Marcar como lida" quando as mensagens forem lidas. Opcionalmente, peça ao usuário para responder. | |
VOICE_ACTION_REPLY_NOTIFICATION |
Parcelável com chave.KEY_NOTIFICATION
que é mapeado para StatusBarNotification .Exige android.permission.BIND_NOTIFICATION_LISTENER_SERVICE . |
Peça ao usuário para declarar a mensagem de resposta, insira a mensagem de resposta em
RemoteInputReply da intent pendente e, em seguida, dispare a
intent pendente. |
VOICE_ACTION_HANDLE_EXCEPTION |
String com chave.KEY_EXCEPTION
que é mapeado para ExceptionValue
(descrito em Valores de exceção).KEY_FALLBACK_ASSISTANT_ENABLED que mapeia para um valor booleano. Se o valor
for true , o assistente alternativo que pode processar a solicitação do usuário foi
desativado. |
A ação esperada para a exceção é definida na documentação dela. |
Valores de exceção
EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING
indica ao assistente de voz que a permissão Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE
está faltando e que ele precisa conseguir essa permissão do usuário.
Solicitar permissão de listener de notificação
Se o assistente de voz padrão não tiver a permissão de listener
de notificação, o FallbackAssistant
da plataforma
(se ativado pelo fabricante do carro) poderá ler a mensagem em voz alta antes que o assistente de voz seja
notificado para solicitar a permissão. Para determinar se FallbackAssistant
está ativado e
leu a mensagem, o assistente de voz precisa verificar o
valor booleano KEY_FALLBACK_ASSISTANT_ENABLED
no payload.
A plataforma recomenda que o assistente por voz adicione uma lógica de limitação de taxa para
o número de vezes que essa permissão é solicitada. Isso respeita o usuário que não
quer conceder essa permissão ao assistente de voz e prefere que o
FallbackAssistant
leia as mensagens de texto em voz alta. Solicitar permissão
a um usuário toda vez que ele pressiona Play em uma notificação de mensagem
pode ser uma experiência negativa para o usuário. A plataforma não impõe limites de taxa
em nome do assistente por voz.
Ao solicitar a permissão do listener de notificação, o assistente de voz precisa
usar CarUxRestrictionsManager
para determinar se o usuário está estacionado ou dirigindo. Se o usuário estiver dirigindo, o assistente de voz
vai mostrar uma notificação com instruções sobre como conceder a permissão. Isso
ajuda (e lembra) o usuário de conceder a permissão quando for mais seguro.
Trabalhar com StatusBarNotification
O StatusBarNotification
transmitido com as ações de voz "Ler" e "Responder"
está sempre em uma notificação de mensagens compatível com o carro, conforme descrito
em Notificar
os usuários sobre mensagens. Embora algumas notificações não tenham a intent "Reply Pending",
todas têm intents pendentes de "Mark as Read".
Para agilizar as interações com notificações, use NotificationPayloadHandler
,
que fornece métodos para extrair mensagens da notificação e gravar as
mensagens de resposta na intent pendente adequada da notificação. Depois que o
assistente de voz ler a mensagem, ele precisa acionar a intent
Marcar como lida.
Atender às condições prévias do recurso "Toque para ler"
Apenas o VoiceInteractionSession
do assistente de voz
padrão é notificado quando um usuário aciona a ação de voz para ler e
responder a mensagens. Como mencionado acima, esse assistente de voz padrão também precisa
ter a permissão do listener de notificação.
Diagramas de sequência
Estas figuras mostram os fluxos lógicos de CarVoiceInteractionSession actions
:
Figura 2. Diagrama de sequência para VOICE_ACTION_READ_NOTIFICATION.
No caso da Figura 3, o app de limites de taxa em solicitações de permissão é recomendado:
Figura 3. Diagrama de sequência para VOICE_ACTION_REPLY_NOTIFICATION.
Figura 4. Diagrama de sequência para VOICE_ACTION_HANDLE_EXCEPTION.
Ler o nome do app
Se você quiser que o assistente de voz leia o nome do app de mensagens em voz durante a leitura da mensagem (por exemplo, "Sam do Hangouts disse..."), crie uma função como a mostrada no exemplo de código abaixo para garantir que o assistente esteja lendo o nome correto:
@Nullable String getMessageApplicationName(Context context, StatusBarNotification statusBarNotification) { ApplicationInfo info = getApplicationInfo(context, statusBarNotification.getPackageName()); if (info == null) return null; Notification notification = statusBarNotification.getNotification(); // Sometimes system packages will post on behalf of other apps, so check this // field for a system app notification. if (isSystemApp(info) && notification.extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) { return notification.extras.getString(Notification.EXTRA_SUBSTITUTE_APP_NAME); } else { PackageManager pm = context.getPackageManager(); return String.valueOf(pm.getApplicationLabel(info)); } } @Nullable ApplicationInfo getApplicationInfo(Context context, String packageName) { final PackageManager pm = context.getPackageManager(); ApplicationInfo info; try { info = pm.getApplicationInfo(packageName, 0); } catch (PackageManager.NameNotFoundException e) { return null; } return info; } boolean isSystemApp(ApplicationInfo info) { return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; }