Merge "Retry TTS speaking for a11y shortcut warning dialog" into rvc-dev am: 67c02e6cb0 am: 28d79c4c4c

Change-Id: Ie4db1054087179502da0d9f7345366a82924cb5f
This commit is contained in:
Automerger Merge Worker
2020-02-25 05:03:40 +00:00
2 changed files with 74 additions and 17 deletions

View File

@@ -412,8 +412,13 @@ public class AccessibilityShortcutController {
* Class to wrap TextToSpeech for shortcut dialog spoken feedback. * Class to wrap TextToSpeech for shortcut dialog spoken feedback.
*/ */
private class TtsPrompt implements TextToSpeech.OnInitListener { private class TtsPrompt implements TextToSpeech.OnInitListener {
private static final int RETRY_MILLIS = 1000;
private final CharSequence mText; private final CharSequence mText;
private int mRetryCount = 3;
private boolean mDismiss; private boolean mDismiss;
private boolean mLanguageReady = false;
private TextToSpeech mTts; private TextToSpeech mTts;
TtsPrompt(String serviceName) { TtsPrompt(String serviceName) {
@@ -437,17 +442,15 @@ public class AccessibilityShortcutController {
playNotificationTone(); playNotificationTone();
return; return;
} }
mHandler.sendMessage(PooledLambda.obtainMessage(TtsPrompt::play, this)); mHandler.sendMessage(PooledLambda.obtainMessage(
TtsPrompt::waitForTtsReady, this));
} }
private void play() { private void play() {
if (mDismiss) { if (mDismiss) {
return; return;
} }
int status = TextToSpeech.ERROR; final int status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
if (setLanguage(Locale.getDefault())) {
status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
}
if (status != TextToSpeech.SUCCESS) { if (status != TextToSpeech.SUCCESS) {
Slog.d(TAG, "Tts play fail"); Slog.d(TAG, "Tts play fail");
playNotificationTone(); playNotificationTone();
@@ -455,21 +458,42 @@ public class AccessibilityShortcutController {
} }
/** /**
* @return false if tts language is not available * Waiting for tts is ready to speak. Trying again if tts language pack is not available
* or tts voice data is not installed yet.
*/ */
private boolean setLanguage(final Locale locale) { private void waitForTtsReady() {
int status = mTts.isLanguageAvailable(locale); if (mDismiss) {
if (status == TextToSpeech.LANG_MISSING_DATA return;
|| status == TextToSpeech.LANG_NOT_SUPPORTED) {
return false;
} }
mTts.setLanguage(locale); if (!mLanguageReady) {
Voice voice = mTts.getVoice(); final int status = mTts.setLanguage(Locale.getDefault());
if (voice == null || (voice.getFeatures() != null && voice.getFeatures() // True if language is available and TTS#loadVoice has called once
.contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED))) { // that trigger TTS service to start initialization.
return false; mLanguageReady = status != TextToSpeech.LANG_MISSING_DATA
&& status != TextToSpeech.LANG_NOT_SUPPORTED;
} }
return true; if (mLanguageReady) {
final Voice voice = mTts.getVoice();
final boolean voiceDataInstalled = voice != null
&& voice.getFeatures() != null
&& !voice.getFeatures().contains(
TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED);
if (voiceDataInstalled) {
mHandler.sendMessage(PooledLambda.obtainMessage(
TtsPrompt::play, this));
return;
}
}
if (mRetryCount == 0) {
Slog.d(TAG, "Tts not ready to speak.");
playNotificationTone();
return;
}
// Retry if TTS service not ready yet.
mRetryCount -= 1;
mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
TtsPrompt::waitForTtsReady, this), RETRY_MILLIS);
} }
} }

View File

@@ -36,6 +36,7 @@ import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -85,7 +86,9 @@ import org.mockito.invocation.InvocationOnMock;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -534,6 +537,36 @@ public class AccessibilityShortcutControllerTest {
verify(mRingtone).play(); verify(mRingtone).play();
} }
@Test
public void testOnAccessibilityShortcut_showsWarningDialog_ttsLongTimeInit_retrySpoken()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
configureTtsSpokenPromptEnabled();
configureHandlerCallbackInvocation();
AccessibilityShortcutController accessibilityShortcutController = getController();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
Set<String> features = new HashSet<>();
features.add(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED);
doReturn(features, Collections.emptySet()).when(mVoice).getFeatures();
doReturn(TextToSpeech.LANG_NOT_SUPPORTED, TextToSpeech.LANG_AVAILABLE)
.when(mTextToSpeech).setLanguage(any());
accessibilityShortcutController.performAccessibilityShortcut();
verify(mAlertDialog).show();
ArgumentCaptor<TextToSpeech.OnInitListener> onInitCap = ArgumentCaptor.forClass(
TextToSpeech.OnInitListener.class);
verify(mFrameworkObjectProvider).getTextToSpeech(any(), onInitCap.capture());
onInitCap.getValue().onInit(TextToSpeech.SUCCESS);
verify(mTextToSpeech).speak(any(), eq(TextToSpeech.QUEUE_FLUSH), any(), any());
ArgumentCaptor<DialogInterface.OnDismissListener> onDismissCap = ArgumentCaptor.forClass(
DialogInterface.OnDismissListener.class);
verify(mAlertDialog).setOnDismissListener(onDismissCap.capture());
onDismissCap.getValue().onDismiss(mAlertDialog);
verify(mTextToSpeech).shutdown();
verify(mRingtone, times(0)).play();
}
private void configureNoShortcutService() throws Exception { private void configureNoShortcutService() throws Exception {
when(mAccessibilityManagerService when(mAccessibilityManagerService
.getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))