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