1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.car.assist; 17 18 import android.content.Context; 19 import android.os.Bundle; 20 import android.os.Handler; 21 import android.service.notification.StatusBarNotification; 22 import android.service.voice.VoiceInteractionService; 23 import android.service.voice.VoiceInteractionSession; 24 25 import androidx.annotation.StringDef; 26 27 import com.android.car.assist.payloadhandlers.NotificationPayloadHandler; 28 29 /** 30 * An active voice interaction session on the car, providing additional actions which assistant 31 * should act on. Override the {@link #onShow(String, Bundle, int)} to received the action specified 32 * by the voice session initiator. 33 */ 34 public abstract class CarVoiceInteractionSession extends VoiceInteractionSession { 35 /** The key used for the action {@link String} in the payload {@link Bundle}. */ 36 public static final String KEY_ACTION = "KEY_ACTION"; 37 38 /** 39 * The key used for the {@link CarVoiceInteractionSession#VOICE_ACTION_HANDLE_EXCEPTION} payload 40 * {@link Bundle}. Must map to a {@link ExceptionValue}. 41 */ 42 public static final String KEY_EXCEPTION = "KEY_EXCEPTION"; 43 44 /** 45 * The key used for the {@link CarVoiceInteractionSession#VOICE_ACTION_HANDLE_EXCEPTION} payload 46 * {@link Bundle}. Must map to a boolean. If value is true, the Fallback Assistant that can 47 * handle the user's request has been disabled. 48 */ 49 public static final String KEY_FALLBACK_ASSISTANT_ENABLED = "KEY_FALLBACK_ASSISTANT_ENABLED"; 50 51 /** 52 * The key used for the payload {@link Bundle}, if a {@link StatusBarNotification} is used as 53 * the payload. 54 */ 55 public static final String KEY_NOTIFICATION = "KEY_NOTIFICATION"; 56 57 /** Indicates to assistant that no action was specified. */ 58 public static final String VOICE_ACTION_NO_ACTION = "VOICE_ACTION_NO_ACTION"; 59 60 /** Indicates to assistant that a read action is being requested for a given payload. */ 61 public static final String VOICE_ACTION_READ_NOTIFICATION = "VOICE_ACTION_READ_NOTIFICATION"; 62 63 /** Indicates to assistant that a reply action is being requested for a given payload. */ 64 public static final String VOICE_ACTION_REPLY_NOTIFICATION = "VOICE_ACTION_REPLY_NOTIFICATION"; 65 66 /** 67 * Indicates to assistant that it should resolve the exception in the given payload (found in 68 * {@link CarVoiceInteractionSession#KEY_EXCEPTION}'s value). 69 */ 70 public static final String VOICE_ACTION_HANDLE_EXCEPTION = "VOICE_ACTION_HANDLE_EXCEPTION"; 71 72 /** 73 * The list of exceptions the active voice service must handle. 74 */ 75 @StringDef({EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING}) 76 public @interface ExceptionValue {} 77 78 /** 79 * Indicates to assistant that it is missing the Notification Listener permission, and should 80 * request this permission from the user. 81 **/ 82 public static final String EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING = 83 "EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING"; 84 85 86 private final NotificationPayloadHandler mNotificationPayloadHandler; 87 CarVoiceInteractionSession(Context context)88 public CarVoiceInteractionSession(Context context) { 89 super(context); 90 mNotificationPayloadHandler = new NotificationPayloadHandler(getContext()); 91 } 92 CarVoiceInteractionSession(Context context, Handler handler)93 public CarVoiceInteractionSession(Context context, Handler handler) { 94 super(context, handler); 95 mNotificationPayloadHandler = new NotificationPayloadHandler(getContext()); 96 } 97 98 /** 99 * Returns the notification payload handler, which can be used to handle actions related to 100 * notification payloads. 101 */ getNotificationPayloadHandler()102 public NotificationPayloadHandler getNotificationPayloadHandler() { 103 return mNotificationPayloadHandler; 104 } 105 106 @Override onShow(Bundle args, int showFlags)107 public final void onShow(Bundle args, int showFlags) { 108 super.onShow(args, showFlags); 109 if (args != null && isCarNotificationSource(showFlags)) { 110 String action = getRequestedVoiceAction(args); 111 if (!VOICE_ACTION_NO_ACTION.equals(action)) { 112 onShow(action, args, showFlags); 113 return; 114 } 115 } 116 onShow(VOICE_ACTION_NO_ACTION, args, showFlags); 117 } 118 119 /** 120 * Called when the session UI is going to be shown. This is called after 121 * {@link #onCreateContentView} (if the session's content UI needed to be created) and 122 * immediately prior to the window being shown. This may be called while the window 123 * is already shown, if a show request has come in while it is shown, to allow you to 124 * update the UI to match the new show arguments. 125 * 126 * @param action The action that is being requested for this session 127 * (e.g. {@link CarVoiceInteractionSession#VOICE_ACTION_READ_NOTIFICATION}, 128 * {@link CarVoiceInteractionSession#VOICE_ACTION_REPLY_NOTIFICATION}). 129 * @param args The arguments that were supplied to 130 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 131 * @param flags The show flags originally provided to 132 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 133 */ onShow(String action, Bundle args, int flags)134 protected abstract void onShow(String action, Bundle args, int flags); 135 136 /** 137 * Returns true if the request was initiated for a car notification. 138 */ isCarNotificationSource(int flags)139 private static boolean isCarNotificationSource(int flags) { 140 return (flags & SHOW_SOURCE_NOTIFICATION) != 0; 141 } 142 143 /** 144 * Returns the action {@link String} provided in the args {@Bundle}, 145 * or {@link CarVoiceInteractionSession#VOICE_ACTION_NO_ACTION} if no such string was provided. 146 */ getRequestedVoiceAction(Bundle args)147 protected static String getRequestedVoiceAction(Bundle args) { 148 return args.getString(KEY_ACTION, VOICE_ACTION_NO_ACTION); 149 } 150 } 151