Assistente de voz toque para ler

O Android Automotive considera a voz um componente crucial para interações seguras na direção e uma das maneiras mais seguras para os usuários interagirem com o sistema operacional Android Automotive enquanto dirigem. Como resultado, expandimos as APIs do assistente de voz do Android (incluindo VoiceInteractionSession ) para permitir que os assistentes de voz executem tarefas para os usuários que podem ser difíceis de realizar enquanto dirigem.

Tap-to-Read permite que assistentes de voz leiam e respondam mensagens de texto em nome do usuário, quando o usuário interage com notificações de mensagens. Para fornecer essa funcionalidade, você pode integrar um assistente de voz com CarVoiceInteractionSession .

No Automotivo, as notificações postadas na Central de Notificações identificadas como INBOX ou INBOX_IN_GROUP (por exemplo, mensagens SMS) incluem um botão Play . O usuário pode clicar em Reproduzir para que o assistente de voz selecionado leia a notificação em voz alta e, opcionalmente, responda por voz.

Notificação toque para ler

Figura 1. Notificação Tap-to-Read com botão Play.

Integrar com CarVoiceInteractionSession

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

Suporta interações de voz

Os aplicativos que fornecem serviços de interação por voz automotiva devem ser integrados às interações de voz existentes do Android. Para saber mais, consulte Google Assistant para Android (com exceção de VoiceInteractionSession ). Embora todos os elementos da API de interação por voz permaneçam iguais aos implementados em dispositivos móveis, CarVoiceInteractionSession (descrito em Implementar CarVoiceInteractionSession ) substitui VoiceInteractionSession . Para obter mais informações, consulte estas páginas:

Implementar CarVoiceInteractionSession

CarVoiceInteractionSession expõe APIs que você pode usar para permitir que assistentes de voz leiam mensagens de texto em voz alta e depois respondam a essas mensagens em nome do usuário.

A principal diferença entre as classes CarVoiceInteractionSession e VoiceInteractionSession é que CarVoiceInteractionSession passa a ação em onShow para que o assistente de voz possa detectar o contexto da solicitação do usuário assim que CarVoiceInteractionSession iniciar uma sessão. Os parâmetros de onShow para cada classe estão listados na tabela a seguir:

CarVoiceInteractionSessão Sessão de interação por voz
onShow usa estes três parâmetros:
  • args
  • showFlags
  • actions
onShow usa estes dois parâmetros:
  • args
  • showFlags

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 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 Carga útil esperada Ação esperada de interação por voz
VOICE_ACTION_READ_NOTIFICATION Leia as mensagens em voz alta para o usuário e, em seguida, acione a intenção pendente Marcar como lida quando as mensagens forem lidas com sucesso. Opcionalmente, solicite uma resposta ao usuário.
VOICE_ACTION_REPLY_NOTIFICATION Parcelável com chave.
KEY_NOTIFICATION que mapeia para StatusBarNotification .
Requer android.permission.BIND_NOTIFICATION_LISTENER_SERVICE .
Solicite ao usuário que declare a mensagem de resposta, insira a mensagem de resposta no RemoteInputReply da intenção pendente e, em seguida, dispare a intenção 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 for true , o assistente de fallback que pode lidar com a solicitação do usuário foi desabilitado.
A ação esperada a ser tomada para a exceção é definida na documentação da exceção.

Valores de exceção

EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING indica ao assistente de voz que está faltando a permissão Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE e para obter essa permissão do usuário.

Solicitar permissão de ouvinte de notificação

Se o assistente de voz padrão não tiver permissão de ouvinte de notificação, o FallbackAssistant da plataforma (se habilitado 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á habilitado e leu a mensagem, o assistente de voz deve verificar o valor booleano KEY_FALLBACK_ASSISTANT_ENABLED na carga útil.

A plataforma recomenda que o assistente de voz adicione 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 deseja conceder essa permissão ao assistente de voz e prefere que o FallbackAssistant leia mensagens de texto em voz alta. Solicitar permissão a um usuário cada 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 tarifas em nome do assistente de voz.

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

Trabalhar com StatusBarNotification

StatusBarNotification transmitida com as ações de voz Ler e Responder está sempre em uma notificação de mensagens compatível com carro, conforme descrito em Notificar usuários sobre mensagens . Embora algumas notificações possam não ter a intenção de Resposta Pendente, todas elas têm intenções pendentes Marcar como Lida.

Para simplificar 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 intenção pendente apropriada da notificação. Depois que o assistente de voz lê a mensagem, ele deve disparar a intenção Marcar como lida.

Satisfazer as pré-condições Tap-to-Read

Somente VoiceInteractionSession do assistente de voz padrão é notificado quando um usuário aciona a ação de voz para ler e responder mensagens. Conforme mencionado acima, este assistente de voz padrão também deve ter permissão de ouvinte de notificação.

Diagramas de sequência

Estas figuras exibem os fluxos lógicos das CarVoiceInteractionSession actions :

VOICE_ACTION_READ_NOTIFICATION

Figura 2. Diagrama de sequência para VOICE_ACTION_READ_NOTIFICATION.

No caso da Figura 3, recomenda-se a aplicação de limites de taxas nas solicitações de permissão:

VOICE_ACTION_REPLY_NOTIFICATION

Figura 3. Diagrama de sequência para VOICE_ACTION_REPLY_NOTIFICATION.

VOICE_ACTION_HANDLE_EXCEPTION

Figura 4. Diagrama de sequência para VOICE_ACTION_HANDLE_EXCEPTION.

Leia o nome do aplicativo

Se você 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;
}