Recurso "Toque para ler" do assistente de voz

O Android Automotive considera a voz um componente crucial para interações seguras no Google Drive e uma das maneiras mais seguras de interagem com o Android Automotive OS enquanto dirige. Como resultado, expandimos a APIs de assistente de voz do Android (incluindo VoiceInteractionSession) para permitir que assistentes de voz realizem tarefas para os usuários que podem ser difíceis de realizar ao dirigir.

O recurso Tocar para ler permite que assistentes de voz leiam e respondam a mensagens de texto no em nome do usuário, quando ele interage com as notificações de mensagem. Para fornecer essa funcionalidade, é possível integrar um assistente de voz 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 Reproduzir. O usuário pode clicar em Reproduzir para que o O assistente de voz lê a notificação em voz alta e, se quiser, pode responder por voz.

Notificação "Toque para ler"

Figura 1. Notificação "Toque para ler" com o botão "Reproduzir".

Integrar com CarVoiceInteractionSession

As próximas seções descrevem como integrar um assistente de voz CarVoiceInteractionSession:

Suporte a interações por voz

Apps que oferecem serviços de interação por voz no carro precisam se integram às interações de voz existentes do Android. Para saber mais, consulte Google Assistente para Android. (exceto VoiceInteractionSession). Embora todas as APIs de interação por voz permanecem os mesmos elementos implementados em dispositivos móveis, CarVoiceInteractionSession (descrito em Implementar CarVoiceInteractionSession), substitui VoiceInteractionSession Para ver mais informações, consulte estas páginas:

Implementar CarVoiceInteractionSession

CarVoiceInteractionSession expõe APIs que podem ser usadas para permitir que assistentes de voz leiam mensagens de texto em voz alta e responder a essas mensagens em nome do usuário.

A principal diferença entre o 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. Parâmetros para onShow para cada classe estão listados na tabela a seguir:

CarVoiceInteractionSession VoiceInteractionSession (em inglês)
onShow usa estes três parâmetros:
  • args
  • showFlags
  • actions
onShow usa estes dois parâmetros:
  • args
  • showFlags

Mudanças no Android 10

No Android 10 e versões mais recentes, a plataforma chama VoiceInteractionService.onGetSupportedVoiceActions para detectar quais ações têm suporte. O assistente de voz substitui e implementa VoiceInteractionService.onGetSupportedVoiceActions, conforme mostrado neste exemplo:

public class MyInteractionService extends VoiceInteractionService {
    private static final List SUPPORTED_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 obter detalhes 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 depois dispare o botão "Marcar como lida" pendente quando as mensagens forem lidas. Opcionalmente, solicite o usuário para receber uma resposta.
VOICE_ACTION_REPLY_NOTIFICATION Parcelable com chave.
KEY_NOTIFICATION que mapeia para StatusBarNotification.
Requer android.permission.BIND_NOTIFICATION_LISTENER_SERVICE.
Peça que o usuário declare a mensagem de resposta, insira-a no o RemoteInputReply da intent pendente e, em seguida, disparar o com um intent pendente.
VOICE_ACTION_HANDLE_EXCEPTION String com chave.
KEY_EXCEPTION que mapeia para ExceptionValue (descrito em Valores de exceção).
KEY_FALLBACK_ASSISTANT_ENABLED que mapeia para um valor booleano. Se o valor é true, o assistente substituto que pode lidar com a solicitação do usuário foi desativado.
A ação esperada a ser tomada para a exceção é definida no documentação para a exceção.

Valores de exceção

EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING indica ao assistente de voz que não tem a permissão Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE e que ela precisa ser solicitada ao usuário.

Solicitar permissão do listener de notificações

Se o assistente de voz padrão não tiver o listener de notificações permissão, o FallbackAssistant da plataforma (se ativado pelo fabricante do carro) pode ler a mensagem em voz alta antes de o assistente de voz ser notificado para solicitar a permissão. Para determinar se o FallbackAssistant está ativado e leu a mensagem, o assistente de voz deve verificar Valor booleano KEY_FALLBACK_ASSISTANT_ENABLED no payload.

A plataforma recomenda que o assistente de voz adicione lógica de limitação de taxa o número de vezes que a permissão é solicitada. Isso respeita o usuário que não quiser conceder essa permissão ao assistente de voz e prefere a FallbackAssistant para ler mensagens de texto em voz alta. A solicitação de um para pedir permissão sempre que ele pressionar Play em uma notificação de mensagem pode ser uma experiência do usuário negativa. A plataforma não impõe limites de taxa. em nome do assistente de voz.

Ao solicitar a permissão do ouvinte de notificações, o assistente de voz deve use CarUxRestrictionsManager para determinar se um usuário está estacionado ou dirigindo. Se o usuário estiver dirigindo, o assistente de voz exibe uma notificação com instruções sobre como conceder a permissão. Ao fazer isso ajuda (e lembra) o usuário de conceder a permissão quando for mais seguro.

Trabalhar com StatusBarNotification

StatusBarNotification transmitido com a função de leitura e resposta. os comandos de voz estão sempre em uma notificação de mensagens compatível com o carro, conforme descrito em Notificar usuários de mensagens. Embora algumas notificações possam não ter a opção "Resposta pendente" todas elas terão intents pendentes Marcar como lida.

Para simplificar as interações com as notificações, use o NotificationPayloadHandler, que fornece métodos para extrair mensagens da notificação e gravar a responder mensagens ao intent pendente apropriado da notificação. Após o o assistente de voz ler a mensagem, ele precisa disparar a marcação como intent de leitura.

Atender às condições prévias do recurso "Tocar para ler"

Apenas VoiceInteractionSession da voz padrão o Google Assistente é notificado quando um usuário aciona a ação de voz para ler e responder às mensagens. Como mencionado acima, esse assistente de voz padrão também precisa ter a permissão de listener de notificações.

Diagramas de sequência

Essas figuras mostram os fluxos lógicos de CarVoiceInteractionSession actions:

VOICE_ACTION_READ_NOTIFICAÇÃO

Figura 2. Diagrama de sequência para VOICE_ACTION_READ_NOTE.

No caso da Figura 3, o aplicativo de limites de taxa nas solicitações de permissão é recomendado:

NOTIFICAÇÃO DE AÇÃO POR VOZ

Figura 3. Diagrama de sequência de VOICE_ACTION_REPLY_NOTE.

EXCEÇÃO_DE_AÇÃO_DE_VOZ

Figura 4. Diagrama de sequência de VOICE_ACTION_HANDLE_EXCEPTION.

Ler o nome do app

Se quiser que seu assistente de voz leia o nome do aplicativo de mensagens em voz alta durante a leitura da mensagem (por exemplo, "Sam do Hangouts disse..."), crie uma função como a mostrada no exemplo de código a seguir 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;
}