1c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev/* 2c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Copyright (C) 2008 The Android Open Source Project 3c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 4c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Licensed under the Apache License, Version 2.0 (the "License"); 5c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * you may not use this file except in compliance with the License. 6c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * You may obtain a copy of the License at 7c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 8c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * http://www.apache.org/licenses/LICENSE-2.0 9c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 10c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Unless required by applicable law or agreed to in writing, software 11c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * distributed under the License is distributed on an "AS IS" BASIS, 12c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * See the License for the specific language governing permissions and 14c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * limitations under the License. 15c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 16c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 17c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * This file has been copied from com.android.server.NotificationPlayer. The only modification is 18c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * the addition of a volume parameter. Hopefully the framework will adapt AsyncPlayer to support 19c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * all the functionality in this class, at which point this one can be deleted. 20c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev */ 21c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 22c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievpackage com.android.mms.transaction; 23c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 24c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.content.Context; 25c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.media.AudioManager; 26c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.media.MediaPlayer; 27c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.media.MediaPlayer.OnCompletionListener; 28c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.net.Uri; 29c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.os.Looper; 30c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.os.PowerManager; 31c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.os.SystemClock; 32c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport android.util.Log; 33c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 34c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport java.lang.Thread; 35c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievimport java.util.LinkedList; 36c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 37c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev/** 38c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @hide 39c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * This class is provides the same interface and functionality as android.media.AsyncPlayer 40c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * with the following differences: 41c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * - whenever audio is played, audio focus is requested, 42c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * - whenever audio playback is stopped or the playback completed, audio focus is abandoned. 43c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev */ 44c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjievpublic class NotificationPlayer implements OnCompletionListener { 45c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private static final int PLAY = 1; 46c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private static final int STOP = 2; 47c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private static final boolean mDebug = false; 48c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 49c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private static final class Command { 50c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev int code; 51c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Context context; 52c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Uri uri; 53c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev boolean looping; 54c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev int stream; 55c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev float volume; 56c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev long requestTime; 57c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 58c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev @Override 59c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public String toString() { 60c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev return "{ code=" + code + " looping=" + looping + " stream=" + stream 61c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev + " uri=" + uri + " }"; 62c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 63c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 64c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 65c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private LinkedList<Command> mCmdQueue = new LinkedList<Command>(); 66c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 67c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private Looper mLooper; 68c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 69c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev /* 70c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Besides the use of audio focus, the only implementation difference between AsyncPlayer and 71c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * NotificationPlayer resides in the creation of the MediaPlayer. For the completion callback, 72c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * OnCompletionListener, to be called at the end of the playback, the MediaPlayer needs to 73c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * be created with a looper running so its event handler is not null. 74c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev */ 75c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private final class CreationAndCompletionThread extends Thread { 76c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public Command mCmd; 77c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public CreationAndCompletionThread(Command cmd) { 78c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev super(); 79c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mCmd = cmd; 80c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 81c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 82c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev @Override 83c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public void run() { 84c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Looper.prepare(); 85c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mLooper = Looper.myLooper(); 86c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized(this) { 87c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev AudioManager audioManager = 88c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE); 89c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev try { 90c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev MediaPlayer player = new MediaPlayer(); 91c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev player.setAudioStreamType(mCmd.stream); 92c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev player.setDataSource(mCmd.context, mCmd.uri); 93c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev player.setLooping(mCmd.looping); 94c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev player.setVolume(mCmd.volume, mCmd.volume); 95c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev player.prepare(); 96c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null) 97c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev && (mCmd.uri.getEncodedPath().length() > 0)) { 98c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mCmd.looping) { 99c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev audioManager.requestAudioFocus(null, mCmd.stream, 100c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev AudioManager.AUDIOFOCUS_GAIN); 101c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } else { 102c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev audioManager.requestAudioFocus(null, mCmd.stream, 103c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); 104c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 105c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 106c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev player.setOnCompletionListener(NotificationPlayer.this); 107c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev player.start(); 108c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mPlayer != null) { 109c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mPlayer.release(); 110c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 111c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mPlayer = player; 112c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 113c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev catch (Exception e) { 114c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Log.w(mTag, "error loading sound for " + mCmd.uri, e); 115c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 116c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mAudioManager = audioManager; 117c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev this.notify(); 118c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 119c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Looper.loop(); 120c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 121c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 122c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 123c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private void startSound(Command cmd) { 124c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // Preparing can be slow, so if there is something else 125c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // is playing, let it continue until we're done, so there 126c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // is less of a glitch. 127c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev try { 128c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mDebug) Log.d(mTag, "Starting playback"); 129c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev //----------------------------------- 130c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // This is were we deviate from the AsyncPlayer implementation and create the 131c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // MediaPlayer in a new thread with which we're synchronized 132c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized(mCompletionHandlingLock) { 133c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // if another sound was already playing, it doesn't matter we won't get notified 134c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // of the completion, since only the completion notification of the last sound 135c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // matters 136c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if((mLooper != null) 137c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev && (mLooper.getThread().getState() != Thread.State.TERMINATED)) { 138c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mLooper.quit(); 139c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 140c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mCompletionThread = new CreationAndCompletionThread(cmd); 141c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized(mCompletionThread) { 142c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mCompletionThread.start(); 143c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mCompletionThread.wait(); 144c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 145c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 146c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev //----------------------------------- 147c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 148c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev long delay = SystemClock.uptimeMillis() - cmd.requestTime; 149c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (delay > 1000) { 150c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Log.w(mTag, "Notification sound delayed by " + delay + "msecs"); 151c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 152c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 153c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev catch (Exception e) { 154c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Log.w(mTag, "error loading sound for " + cmd.uri, e); 155c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 156c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 157c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 158c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private final class CmdThread extends java.lang.Thread { 159c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev CmdThread() { 160c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev super("NotificationPlayer-" + mTag); 161c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 162c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 163c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev @Override 164c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public void run() { 165c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev while (true) { 166c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Command cmd = null; 167c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 168c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized (mCmdQueue) { 169c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mDebug) Log.d(mTag, "RemoveFirst"); 170c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd = mCmdQueue.removeFirst(); 171c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 172c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 173c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev switch (cmd.code) { 174c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev case PLAY: 175c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mDebug) Log.d(mTag, "PLAY"); 176c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev startSound(cmd); 177c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev break; 178c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev case STOP: 179c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mDebug) Log.d(mTag, "STOP"); 180c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mPlayer != null) { 181c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev long delay = SystemClock.uptimeMillis() - cmd.requestTime; 182c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (delay > 1000) { 183c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Log.w(mTag, "Notification stop delayed by " + delay + "msecs"); 184c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 185c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mPlayer.stop(); 186c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mPlayer.release(); 187c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mPlayer = null; 188c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mAudioManager.abandonAudioFocus(null); 189c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mAudioManager = null; 190c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if((mLooper != null) 191c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev && (mLooper.getThread().getState() != Thread.State.TERMINATED)) { 192c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mLooper.quit(); 193c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 194c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } else { 195c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Log.w(mTag, "STOP command without a player"); 196c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 197c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev break; 198c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 199c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 200c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized (mCmdQueue) { 201c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mCmdQueue.size() == 0) { 202c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // nothing left to do, quit 203c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // doing this check after we're done prevents the case where they 204c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // added it during the operation from spawning two threads and 205c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // trying to do them in parallel. 206c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mThread = null; 207c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev releaseWakeLock(); 208c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev return; 209c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 210c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 211c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 212c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 213c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 214c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 215c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev @Override 216c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public void onCompletion(MediaPlayer mp) { 217c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mAudioManager != null) { 218c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mAudioManager.abandonAudioFocus(null); 219c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 220c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // if there are no more sounds to play, end the Looper to listen for media completion 221c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized (mCmdQueue) { 222c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mCmdQueue.size() == 0) { 223c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized(mCompletionHandlingLock) { 224c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if(mLooper != null) { 225c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mLooper.quit(); 226c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 227c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mCompletionThread = null; 228c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 229c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 230c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 231c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 232c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 233c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private String mTag; 234c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private CmdThread mThread; 235c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private CreationAndCompletionThread mCompletionThread; 236c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private final Object mCompletionHandlingLock = new Object(); 237c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private MediaPlayer mPlayer; 238c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private PowerManager.WakeLock mWakeLock; 239c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private AudioManager mAudioManager; 240c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 241c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // The current state according to the caller. Reality lags behind 242c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // because of the asynchronous nature of this class. 243c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private int mState = STOP; 244c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 245c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev /** 246c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Construct a NotificationPlayer object. 247c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 248c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @param tag a string to use for debugging 249c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev */ 250c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public NotificationPlayer(String tag) { 251c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (tag != null) { 252c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mTag = tag; 253c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } else { 254c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mTag = "NotificationPlayer"; 255c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 256c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 257c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 258c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev /** 259c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Start playing the sound. It will actually start playing at some 260c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * point in the future. There are no guarantees about latency here. 261c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Calling this before another audio file is done playing will stop 262c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * that one and start the new one. 263c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 264c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @param context Your application's context. 265c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @param uri The URI to play. (see {@link MediaPlayer#setDataSource(Context, Uri)}) 266c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @param looping Whether the audio should loop forever. 267c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * (see {@link MediaPlayer#setLooping(boolean)}) 268c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @param stream the AudioStream to use. 269c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * (see {@link MediaPlayer#setAudioStreamType(int)}) 270c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @param volume The volume at which to play this sound, as a fraction of the system volume for 271c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * the relevant stream type. A value of 1 is the maximum and means play at the system 272c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * volume with no attenuation. 273c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev */ 274c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public void play(Context context, Uri uri, boolean looping, int stream, float volume) { 275c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Command cmd = new Command(); 276c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.requestTime = SystemClock.uptimeMillis(); 277c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.code = PLAY; 278c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.context = context; 279c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.uri = uri; 280c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.looping = looping; 281c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.stream = stream; 282c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.volume = volume; 283c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized (mCmdQueue) { 284c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev enqueueLocked(cmd); 285c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mState = PLAY; 286c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 287c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 288c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 289c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev /** 290c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * Stop a previously played sound. It can't be played again or unpaused 291c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * at this point. Calling this multiple times has no ill effects. 292c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev */ 293c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public void stop() { 294c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev synchronized (mCmdQueue) { 295c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // This check allows stop to be called multiple times without starting 296c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // a thread that ends up doing nothing. 297c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mState != STOP) { 298c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev Command cmd = new Command(); 299c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.requestTime = SystemClock.uptimeMillis(); 300c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev cmd.code = STOP; 301c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev enqueueLocked(cmd); 302c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mState = STOP; 303c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 304c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 305c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 306c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 307c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private void enqueueLocked(Command cmd) { 308c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mCmdQueue.add(cmd); 309c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mThread == null) { 310c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev acquireWakeLock(); 311c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mThread = new CmdThread(); 312c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mThread.start(); 313c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 314c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 315c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 316c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev /** 317c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * We want to hold a wake lock while we do the prepare and play. The stop probably is 318c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * optional, but it won't hurt to have it too. The problem is that if you start a sound 319c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * while you're holding a wake lock (e.g. an alarm starting a notification), you want the 320c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * sound to play, but if the CPU turns off before mThread gets to work, it won't. The 321c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * simplest way to deal with this is to make it so there is a wake lock held while the 322c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * thread is starting or running. You're going to need the WAKE_LOCK permission if you're 323c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * going to call this. 324c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 325c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * This must be called before the first time play is called. 326c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * 327c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev * @hide 328c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev */ 329c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev public void setUsesWakeLock(Context context) { 330c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mWakeLock != null || mThread != null) { 331c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // if either of these has happened, we've already played something. 332c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev // and our releases will be out of sync. 333c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock 334c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev + " mThread=" + mThread); 335c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 336c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 337c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag); 338c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 339c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 340c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private void acquireWakeLock() { 341c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mWakeLock != null) { 342c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mWakeLock.acquire(); 343c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 344c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 345c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 346c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev private void releaseWakeLock() { 347c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev if (mWakeLock != null) { 348c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev mWakeLock.release(); 349c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 350c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev } 351c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev} 352c8d727902ff6976c45285a12aab176545a7848bbTodor Kalaydjiev 353