diff options
7 files changed, 79 insertions, 38 deletions
diff --git a/src/com/android/car/messenger/MapMessageMonitor.java b/src/com/android/car/messenger/MapMessageMonitor.java index b191e4b..3c427dd 100644 --- a/src/com/android/car/messenger/MapMessageMonitor.java +++ b/src/com/android/car/messenger/MapMessageMonitor.java @@ -35,7 +35,6 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.media.AudioManager; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -49,7 +48,6 @@ import androidx.annotation.Nullable; import com.android.car.apps.common.LetterTileDrawable; import com.android.car.messenger.tts.TTSHelper; - import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.SimpleTarget; @@ -94,8 +92,6 @@ class MapMessageMonitor { private final Map<SenderKey, NotificationInfo> mNotificationInfos = new HashMap<>(); private final TTSHelper mTTSHelper; private final HashMap<String, Boolean> mReplyFeatureMap = new HashMap<>(); - private final AudioManager mAudioManager; - private final AudioManager.OnAudioFocusChangeListener mNoOpAFChangeListener = (f) -> {}; MapMessageMonitor(Context context) { mContext = context; @@ -104,8 +100,6 @@ class MapMessageMonitor { mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mTTSHelper = new TTSHelper(mContext); - - mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); } public boolean isPlaying() { @@ -348,34 +342,29 @@ class MapMessageMonitor { ttsMessages.add(mContext.getString(R.string.tts_sender_says, notificationInfo.mSenderName)); ttsMessages.add(ttsMessage); - int result = mAudioManager.requestAudioFocus(mNoOpAFChangeListener, - // Use the music stream. - AudioManager.STREAM_MUSIC, - // Request permanent focus. - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); - if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - mTTSHelper.requestPlay(ttsMessages, - new TTSHelper.Listener() { - @Override - public void onTTSStarted() { - Intent intent = new Intent(ACTION_MESSAGE_PLAY_START); - mContext.sendBroadcast(intent); - } + mTTSHelper.requestPlay(ttsMessages, + new TTSHelper.Listener() { + @Override + public void onTTSStarted() { + Intent intent = new Intent(ACTION_MESSAGE_PLAY_START); + mContext.sendBroadcast(intent); + } - @Override - public void onTTSStopped(boolean error) { - mAudioManager.abandonAudioFocus(mNoOpAFChangeListener); - Intent intent = new Intent(ACTION_MESSAGE_PLAY_STOP); - mContext.sendBroadcast(intent); - if (error) { - Toast.makeText(mContext, R.string.tts_failed_toast, - Toast.LENGTH_SHORT).show(); - } + @Override + public void onTTSStopped(boolean error) { + Intent intent = new Intent(ACTION_MESSAGE_PLAY_STOP); + mContext.sendBroadcast(intent); + if (error) { + Toast.makeText(mContext, R.string.tts_failed_toast, + Toast.LENGTH_SHORT).show(); } - }); - } else { - Log.w(TAG, "failed to require audio focus."); - } + } + + @Override + public void onAudioFocusFailed() { + Log.w(TAG, "failed to require audio focus."); + } + }); } void stopPlayout() { diff --git a/src/com/android/car/messenger/PlayMessageActivity.java b/src/com/android/car/messenger/PlayMessageActivity.java index 90dcf29..7bfc5f9 100644 --- a/src/com/android/car/messenger/PlayMessageActivity.java +++ b/src/com/android/car/messenger/PlayMessageActivity.java @@ -159,6 +159,11 @@ public class PlayMessageActivity extends Activity { } finish(); } + + @Override + public void onAudioFocusFailed() { + Log.w(TAG, "failed to require audio focus."); + } }); } diff --git a/src/com/android/car/messenger/tts/AndroidTTSEngine.java b/src/com/android/car/messenger/tts/AndroidTTSEngine.java index b6a7988..70d0aff 100644 --- a/src/com/android/car/messenger/tts/AndroidTTSEngine.java +++ b/src/com/android/car/messenger/tts/AndroidTTSEngine.java @@ -54,4 +54,9 @@ class AndroidTTSEngine implements TTSEngine { mTextToSpeech.shutdown(); mTextToSpeech = null; } + + @Override + public int getStream() { + return TextToSpeech.Engine.DEFAULT_STREAM; + } } diff --git a/src/com/android/car/messenger/tts/FakeTTSEngine.java b/src/com/android/car/messenger/tts/FakeTTSEngine.java index 0870379..6af64e4 100644 --- a/src/com/android/car/messenger/tts/FakeTTSEngine.java +++ b/src/com/android/car/messenger/tts/FakeTTSEngine.java @@ -53,6 +53,11 @@ class FakeTTSEngine implements TTSEngine { mOnInitListener = null; } + @Override + public int getStream() { + return TextToSpeech.Engine.DEFAULT_STREAM; + } + void startRequest(String utteranceId) { mProgressListener.onStart(utteranceId); } diff --git a/src/com/android/car/messenger/tts/TTSEngine.java b/src/com/android/car/messenger/tts/TTSEngine.java index ed1313f..e789327 100644 --- a/src/com/android/car/messenger/tts/TTSEngine.java +++ b/src/com/android/car/messenger/tts/TTSEngine.java @@ -48,4 +48,9 @@ public interface TTSEngine { * using this engine. */ void shutdown(); + + /** + * Returns the stream used by this TTS engine. + */ + int getStream(); } diff --git a/src/com/android/car/messenger/tts/TTSHelper.java b/src/com/android/car/messenger/tts/TTSHelper.java index 8ad132e..aef20d7 100644 --- a/src/com/android/car/messenger/tts/TTSHelper.java +++ b/src/com/android/car/messenger/tts/TTSHelper.java @@ -17,6 +17,7 @@ package com.android.car.messenger.tts; import android.content.Context; +import android.media.AudioManager; import android.os.Handler; import android.speech.tts.TextToSpeech; import android.speech.tts.UtteranceProgressListener; @@ -57,6 +58,12 @@ public class TTSHelper { * not considered an error. */ void onTTSStopped(boolean error); + + /** + * Called when request to get audio focus failed. This happens before the requested TTS + * is played. + */ + void onAudioFocusFailed(); } private static final String TAG = "Messenger.TTSHelper"; @@ -67,6 +74,8 @@ public class TTSHelper { private final Handler mHandler = new Handler(); private final Context mContext; + private final AudioManager mAudioManager; + private final AudioManager.OnAudioFocusChangeListener mNoOpAFChangeListener = (f) -> {}; private final long mShutdownDelayMillis; private TTSEngine mTTSEngine; private int mInitStatus; @@ -84,6 +93,7 @@ public class TTSHelper { @VisibleForTesting TTSHelper(Context context, TTSEngine ttsEngine, long shutdownDelayMillis) { mContext = context; + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mTTSEngine = ttsEngine; mShutdownDelayMillis = shutdownDelayMillis; // OnInitListener will only set to SUCCESS/ERROR. So we initialize to STOPPED. @@ -130,6 +140,7 @@ public class TTSHelper { * until then. Only one batch is supported at a time; If a previous batch is waiting engine * setup, that batch is dropped. If a previous batch is playing, the play-out is stopped and * next one is passed to the TTS Engine. Callbacks are issued on the provided {@code listener}. + * Will request audio focus first, failure will trigger onAudioFocusFailed in listener. * * NOTE: Underlying engine may have limit on length of text in each element of the batch; it * will reject anything longer. See {@link TextToSpeech#getMaxSpeechInputLength()}. @@ -138,16 +149,23 @@ public class TTSHelper { * @param listener Observer that will receive callbacks about play-out progress. */ public void requestPlay(List<CharSequence> textToSpeak, Listener listener) { - if (textToSpeak == null || textToSpeak.size() < 1) { + if (textToSpeak == null || textToSpeak.isEmpty()) { throw new IllegalArgumentException("Empty/null textToSpeak"); } + int result = mAudioManager.requestAudioFocus(mNoOpAFChangeListener, + getStream(), + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + listener.onAudioFocusFailed(); + return; + } initMaybeAndKeepAlive(); // Check if its still initializing. if (mInitStatus == TextToSpeech.STOPPED) { // Squash any already queued request. if (mPendingRequest != null) { - mPendingRequest.mListener.onTTSStopped(false /* error */); + onTtsStopped(mPendingRequest.mListener, false /* error */); } mPendingRequest = new SpeechRequest(textToSpeak, listener); } else { @@ -164,10 +182,16 @@ public class TTSHelper { return mTTSEngine.isSpeaking(); } + // wrap call back to listener.onTTSStopped with adandonAudioFocus. + private void onTtsStopped(Listener listener, boolean error) { + mAudioManager.abandonAudioFocus(mNoOpAFChangeListener); + mHandler.post(() -> listener.onTTSStopped(error)); + } + private void playInternal(List<CharSequence> textToSpeak, Listener listener) { if (mInitStatus == TextToSpeech.ERROR) { Log.e(TAG, "TTS setup failed!"); - mHandler.post(() -> listener.onTTSStopped(true /* error */)); + onTtsStopped(listener, true /* error */); return; } @@ -189,7 +213,7 @@ public class TTSHelper { mTTSEngine.stop(); currentBatchId = null; Log.e(TAG, "Queuing text failed!"); - mHandler.post(() -> listener.onTTSStopped(true /* error */)); + onTtsStopped(listener, true /* error */); return; } index--; @@ -207,6 +231,10 @@ public class TTSHelper { shutdownEngine(); } + public int getStream() { + return mTTSEngine.getStream(); + } + private void shutdownEngine() { if (mTTSEngine.isInitialized()) { if (DBG) { @@ -325,7 +353,7 @@ public class TTSHelper { // Handles terminal callbacks for the batch. We invoke stopped and remove ourselves. // No further callbacks will be handled for the batch. private void handleBatchFinished(Pair<String, Integer> parsedId, boolean error) { - mListener.onTTSStopped(error); + onTtsStopped(mListener, error); mListeners.remove(parsedId.first); } } diff --git a/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java b/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java index f9745f4..dbbb5e4 100644 --- a/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java +++ b/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java @@ -156,5 +156,9 @@ public class TTSHelperTest { mStopped = true; mError = error; } + + @Override + public void onAudioFocusFailed() { + } } -}
\ No newline at end of file +} |