Assistente de voz tocar para ler

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

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

Notificação de toque para ler

No setor automotivo, as notificações postadas na Central de Notificações identificadas como notificações INBOX ou INBOX_IN_GROUP (por exemplo, mensagens SMS) incluirão um botão de ação Reproduzir. Este botão permite ao usuário fazer com que a notificação seja lida em voz alta pelo VIA selecionado e, opcionalmente, responder por voz.

Figura 1. Notificação de toque para ler

Integre com CarVoiceInteractionSession

1. Suporta VoiceInteractions

Os aplicativos que fornecem serviços de interação de voz do carro devem se integrar às interações de voz Android existentes (com exceção de VoiceInteractionSession ). Embora todos os outros componentes da API de interação por voz permaneçam os mesmos implementados em dispositivos móveis, CarVoiceInteractionSession (descrito abaixo) substitui VoiceInteractionSession . Para mais informações, consulte estes artigos:

2. Implemente CarVoiceInteractionSession

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

Ao comparar CarVoiceInteractionSession e VoiceInteractionSession , a principal diferença é 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.

CarVoiceInteractionSession VoiceInteractionSession
onShow recebe estes três parâmetros:
  • args
  • showFlags
  • actions
onShow recebe esses 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 abaixo:

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 abaixo. Para obter detalhes sobre cada ação, consulte os Diagramas de Sequência abaixo.

Ação Carga útil esperada Ação de interação de voz esperada
VOICE_ACTION_READ_NOTIFICATION Leia as mensagens em voz alta para o usuário e, em seguida, dispare a intenção Marcar como lida pendente quando as mensagens forem lidas com êxito. Opcionalmente, solicite uma resposta do usuário.
VOICE_ACTION_REPLY_NOTIFICATION Parcelável com chave.
KEY_NOTIFICATION que mapeia para um 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 acione a intenção pendente.
VOICE_ACTION_HANDLE_EXCEPTION Corda com chave.
KEY_EXCEPTION que mapeia para um ExceptionValue (descrito abaixo).
KEY_FALLBACK_ASSISTANT_ENABLED que mapeia para um valor booleano. Se o valor for true, o Fallback Assistant que pode manipular a solicitação do usuário foi desabilitado.
A ação esperada a ser tomada para a exceção será definida na documentação da exceção.

ExceptionValue

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.

3. Solicitar permissão de ouvinte de notificação

Se o assistente de voz padrão não tiver permissões 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 o FallbackAssistant está ativado e, portanto, 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 uma lógica de limitação de taxa quanto ao 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 em voz alta as mensagens de texto. Solicitar permissão a esse usuário sempre que ele pressionar Reproduzir na notificação de mensagem pode levar a uma experiência negativa do usuário. A plataforma não impõe limites de taxa em nome do assistente de voz.

Ao solicitar a permissão do Notification Listener, 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 exibe uma notificação que fornece instruções sobre como conceder a permissão. Isso ajuda (e lembra) o usuário a conceder a permissão quando for mais seguro.

4. Trabalhe com StatusBarNotification

StatusBarNotifications transmitidas com as ações de voz Read e Reply estão sempre em uma notificação de mensagem 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 de marcar como lidas.

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, o assistente de voz deve disparar a intenção Mark as Read.

5. Satisfaça as pré-condições de tocar para ler

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

Diagramas de Sequência

Essas 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 abaixo, recomenda-se a aplicação de limites de taxa 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

Dicas e truques

Lendo o nome de um aplicativo

Se o seu assistente de voz quiser ler em voz alta o nome do aplicativo de mensagens durante a leitura da mensagem (ou seja, "Sam do Hangouts disse..."), você deve criar uma função como a mostrada abaixo para garantir que está 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 applications, so check this
    // field for a system application 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;
}