음성 어시스턴트 탭하여 읽기

Android Automotive는 음성을 안전 운전 상호작용의 핵심 구성요소이자 사용자가 운전하며 Android Automotive OS와 가장 안전하게 상호작용할 수 있는 방법 중 하나로 간주합니다. 따라서 Android 음성 어시스턴트 API(VoiceInteractionSession 포함)를 확장하여 사용자가 운전 중에 달성하기 어려운 작업을 음성 어시스턴트가 실행할 수 있도록 했습니다.

탭하여 읽기를 사용하면 사용자가 메시지 알림과 상호작용할 때 음성 어시스턴트가 사용자를 대신하여 문자 메시지를 읽고 답장할 수 있습니다. 이 기능을 제공하기 위해 음성 어시스턴트를 CarVoiceInteractionSession과 통합할 수 있습니다.

Automotive에서 INBOX 또는 INBOX_IN_GROUP으로 식별되며 알림 센터에 게시되는 알림(예: SMS 메시지)에는 재생 버튼이 포함됩니다. 사용자가 재생을 클릭하면 선택된 음성 어시스턴트가 알림을 소리 내어 읽고 원하는 경우 음성으로 답장할 수 있습니다.

탭하여 읽기 알림

그림 1. 재생 버튼이 있는 탭하여 읽기 알림

CarVoiceInteractionSession과 통합

다음 섹션에서는 음성 어시스턴트를 CarVoiceInteractionSession과 통합하는 방법을 설명합니다.

음성 상호작용 지원

자동차 음성 상호작용 서비스를 제공하는 앱은 기존 Android 음성 상호작용과 반드시 통합되어야 합니다. 자세한 내용은 Android용 Google 어시스턴트(VoiceInteractionSession 제외)를 참고하세요. 모든 음성 상호작용 API 요소는 휴대기기에서 구현된 것과 동일하게 유지되지만 CarVoiceInteractionSession(CarVoiceInteractionSession 구현에 설명됨)은 VoiceInteractionSession을 대체합니다. 자세한 내용은 다음 페이지를 참고하세요.

CarVoiceInteractionSession 구현

CarVoiceInteractionSession은 음성 어시스턴트가 문자 메시지를 소리 내어 읽고 사용자를 대신하여 이러한 메시지에 답장할 수 있도록 하는 데 사용할 수 있는 API를 노출합니다.

CarVoiceInteractionSession 클래스와 VoiceInteractionSession 클래스의 주요 차이점은 CarVoiceInteractionSessiononShow의 작업을 전달하므로 CarVoiceInteractionSession이 세션을 시작하는 즉시 음성 어시스턴트에서 사용자 요청의 컨텍스트를 감지할 수 있다는 점입니다. 각 클래스의 onShow 매개변수는 다음 표에 나열되어 있습니다.

CarVoiceInteractionSession VoiceInteractionSession
onShow는 다음 세 가지 매개변수를 사용합니다.
  • args
  • showFlags
  • actions
onShow는 다음 두 가지 매개변수를 사용합니다.
  • args
  • showFlags

Android 10 변경사항

Android 10부터 플랫폼은 VoiceInteractionService.onGetSupportedVoiceActions를 호출하여 지원되는 작업을 감지합니다. 다음 예와 같이 음성 어시스턴트는 VoiceInteractionService.onGetSupportedVoiceActions를 재정의하고 구현합니다.

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

유효한 작업은 다음 표에 설명되어 있습니다. 각 작업에 관한 자세한 내용은 시퀀스 다이어그램을 참고하세요.

작업 예상 페이로드 예상 음성 상호작용 작업
VOICE_ACTION_READ_NOTIFICATION 사용자에게 메시지를 소리 내어 읽어준 후 메시지를 성공적으로 읽었을 때 '읽음으로 표시' 대기 중 인텐트를 실행합니다. 선택적으로, 사용자에게 답장을 보낼지 묻습니다.
VOICE_ACTION_REPLY_NOTIFICATION 키가 있는 Parcelable입니다.
StatusBarNotification에 매핑되는 KEY_NOTIFICATION입니다.
android.permission.BIND_NOTIFICATION_LISTENER_SERVICE가 필요합니다.
사용자에게 답장 메시지를 말하도록 요청하고 대기 중 인텐트의 RemoteInputReply에 답장 메시지를 입력한 후 대기 중 인텐트를 실행합니다.
VOICE_ACTION_HANDLE_EXCEPTION 키가 있는 문자열입니다.
ExceptionValue에 매핑되는 KEY_EXCEPTION입니다(예외 값에 설명됨).
불리언 값에 매핑되는 KEY_FALLBACK_ASSISTANT_ENABLED입니다. 값이 true이면 사용자의 요청을 처리할 수 있는 대체 어시스턴트가 사용 중지된 것입니다.
예외와 관련하여 실행해야 할 예상 작업은 예외에 관한 문서에 정의됩니다.

예외 값

EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING은 음성 어시스턴트에 Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE 권한이 없음을 나타내며 사용자로부터 이 권한을 얻도록 합니다.

알림 리스너 권한 요청

기본 음성 어시스턴트에 알림 리스너 권한이 없다면 플랫폼의 FallbackAssistant(자동차 제조업체에서 사용 설정한 경우)는 음성 어시스턴트가 권한을 요청하라는 알림을 받기 전에 메시지를 소리 내어 읽을 수 있습니다. FallbackAssistant가 사용 설정되고 메시지를 읽었는지 확인하려면 음성 어시스턴트가 페이로드에서 KEY_FALLBACK_ASSISTANT_ENABLED 불리언 값을 확인해야 합니다.

플랫폼에서는 음성 어시스턴트가 이 권한이 요청되는 횟수와 관련하여 비율 제한 로직을 추가할 것을 권장합니다. 비율 제한 로직을 추가하면 음성 어시스턴트에 이 권한을 부여하고 싶어하지 않으며 FallbackAssistant가 문자 메시지를 소리 내어 읽는 것을 선호하는 사용자를 존중할 수 있습니다. 사용자가 메시지 알림에서 재생을 누를 때마다 권한을 요청하면 부정적인 사용자 환경을 초래할 수 있습니다. 플랫폼은 음성 어시스턴트를 대신하여 비율 제한을 부과하지 않습니다.

알림 리스너 권한을 요청할 때 음성 어시스턴트는 CarUxRestrictionsManager 를 사용하여 사용자가 주차했는지 또는 운전 중인지 확인해야 합니다. 사용자가 운전 중이면 음성 어시스턴트는 권한 부여 방법에 관한 안내를 제공하는 알림을 표시합니다. 이렇게 하면 사용자가 더 안전한 상황에서 권한을 부여할 수 있습니다.

StatusBarNotification 사용

읽기 및 답장 음성 작업으로 전달된 StatusBarNotification사용자에게 메시지 알림에 설명된 대로 항상 '자동차 호환 메시지 알림'에 있습니다. 일부 알림에는 답장 대기 중 인텐트가 없을 수 있지만 모든 알림에는 읽음으로 표시 대기 중 인텐트가 있습니다.

알림과의 상호작용을 간소화하려면 알림에서 메시지를 추출하고 알림의 적절한 대기 중 인텐트에 답장 메시지를 작성하는 메서드를 제공하는 NotificationPayloadHandler를 사용합니다. 음성 어시스턴트가 메시지를 읽은 이후 음성 어시스턴트는 읽음으로 표시 인텐트를 실행해야 합니다.

탭하여 읽기 사전 조건 충족

사용자가 음성 작업을 트리거하여 메시지를 읽고 답장할 때 기본 음성 어시스턴트의 VoiceInteractionSession만 알림을 받습니다. 위에서 언급했듯이 이 기본 음성 어시스턴트에는 알림 리스너 권한도 있어야 합니다.

시퀀스 다이어그램

다음 그림은 CarVoiceInteractionSession actions의 논리 흐름을 표시합니다.

VOICE_ACTION_READ_NOTIFICATION

그림 2. VOICE_ACTION_READ_NOTIFICATION 시퀀스 다이어그램

그림 3의 경우 권한 요청에 관한 비율 제한 앱이 권장됩니다.

VOICE_ACTION_REPLY_NOTIFICATION

그림 3. VOICE_ACTION_REPLY_NOTIFICATION 시퀀스 다이어그램

VOICE_ACTION_HANDLE_EXCEPTION

그림 4. VOICE_ACTION_HANDLE_EXCEPTION 시퀀스 다이어그램

앱 이름 읽기

음성 어시스턴트가 메시지를 읽는 동안 메시지 앱의 이름을 소리 내어 읽도록 하려면(예: "행아웃의 진수가...") 다음 코드 예와 같은 함수를 만들어 어시스턴트가 올바른 이름을 읽고 있는지 확인하세요.

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