Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 5 | * use this file except in compliance with the License. You may obtain a copy of |
| 6 | * 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, WITHOUT |
| 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | * License for the specific language governing permissions and limitations under |
| 14 | * the License. |
| 15 | */ |
| 16 | package android.speech.tts; |
| 17 | |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 18 | import android.util.Log; |
| 19 | |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 20 | import java.util.Iterator; |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 21 | import java.util.concurrent.LinkedBlockingQueue; |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 22 | |
| 23 | class AudioPlaybackHandler { |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 24 | private static final String TAG = "TTS.AudioPlaybackHandler"; |
| 25 | private static final boolean DBG = false; |
| 26 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 27 | private final LinkedBlockingQueue<PlaybackQueueItem> mQueue = |
| 28 | new LinkedBlockingQueue<PlaybackQueueItem>(); |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 29 | private final Thread mHandlerThread; |
| 30 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 31 | private volatile PlaybackQueueItem mCurrentWorkItem = null; |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 32 | |
| 33 | AudioPlaybackHandler() { |
| 34 | mHandlerThread = new Thread(new MessageLoop(), "TTS.AudioPlaybackThread"); |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 35 | } |
| 36 | |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 37 | public void start() { |
| 38 | mHandlerThread.start(); |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 39 | } |
| 40 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 41 | private void stop(PlaybackQueueItem item) { |
| 42 | if (item == null) { |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 43 | return; |
| 44 | } |
| 45 | |
Przemyslaw Szczepaniak | fc4b289 | 2014-06-26 11:52:20 +0100 | [diff] [blame] | 46 | item.stop(TextToSpeech.STOPPED); |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 47 | } |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 48 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 49 | public void enqueue(PlaybackQueueItem item) { |
| 50 | try { |
| 51 | mQueue.put(item); |
| 52 | } catch (InterruptedException ie) { |
| 53 | // This exception will never be thrown, since we allow our queue |
| 54 | // to be have an unbounded size. put() will therefore never block. |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 55 | } |
| 56 | } |
| 57 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 58 | public void stopForApp(Object callerIdentity) { |
| 59 | if (DBG) Log.d(TAG, "Removing all callback items for : " + callerIdentity); |
| 60 | removeWorkItemsFor(callerIdentity); |
Narayan Kamath | be4ad4a | 2011-07-15 13:01:09 +0100 | [diff] [blame] | 61 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 62 | final PlaybackQueueItem current = mCurrentWorkItem; |
Narayan Kamath | 492b7f0 | 2011-11-29 17:02:06 +0000 | [diff] [blame] | 63 | if (current != null && (current.getCallerIdentity() == callerIdentity)) { |
Narayan Kamath | be4ad4a | 2011-07-15 13:01:09 +0100 | [diff] [blame] | 64 | stop(current); |
| 65 | } |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 66 | } |
| 67 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 68 | public void stop() { |
| 69 | if (DBG) Log.d(TAG, "Stopping all items"); |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 70 | removeAllMessages(); |
Narayan Kamath | 40f71f0 | 2011-11-23 16:42:53 +0000 | [diff] [blame] | 71 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 72 | stop(mCurrentWorkItem); |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 73 | } |
| 74 | |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 75 | /** |
Narayan Kamath | c34f76f | 2011-07-15 11:13:10 +0100 | [diff] [blame] | 76 | * @return false iff the queue is empty and no queue item is currently |
| 77 | * being handled, true otherwise. |
| 78 | */ |
| 79 | public boolean isSpeaking() { |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 80 | return (mQueue.peek() != null) || (mCurrentWorkItem != null); |
Narayan Kamath | c34f76f | 2011-07-15 11:13:10 +0100 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | /** |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 84 | * Shut down the audio playback thread. |
| 85 | */ |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 86 | public void quit() { |
Narayan Kamath | be4ad4a | 2011-07-15 13:01:09 +0100 | [diff] [blame] | 87 | removeAllMessages(); |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 88 | stop(mCurrentWorkItem); |
| 89 | mHandlerThread.interrupt(); |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 90 | } |
| 91 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 92 | /* |
| 93 | * Atomically clear the queue of all messages. |
| 94 | */ |
| 95 | private void removeAllMessages() { |
| 96 | mQueue.clear(); |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 97 | } |
| 98 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 99 | /* |
| 100 | * Remove all messages that originate from a given calling app. |
| 101 | */ |
| 102 | private void removeWorkItemsFor(Object callerIdentity) { |
| 103 | Iterator<PlaybackQueueItem> it = mQueue.iterator(); |
| 104 | |
| 105 | while (it.hasNext()) { |
| 106 | final PlaybackQueueItem item = it.next(); |
| 107 | if (item.getCallerIdentity() == callerIdentity) { |
| 108 | it.remove(); |
Kazuhiro Inaba | a81d17b | 2017-07-25 13:16:13 +0900 | [diff] [blame] | 109 | stop(item); |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 110 | } |
| 111 | } |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 112 | } |
| 113 | |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 114 | /* |
| 115 | * The MessageLoop is a handler like implementation that |
| 116 | * processes messages from a priority queue. |
| 117 | */ |
| 118 | private final class MessageLoop implements Runnable { |
| 119 | @Override |
| 120 | public void run() { |
| 121 | while (true) { |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 122 | PlaybackQueueItem item = null; |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 123 | try { |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 124 | item = mQueue.take(); |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 125 | } catch (InterruptedException ie) { |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 126 | if (DBG) Log.d(TAG, "MessageLoop : Shutting down (interrupted)"); |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 127 | return; |
| 128 | } |
| 129 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 130 | // If stop() or stopForApp() are called between mQueue.take() |
| 131 | // returning and mCurrentWorkItem being set, the current work item |
| 132 | // will be run anyway. |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 133 | |
Narayan Kamath | 67ae6bc | 2011-11-30 14:51:00 +0000 | [diff] [blame] | 134 | mCurrentWorkItem = item; |
| 135 | item.run(); |
| 136 | mCurrentWorkItem = null; |
Narayan Kamath | 4924fe3 | 2011-06-09 11:35:13 +0100 | [diff] [blame] | 137 | } |
| 138 | } |
| 139 | } |
| 140 | |
Narayan Kamath | 8d1fc24 | 2011-06-03 18:11:54 +0100 | [diff] [blame] | 141 | } |