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