音声アシスタント タップして読む

Android Automotive では、音声は運転中の安全な操作にとって重要なコンポーネントであり、ユーザーが運転中に Android Automotive OS を操作するための最も安全な方法の 1 つであると考えています。その結果、Android 音声アシスタント API ( VoiceInteractionSessionを含む) を拡張し、運転中に実行するのが難しいユーザーのタスクを音声アシスタントが実行できるようにしました。

Tap-to-Read を使用すると、ユーザーがメッセージ通知を操作するときに、音声アシスタントがユーザーに代わってテキスト メッセージを読んで返信できるようになります。この機能を提供するには、音声アシスタントを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のパラメーターを次の表に示します。

CarVoiceインタラクションセッションVoiceInteractionセッション
onShow次の3 つのパラメータを受け取ります。
  • args
  • showFlags
  • actions
onShow次の2 つのパラメータを受け取ります。
  • 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鍵付きで取り出し可能です。
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 の操作

Read および Reply 音声アクションで渡されるStatusBarNotification 「ユーザーにメッセージを通知する」で説明されているように、常に自動車互換のメッセージング通知内にあります。一部の通知には返信保留インテントがない場合がありますが、すべての通知に保留中既読としてマークインテントがあります。

通知との対話を合理化するには、通知からメッセージを抽出し、通知の適切な保留中のインテントに応答メッセージを書き込むメソッドを提供するNotificationPayloadHandlerを使用します。音声アシスタントがメッセージを読み上げた後、音声アシスタントは既読としてマークインテントを起動する必要があります

Tap-to-Readの前提条件を満たす

ユーザーがメッセージを読んで返信する音声アクションをトリガーすると、デフォルトの音声アシスタントの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;
}