From 4b73867a12a9339c7788e8949aac4a32d2eee22b Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczepaniak Date: Wed, 19 Nov 2014 17:36:57 +0000 Subject: Add UtteranceProgressListener#onStop callback New UtteranceProgressListener callback that allows to detect a call to TextToSpeech#stop() (or QUEUE_FLUSH usage) from the same client, or a QUEUE_DESTROY usage from any other client (Talkback uses it to preempt other users of TextToSpeech queue). This change is required for seamless Books read aloud feature+Talkback usage. + Fixes for broken tests/TtsTests Bug: 17901521 Change-Id: I30d2f297bb7c8d05cbeb16f63e85c1be0cca5c84 --- .../android/speech/tts/ITextToSpeechCallback.aidl | 2 +- core/java/android/speech/tts/TextToSpeech.java | 4 +- .../android/speech/tts/TextToSpeechService.java | 79 +++++++++++++++++++--- .../speech/tts/UtteranceProgressListener.java | 19 ++++++ 4 files changed, 90 insertions(+), 14 deletions(-) (limited to 'core/java/android/speech') diff --git a/core/java/android/speech/tts/ITextToSpeechCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl index 899515f..d785c3f 100644 --- a/core/java/android/speech/tts/ITextToSpeechCallback.aidl +++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl @@ -40,7 +40,7 @@ oneway interface ITextToSpeechCallback { * * @param utteranceId Unique id identifying synthesis request. */ - void onStop(String utteranceId); + void onStop(String utteranceId, boolean isStarted); /** * Tells the client that the synthesis has failed. diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index c59ca8a..06e9ce0 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -2066,10 +2066,10 @@ public class TextToSpeech { private boolean mEstablished; private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { - public void onStop(String utteranceId) throws RemoteException { + public void onStop(String utteranceId, boolean isStarted) throws RemoteException { UtteranceProgressListener listener = mUtteranceProgressListener; if (listener != null) { - listener.onDone(utteranceId); + listener.onStop(utteranceId, isStarted); } }; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 9bb7f02..02c9a36 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -455,10 +455,37 @@ public abstract class TextToSpeechService extends Service { private class SynthHandler extends Handler { private SpeechItem mCurrentSpeechItem = null; + private ArrayList mFlushedObjects = new ArrayList(); + private boolean mFlushAll; + public SynthHandler(Looper looper) { super(looper); } + private void startFlushingSpeechItems(Object callerIdentity) { + synchronized (mFlushedObjects) { + if (callerIdentity == null) { + mFlushAll = true; + } else { + mFlushedObjects.add(callerIdentity); + } + } + } + private void endFlushingSpeechItems(Object callerIdentity) { + synchronized (mFlushedObjects) { + if (callerIdentity == null) { + mFlushAll = false; + } else { + mFlushedObjects.remove(callerIdentity); + } + } + } + private boolean isFlushed(SpeechItem speechItem) { + synchronized (mFlushedObjects) { + return mFlushAll || mFlushedObjects.contains(speechItem.getCallerIdentity()); + } + } + private synchronized SpeechItem getCurrentSpeechItem() { return mCurrentSpeechItem; } @@ -522,9 +549,13 @@ public abstract class TextToSpeechService extends Service { Runnable runnable = new Runnable() { @Override public void run() { - setCurrentSpeechItem(speechItem); - speechItem.play(); - setCurrentSpeechItem(null); + if (isFlushed(speechItem)) { + speechItem.stop(); + } else { + setCurrentSpeechItem(speechItem); + speechItem.play(); + setCurrentSpeechItem(null); + } } }; Message msg = Message.obtain(this, runnable); @@ -552,12 +583,14 @@ public abstract class TextToSpeechService extends Service { * * Called on a service binder thread. */ - public int stopForApp(Object callerIdentity) { + public int stopForApp(final Object callerIdentity) { if (callerIdentity == null) { return TextToSpeech.ERROR; } - removeCallbacksAndMessages(callerIdentity); + // Flush pending messages from callerIdentity + startFlushingSpeechItems(callerIdentity); + // This stops writing data to the file / or publishing // items to the audio playback handler. // @@ -573,20 +606,39 @@ public abstract class TextToSpeechService extends Service { // Remove any enqueued audio too. mAudioPlaybackHandler.stopForApp(callerIdentity); + // Stop flushing pending messages + Runnable runnable = new Runnable() { + @Override + public void run() { + endFlushingSpeechItems(callerIdentity); + } + }; + sendMessage(Message.obtain(this, runnable)); return TextToSpeech.SUCCESS; } public int stopAll() { + // Order to flush pending messages + startFlushingSpeechItems(null); + // Stop the current speech item unconditionally . SpeechItem current = setCurrentSpeechItem(null); if (current != null) { current.stop(); } - // Remove all other items from the queue. - removeCallbacksAndMessages(null); // Remove all pending playback as well. mAudioPlaybackHandler.stop(); + // Message to stop flushing pending messages + Runnable runnable = new Runnable() { + @Override + public void run() { + endFlushingSpeechItems(null); + } + }; + sendMessage(Message.obtain(this, runnable)); + + return TextToSpeech.SUCCESS; } } @@ -698,7 +750,6 @@ public abstract class TextToSpeechService extends Service { return mCallerIdentity; } - public int getCallerUid() { return mCallerUid; } @@ -752,6 +803,10 @@ public abstract class TextToSpeechService extends Service { protected synchronized boolean isStopped() { return mStopped; } + + protected synchronized boolean isStarted() { + return mStarted; + } } /** @@ -777,7 +832,7 @@ public abstract class TextToSpeechService extends Service { public void dispatchOnStop() { final String utteranceId = getUtteranceId(); if (utteranceId != null) { - mCallbacks.dispatchOnStop(getCallerIdentity(), utteranceId); + mCallbacks.dispatchOnStop(getCallerIdentity(), utteranceId, isStarted()); } } @@ -940,6 +995,8 @@ public abstract class TextToSpeechService extends Service { // turn implies that synthesis would not have started. synthesisCallback.stop(); TextToSpeechService.this.onStop(); + } else { + dispatchOnStop(); } } @@ -1345,11 +1402,11 @@ public abstract class TextToSpeechService extends Service { } } - public void dispatchOnStop(Object callerIdentity, String utteranceId) { + public void dispatchOnStop(Object callerIdentity, String utteranceId, boolean started) { ITextToSpeechCallback cb = getCallbackFor(callerIdentity); if (cb == null) return; try { - cb.onStop(utteranceId); + cb.onStop(utteranceId, started); } catch (RemoteException e) { Log.e(TAG, "Callback onStop failed: " + e); } diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java index 6769794..9eb22ef 100644 --- a/core/java/android/speech/tts/UtteranceProgressListener.java +++ b/core/java/android/speech/tts/UtteranceProgressListener.java @@ -60,6 +60,20 @@ public abstract class UtteranceProgressListener { } /** + * Called when an utterance has been stopped while in progress or flushed from the + * synthesis queue. This can happen if client calls {@link TextToSpeech#stop()} + * or use {@link TextToSpeech#QUEUE_FLUSH} as an argument in + * {@link TextToSpeech#speak} or {@link TextToSpeech#synthesizeToFile} methods. + * + * @param utteranceId the utterance ID of the utterance. + * @param isStarted If true, then utterance was interrupted while being synthesized + * and it's output is incomplete. If it's false, then utterance was flushed + * before the synthesis started. + */ + public void onStop(String utteranceId, boolean isStarted) { + } + + /** * Wraps an old deprecated OnUtteranceCompletedListener with a shiny new * progress listener. * @@ -83,6 +97,11 @@ public abstract class UtteranceProgressListener { // Left unimplemented, has no equivalent in the old // API. } + + @Override + public void onStop(String utteranceId, boolean isStarted) { + listener.onUtteranceCompleted(utteranceId); + } }; } } -- cgit v1.1