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