NotificationPlayerpublic class NotificationPlayer extends Object implements android.media.MediaPlayer.OnCompletionListener
Fields Summary |
---|
private static final int | PLAY | private static final int | STOP | private static final boolean | mDebug | private LinkedList | mCmdQueue | private android.os.Looper | mLooper | private String | mTag | private CmdThread | mThread | private CreationAndCompletionThread | mCompletionThread | private final Object | mCompletionHandlingLock | private android.media.MediaPlayer | mPlayer | private PowerManager.WakeLock | mWakeLock | private final Object | mQueueAudioFocusLock | private android.media.AudioManager | mAudioManagerWithAudioFocus | private int | mState |
Constructors Summary |
---|
public NotificationPlayer(String tag)Construct a NotificationPlayer object.
if (tag != null) {
mTag = tag;
} else {
mTag = "NotificationPlayer";
}
|
Methods Summary |
---|
private void | acquireWakeLock()
if (mWakeLock != null) {
mWakeLock.acquire();
}
| private void | enqueueLocked(com.android.systemui.media.NotificationPlayer$Command cmd)
mCmdQueue.add(cmd);
if (mThread == null) {
acquireWakeLock();
mThread = new CmdThread();
mThread.start();
}
| public void | onCompletion(android.media.MediaPlayer mp)
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
if (mDebug) Log.d(mTag, "onCompletion() abandonning AudioFocus");
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
} else {
if (mDebug) Log.d(mTag, "onCompletion() no need to abandon AudioFocus");
}
}
// if there are no more sounds to play, end the Looper to listen for media completion
synchronized (mCmdQueue) {
if (mCmdQueue.size() == 0) {
synchronized(mCompletionHandlingLock) {
if(mLooper != null) {
mLooper.quit();
}
mCompletionThread = null;
}
}
}
| public void | play(android.content.Context context, android.net.Uri uri, boolean looping, int stream)Start playing the sound. It will actually start playing at some
point in the future. There are no guarantees about latency here.
Calling this before another audio file is done playing will stop
that one and start the new one.
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;
cmd.context = context;
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = new AudioAttributes.Builder().setInternalLegacyStreamType(stream).build();
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;
}
| public void | play(android.content.Context context, android.net.Uri uri, boolean looping, android.media.AudioAttributes attributes)Start playing the sound. It will actually start playing at some
point in the future. There are no guarantees about latency here.
Calling this before another audio file is done playing will stop
that one and start the new one.
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;
cmd.context = context;
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = attributes;
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;
}
| private void | releaseWakeLock()
if (mWakeLock != null) {
mWakeLock.release();
}
| public void | setUsesWakeLock(android.content.Context context)We want to hold a wake lock while we do the prepare and play. The stop probably is
optional, but it won't hurt to have it too. The problem is that if you start a sound
while you're holding a wake lock (e.g. an alarm starting a notification), you want the
sound to play, but if the CPU turns off before mThread gets to work, it won't. The
simplest way to deal with this is to make it so there is a wake lock held while the
thread is starting or running. You're going to need the WAKE_LOCK permission if you're
going to call this.
This must be called before the first time play is called.
if (mWakeLock != null || mThread != null) {
// if either of these has happened, we've already played something.
// and our releases will be out of sync.
throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
+ " mThread=" + mThread);
}
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
| private void | startSound(com.android.systemui.media.NotificationPlayer$Command cmd)
// Preparing can be slow, so if there is something else
// is playing, let it continue until we're done, so there
// is less of a glitch.
try {
if (mDebug) Log.d(mTag, "Starting playback");
//-----------------------------------
// This is were we deviate from the AsyncPlayer implementation and create the
// MediaPlayer in a new thread with which we're synchronized
synchronized(mCompletionHandlingLock) {
// if another sound was already playing, it doesn't matter we won't get notified
// of the completion, since only the completion notification of the last sound
// matters
if((mLooper != null)
&& (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
mLooper.quit();
}
mCompletionThread = new CreationAndCompletionThread(cmd);
synchronized(mCompletionThread) {
mCompletionThread.start();
mCompletionThread.wait();
}
}
//-----------------------------------
long delay = SystemClock.uptimeMillis() - cmd.requestTime;
if (delay > 1000) {
Log.w(mTag, "Notification sound delayed by " + delay + "msecs");
}
}
catch (Exception e) {
Log.w(mTag, "error loading sound for " + cmd.uri, e);
}
| public void | stop()Stop a previously played sound. It can't be played again or unpaused
at this point. Calling this multiple times has no ill effects.
synchronized (mCmdQueue) {
// This check allows stop to be called multiple times without starting
// a thread that ends up doing nothing.
if (mState != STOP) {
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = STOP;
enqueueLocked(cmd);
mState = STOP;
}
}
|
|