Asystent głosowy – „kliknij, aby przeczytać”

Android Automotive uważa głos za kluczowy element interakcji bezpiecznych dla kierowcy i jeden z najbezpieczniejszych sposobów na interakcję z Androidem Automotive podczas jazdy. W rezultacie rozszerzyliśmy interfejsy API Asystenta głosowego na Androidzie (w tym VoiceInteractionSession), aby umożliwić asystentom głosowym wykonywanie zadań dla użytkowników, które mogą być trudne do wykonania podczas jazdy.

Funkcja „dotknij, aby przeczytać” umożliwia asystentom głosowym odczytywanie i odpowiadanie na wiadomości tekstowe w imieniu użytkownika, gdy ten będzie wchodzić w interakcję z powiadomieniami o wiadomościach. Aby udostępnić tę funkcję, możesz zintegrować asystenta głosowego z CarVoiceInteractionSession.

W systemie samochodowym powiadomienia wyświetlane w Centrum powiadomień o typie INBOX lub INBOX_IN_GROUP (np. SMS-y) zawierają przycisk Odtwórz. Użytkownik może kliknąć Odtwórz, aby wybrany asystent głosowy odczytał powiadomienie na głos, a opcjonalnie – aby na nie odpowiedzieć głosowo.

Powiadomienie „dotknij, aby przeczytać”

Rysunek 1. Powiadomienie „Dotknij, aby przeczytać” z przyciskiem Odtwórz.

Integracja z CarVoiceInteractionSession

W kolejnych sekcjach znajdziesz opis integracji asystenta głosowego z CarVoiceInteractionSession.

Obsługa interakcji głosowych

Aplikacje, które zapewniają usługi interakcji głosowej w samochodzie, muszą być zintegrowane z dotychczasowymi interakcjami głosowymi w Androidzie. Więcej informacji znajdziesz w artykule Asystent Google na Androida (z wyjątkiem VoiceInteractionSession). Wszystkie elementy interfejsu API interakcji głosowej pozostają takie same jak na urządzeniach mobilnych, ale CarVoiceInteractionSession (opisany w artykule Wdrażanie interfejsu CarVoiceInteractionSession) zastępuje VoiceInteractionSession. Więcej informacji znajdziesz na tych stronach:

Zaimplementuj CarVoiceInteractionSession

CarVoiceInteractionSession pozwala na korzystanie z interfejsów API, które umożliwiają asystentom głosowym odczytywanie na głos wiadomości tekstowych i odpowiadanie na nie w imieniu użytkownika.

Główna różnica między klasami CarVoiceInteractionSessionVoiceInteractionSession polega na tym, że CarVoiceInteractionSession przekazuje działanie w onShow, dzięki czemu asystent głosowy może wykryć kontekst żądania użytkownika, gdy tylko CarVoiceInteractionSession rozpocznie sesję. Parametry onShow dla poszczególnych klas znajdziesz w tabeli poniżej:

CarVoiceInteractionSession VoiceInteractionSession
onShow przyjmuje 3 parametry:
  • args
  • showFlags
  • actions
onShow przyjmuje 2parametry:
  • args
  • showFlags

Zmiany w Androidzie 10

Od Androida 10 platforma wywołuje funkcję VoiceInteractionService.onGetSupportedVoiceActions, aby wykryć, które działania są obsługiwane. Asystent głosowy zastępuje i wdraża VoiceInteractionService.onGetSupportedVoiceActions, jak w tym przykładzie:

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;
   }
}

Dozwolone działania są opisane w tabeli poniżej. Szczegółowe informacje o każdym działaniu znajdziesz w diagramach sekwencji.

Działanie Oczekiwany ładunek Oczekiwane działanie po interakcji głosowej
VOICE_ACTION_READ_NOTIFICATION Czytaj na głos wiadomości użytkownikowi, a potem wywołaj działanie „Oznacz jako przeczytane” po odczytaniu wiadomości. Opcjonalnie poproś użytkownika o odpowiedź.
VOICE_ACTION_REPLY_NOTIFICATION Parcelable z kluczem.
KEY_NOTIFICATION zmapowany na StatusBarNotification.
Wymaga android.permission.BIND_NOTIFICATION_LISTENER_SERVICE.
Poproś użytkownika o podanie odpowiedzi, wpisz ją w polu RemoteInputReply zapytania oczekującego, a potem uruchom to zapytanie.
VOICE_ACTION_HANDLE_EXCEPTION Ciąg znaków z kluczem.
KEY_EXCEPTION mapuje się na ExceptionValue (opisane w sekcji Wartości wyjątków).
KEY_FALLBACK_ASSISTANT_ENABLED, która jest mapowana na wartość logiczną. Jeśli wartość to true, pomocnik awaryjny, który może obsłużyć żądanie użytkownika, został wyłączony.
Oczekiwane działanie w przypadku wyjątku jest określone w dokumentacji dotyczącej tego wyjątku.

Wartości wyjątku

EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING wskazuje asystentowi głosowemu, że brakuje mu uprawnienia Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE i że musi uzyskać to uprawnienie od użytkownika.

Prośba o zgodę na odbiór powiadomień

Jeśli domyślny asystent głosowy nie ma uprawnień do odbioru powiadomień, platforma FallbackAssistant (jeśli została włączona przez producenta samochodu) może odczytać wiadomość na głos, zanim asystent głosowy poprosi o uprawnienia. Aby ustalić, czy FallbackAssistant jest włączona i czy przeczytała wiadomość, asystent głosowy powinien sprawdzić wartość logiczną KEY_FALLBACK_ASSISTANT_ENABLED w pliku danych.

Platforma zaleca, aby asystent głosowy dodał logikę ograniczającą częstotliwość żądania tego uprawnienia. Dzięki temu użytkownik, który nie chce przyznać Asystentowi głosowemu tego uprawnienia i woli, aby FallbackAssistant odczytywał na głos wiadomości tekstowe, nie będzie miał tego uprawnienia. Proszenie użytkownika o pozwolenie za każdym razem, gdy naciśnie Odtwórz w powiadomieniu o wiadomości, może negatywnie wpłynąć na wrażenia użytkownika. Platforma nie nakłada limitów szybkości w imieniu asystenta głosowego.

Podczas prośby o dostęp do powiadomień asystent głosowy powinien używać CarUxRestrictionsManager , aby określić, czy użytkownik jest zaparkowany, czy prowadzi pojazd. Jeśli użytkownik prowadzi pojazd, asystent głosowy wyświetli powiadomienie z instrukcjami udzielenia uprawnienia. Pomoże to użytkownikowi przyznać uprawnienia, gdy będzie to bezpieczne (i przypomni mu o tym).

Praca z klasą StatusBarNotification

Wartość StatusBarNotification przekazana w ramach działań głosowych „Read” i „Reply” jest zawsze wyświetlana w powiadomieniu o wiadomości zgodnym z wymaganiami dotyczącymi samochodów, zgodnie z opisem w sekcji Powiadomienia użytkowników o wiadomościach. Niektóre powiadomienia mogą nie mieć intencji Odpowiedz, ale wszystkie mają oznaczenie jako przeczytane.

Aby usprawnić interakcje z powiadomieniami, użyj interfejsu NotificationPayloadHandler, który zawiera metody wyodrębniania wiadomości z powiadomienia i tworzenia wiadomości odpowiedzi do odpowiedniej oczekującej intencji powiadomienia. Gdy asystent głosowy odczyta wiadomość, musi wywołać intent Oznaczyć jako przeczytane.

Warunki wstępne funkcji Dotknij, aby przeczytać

Gdy użytkownik uruchamia działanie głosowe, aby odczytać i odpwiedzieć na wiadomości, tylko VoiceInteractionSession domyślnego asystenta głosowego jest powiadamiany. Jak już wspomnieliśmy, ten domyślny asystent głosowy musi też mieć uprawnienia do odbierania powiadomień.

Diagramy sekwencji

Te rysunki przedstawiają przepływy logiczne w programie CarVoiceInteractionSession actions:

VOICE_ACTION_READ_NOTIFICATION

Rysunek 2. Schemat sekwencji dla VOICE_ACTION_READ_NOTIFICATION.

W przypadku rysunku 3 zaleca się zastosowanie limitów częstotliwości żądań uprawnień:

VOICE_ACTION_REPLY_NOTIFICATION

Rysunek 3. Schemat sekwencji dla VOICE_ACTION_REPLY_NOTIFICATION.

VOICE_ACTION_HANDLE_EXCEPTION

Rysunek 4. Schemat sekwencji dla VOICE_ACTION_HANDLE_EXCEPTION.

Czytaj nazwę aplikacji

Jeśli chcesz, aby asystent głosowy odczytywał na głos nazwę aplikacji do obsługi wiadomości (np. „Sam z Hangouts powiedział…”), utwórz funkcję podobną do tej w następującym przykładzie kodu, aby upewnić się, że asystent odczytuje prawidłową nazwę:

@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;
}