1 /** 2 * Copyright (C) 2021 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.voicecontrol; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.annotation.StringRes; 21 22 import java.util.List; 23 import java.util.function.Consumer; 24 25 /** 26 * Minimal text-to-speech module interface. 27 */ 28 public interface TextToSpeech { 29 /** 30 * Listener used to receive a notification once utterance is finished. 31 */ 32 interface Listener { 33 /** 34 * Called when TTS is ready. 35 */ onReady(TextToSpeech tts)36 default void onReady(TextToSpeech tts) {}; 37 38 /** 39 * Called when the last utterance requested with {@link TextToSpeech#speak(int, Object...)} 40 * has finished. 41 * @param successful whether utterance was successful. 42 */ onUtteranceDone(boolean successful)43 default void onUtteranceDone(boolean successful) {}; 44 45 /** 46 * Called when the last utterance requires the user to answer a question. Implementors must 47 * call {@link TextToSpeech#provideAnswer(List)} once that answer has been 48 * captured. 49 */ onWaitingForAnswer()50 default void onWaitingForAnswer() {} 51 } 52 53 /** 54 * A callback to be provided when asking a question to the user 55 */ 56 interface QuestionCallback { 57 /** 58 * Method invoked when the answer to a question is received 59 * 60 * @param results Recognized answers in confidence order, or empty list if no answer was 61 * recognized. 62 */ onResult(@onNull List<String> results)63 void onResult(@NonNull List<String> results); 64 } 65 66 /** 67 * Known answer types 68 */ 69 enum AnswerType { 70 AFFIRMATIVE, 71 NEGATIVE, 72 } 73 74 /** 75 * Convenient factory method that creates a 'yes/no' question callback. 76 * 77 * @param consumer A lambda that will receive the yes or no answer. 78 * @param retries Number of times we will try to get a yes/no answer from the user. After this 79 * many tries, the application will use the default answer. 80 */ createBooleanQuestionCallback(Consumer<Boolean> consumer, int retries, boolean defaultAnswer)81 default QuestionCallback createBooleanQuestionCallback(Consumer<Boolean> consumer, 82 int retries, boolean defaultAnswer) { 83 return new QuestionCallback() { 84 private int mRetries = retries; 85 86 @Override 87 public void onResult(List<String> results) { 88 AnswerType type = getAnswerType(results); 89 if (type != AnswerType.AFFIRMATIVE && type != AnswerType.NEGATIVE) { 90 if (mRetries > 0) { 91 mRetries--; 92 ask(this, R.string.speech_reply_yes_no_question_not_understood); 93 } else { 94 // If no answer is understood after a few retries, let's assume a 95 // negative answer 96 consumer.accept(defaultAnswer); 97 } 98 } else { 99 consumer.accept(type == AnswerType.AFFIRMATIVE); 100 } 101 } 102 }; 103 } 104 105 /** 106 * Releases internal resources 107 */ destroy()108 void destroy(); 109 110 /** 111 * Requests the given text to be added to the queue of pending utterances to read out. 112 * If this method is called before {@link Listener#onReady(TextToSpeech)}, the speech will be 113 * saved and its utterance will be delayed until TTS is ready. 114 */ speak(String fmt, Object... args)115 void speak(String fmt, Object... args); 116 117 /** 118 * Similar to {@link #speak(String, Object[])} but it takes a string resource rather than an 119 * actual string. 120 */ speak(@tringRes int stringId, Object... args)121 void speak(@StringRes int stringId, Object... args); 122 123 /** 124 * Requests the given text to be added to the queue of pending utterances to read out (similar 125 * semantics as {@link #speak(int, Object...)}. 126 * Once this is done, instead of calling {@link Listener#onUtteranceDone(boolean)}, it will 127 * call {@link Listener#onWaitingForAnswer()}, to wait for the user to provide an answer. Once 128 * the answer is collected (using {@link #provideAnswer(List)}), the answer will be 129 * given through the {@link QuestionCallback}. 130 */ ask(QuestionCallback callback, String fmt, Object... args)131 void ask(QuestionCallback callback, String fmt, Object... args); 132 133 /** 134 * Similar to {@link #ask(QuestionCallback, String, Object...)} but it takes a string 135 * resource rather than an actual string. 136 */ ask(QuestionCallback callback, @StringRes int resId, Object... args)137 void ask(QuestionCallback callback, @StringRes int resId, Object... args); 138 139 /** 140 * @return true if the last utterance was a question that is waiting to be answered 141 */ isWaitingForAnswer()142 boolean isWaitingForAnswer(); 143 144 /** 145 * Provides an answer to last uttered questions. Calling this method could cause the 146 * {@link QuestionCallback#onResult(List)} method of the last 147 * {@link #ask(QuestionCallback, int, Object...)} invocation to be called. 148 */ provideAnswer(@onNull List<String> results)149 void provideAnswer(@NonNull List<String> results); 150 151 /** 152 * @return the type of answer received, or null if the answer is not recognized 153 */ getAnswerType(List<String> strings)154 @Nullable AnswerType getAnswerType(List<String> strings); 155 156 /** 157 * @return the list of voices supported by this TTS. Can only be called after 158 * {@link Listener#onReady(TextToSpeech)} 159 */ getVoices()160 List<String> getVoices(); 161 162 /** 163 * Sets the voice that should be used. The value provided should be one of items returned by 164 * {@link #getVoices()} 165 */ setSelectedVoice(String name)166 void setSelectedVoice(String name); 167 } 168