package com.android.music;
import android.app.Notification;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.media.AudioManager;
-import android.media.MediaFile;
+import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaPlayer;
import android.net.Uri;
-import android.os.Environment;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.os.PowerManager.WakeLock;
import android.provider.MediaStore;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneStateIntentReceiver;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.Random;
import java.util.Vector;
public static final String PREVIOUS_ACTION = "com.android.music.musicservicecommand.previous";
public static final String NEXT_ACTION = "com.android.music.musicservicecommand.next";
- private static final int PHONE_CHANGED = 1;
private static final int TRACK_ENDED = 1;
private static final int RELEASE_WAKELOCK = 2;
private static final int SERVER_DIED = 3;
private static final int FADEIN = 4;
- private static final int MAX_HISTORY_SIZE = 10;
+ private static final int MAX_HISTORY_SIZE = 100;
private MultiPlayer mPlayer;
private String mFileToPlay;
- private PhoneStateIntentReceiver mPsir;
private int mShuffleMode = SHUFFLE_NONE;
private int mRepeatMode = REPEAT_NONE;
private int mMediaMountedCount = 0;
- private int [] mAutoShuffleList = null;
+ private long [] mAutoShuffleList = null;
private boolean mOneShot;
- private int [] mPlayList = null;
+ private long [] mPlayList = null;
private int mPlayListLen = 0;
private Vector<Integer> mHistory = new Vector<Integer>(MAX_HISTORY_SIZE);
private Cursor mCursor;
private boolean mResumeAfterCall = false;
private boolean mIsSupposedToBePlaying = false;
private boolean mQuietMode = false;
-
+ private AudioManager mAudioManager;
+ // used to track what type of audio focus loss caused the playback to pause
+ private boolean mPausedByTransientLossOfFocus = false;
+
private SharedPreferences mPreferences;
// We use this to distinguish between different cards when saving/restoring playlists.
// This will have to change if we want to support multiple simultaneous cards.
// interval after which we stop the service when idle
private static final int IDLE_DELAY = 60000;
- private Handler mPhoneHandler = new Handler() {
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case PHONE_CHANGED:
- Phone.State state = mPsir.getPhoneState();
- if (state == Phone.State.RINGING) {
- AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- int ringvolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
- if (ringvolume > 0) {
- mResumeAfterCall = (isPlaying() || mResumeAfterCall) && (getAudioId() >= 0);
- pause();
- }
- } else if (state == Phone.State.OFFHOOK) {
- // pause the music while a conversation is in progress
- mResumeAfterCall = (isPlaying() || mResumeAfterCall) && (getAudioId() >= 0);
- pause();
- } else if (state == Phone.State.IDLE) {
- // start playing again
- if (mResumeAfterCall) {
- // resume playback only if music was playing
- // when the call was answered
- startAndFadeIn();
- mResumeAfterCall = false;
- }
- }
- break;
- default:
- break;
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (state == TelephonyManager.CALL_STATE_RINGING) {
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ int ringvolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
+ if (ringvolume > 0) {
+ mResumeAfterCall = (isPlaying() || mResumeAfterCall) && (getAudioId() >= 0);
+ pause();
+ }
+ } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
+ // pause the music while a conversation is in progress
+ mResumeAfterCall = (isPlaying() || mResumeAfterCall) && (getAudioId() >= 0);
+ pause();
+ } else if (state == TelephonyManager.CALL_STATE_IDLE) {
+ // start playing again
+ if (mResumeAfterCall) {
+ // resume playback only if music was playing
+ // when the call was answered
+ startAndFadeIn();
+ mResumeAfterCall = false;
+ }
}
}
};
float mCurrentVolume = 1.0f;
@Override
public void handleMessage(Message msg) {
+ MusicUtils.debugLog("mMediaplayerHandler.handleMessage " + msg.what);
switch (msg.what) {
case FADEIN:
if (!isPlaying()) {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String cmd = intent.getStringExtra("command");
+ MusicUtils.debugLog("mIntentReceiver.onReceive " + action + " / " + cmd);
if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
next(true);
} else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
}
};
+ private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
+ public void onAudioFocusChanged(int focusChange) {
+ // AudioFocus is a new feature: focus updates are made verbose on purpose
+ switch (focusChange) {
+ case AudioManager.AUDIOFOCUS_LOSS:
+ Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS");
+ if(isPlaying()) {
+ mPausedByTransientLossOfFocus = false;
+ pause();
+ }
+ break;
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
+ Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");
+ if(isPlaying()) {
+ mPausedByTransientLossOfFocus = true;
+ pause();
+ }
+ break;
+ case AudioManager.AUDIOFOCUS_GAIN:
+ Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN");
+ if(!isPlaying() && mPausedByTransientLossOfFocus) {
+ mPausedByTransientLossOfFocus = false;
+ play();
+ }
+ break;
+ default:
+ Log.e(LOGTAG, "Unknown audio focus change code");
+ }
+ }
+ };
+
public MediaPlaybackService() {
- mPsir = new PhoneStateIntentReceiver(this, mPhoneHandler);
- mPsir.notifyPhoneCallState(PHONE_CHANGED);
}
@Override
public void onCreate() {
super.onCreate();
+
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ mAudioManager.registerAudioFocusListener(mAudioFocusListener);
+ mAudioManager.registerMediaButtonEventReceiver(new ComponentName(getPackageName(),
+ MediaButtonIntentReceiver.class.getName()));
mPreferences = getSharedPreferences("Music", MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
- mCardId = FileUtils.getFatVolumeId(Environment.getExternalStorageDirectory().getPath());
+ mCardId = MusicUtils.getCardId(this);
registerExternalStorageListener();
mPlayer = new MultiPlayer();
mPlayer.setHandler(mMediaplayerHandler);
- // Clear leftover notification in case this service previously got killed while playing
- NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel(PLAYBACKSERVICE_STATUS);
-
reloadQueue();
IntentFilter commandFilter = new IntentFilter();
commandFilter.addAction(PREVIOUS_ACTION);
registerReceiver(mIntentReceiver, commandFilter);
- mPsir.registerIntent();
+ TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
mWakeLock.setReferenceCounted(false);
public void onDestroy() {
// Check that we're not being destroyed while something is still playing.
if (isPlaying()) {
- Log.e("MediaPlaybackService", "Service being destroyed while still playing.");
+ Log.e(LOGTAG, "Service being destroyed while still playing.");
}
- // and for good measure, call mPlayer.stop(), which calls MediaPlayer.reset(), which
- // releases the MediaPlayer's wake lock, if any.
- mPlayer.stop();
+ // release all MediaPlayer resources, including the native player and wakelocks
+ mPlayer.release();
+ mPlayer = null;
+
+ mAudioManager.abandonAudioFocus(mAudioFocusListener);
+ mAudioManager.unregisterAudioFocusListener(mAudioFocusListener);
+ // make sure there aren't any other messages coming
+ mDelayedStopHandler.removeCallbacksAndMessages(null);
+ mMediaplayerHandler.removeCallbacksAndMessages(null);
+
if (mCursor != null) {
mCursor.close();
mCursor = null;
}
+ TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ tmgr.listen(mPhoneStateListener, 0);
+
unregisterReceiver(mIntentReceiver);
if (mUnmountReceiver != null) {
unregisterReceiver(mUnmountReceiver);
mUnmountReceiver = null;
}
- mPsir.unregisterIntent();
mWakeLock.release();
super.onDestroy();
}
// on the phone)
int len = mPlayListLen;
for (int i = 0; i < len; i++) {
- int n = mPlayList[i];
+ long n = mPlayList[i];
if (n == 0) {
q.append("0;");
} else {
while (n != 0) {
- int digit = n & 0xf;
+ int digit = (int)(n & 0xf);
n >>= 4;
q.append(hexdigits[digit]);
}
//Log.i("@@@@ service", "created queue string in " + (System.currentTimeMillis() - start) + " ms");
ed.putString("queue", q.toString());
ed.putInt("cardid", mCardId);
+ if (mShuffleMode != SHUFFLE_NONE) {
+ // In shuffle mode we need to save the history too
+ len = mHistory.size();
+ q.setLength(0);
+ for (int i = 0; i < len; i++) {
+ int n = mHistory.get(i);
+ if (n == 0) {
+ q.append("0;");
+ } else {
+ while (n != 0) {
+ int digit = (n & 0xf);
+ n >>= 4;
+ q.append(hexdigits[digit]);
+ }
+ q.append(";");
+ }
+ }
+ ed.putString("history", q.toString());
+ }
}
ed.putInt("curpos", mPlayPos);
if (mPlayer.isInitialized()) {
// the same one as when the playlist was saved
q = mPreferences.getString("queue", "");
}
- if (q != null && q.length() > 1) {
+ int qlen = q != null ? q.length() : 0;
+ if (qlen > 1) {
//Log.i("@@@@ service", "loaded queue: " + q);
- String [] entries = q.split(";");
- int len = entries.length;
- ensurePlayListCapacity(len);
- for (int i = 0; i < len; i++) {
- if (newstyle) {
- String revhex = entries[i];
- int n = 0;
- for (int j = revhex.length() - 1; j >= 0 ; j--) {
- n <<= 4;
- char c = revhex.charAt(j);
- if (c >= '0' && c <= '9') {
- n += (c - '0');
- } else if (c >= 'a' && c <= 'f') {
- n += (10 + c - 'a');
- } else {
- // bogus playlist data
- len = 0;
- break;
- }
- }
- mPlayList[i] = n;
+ int plen = 0;
+ int n = 0;
+ int shift = 0;
+ for (int i = 0; i < qlen; i++) {
+ char c = q.charAt(i);
+ if (c == ';') {
+ ensurePlayListCapacity(plen + 1);
+ mPlayList[plen] = n;
+ plen++;
+ n = 0;
+ shift = 0;
} else {
- mPlayList[i] = Integer.parseInt(entries[i]);
+ if (c >= '0' && c <= '9') {
+ n += ((c - '0') << shift);
+ } else if (c >= 'a' && c <= 'f') {
+ n += ((10 + c - 'a') << shift);
+ } else {
+ // bogus playlist data
+ plen = 0;
+ break;
+ }
+ shift += 4;
}
}
- mPlayListLen = len;
+ mPlayListLen = plen;
int pos = mPreferences.getInt("curpos", 0);
- if (pos < 0 || pos >= len) {
+ if (pos < 0 || pos >= mPlayListLen) {
// The saved playlist is bogus, discard it
mPlayListLen = 0;
return;
// To deal with this, try querying for the current file, and if
// that fails, wait a while and try again. If that too fails,
// assume there is a problem and don't restore the state.
- Cursor c = MusicUtils.query(this,
+ Cursor crsr = MusicUtils.query(this,
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String [] {"_id"}, "_id=" + mPlayList[mPlayPos] , null, null);
- if (c == null || c.getCount() == 0) {
+ if (crsr == null || crsr.getCount() == 0) {
// wait a bit and try again
SystemClock.sleep(3000);
- c = getContentResolver().query(
+ crsr = getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
mCursorCols, "_id=" + mPlayList[mPlayPos] , null, null);
}
- if (c != null) {
- c.close();
+ if (crsr != null) {
+ crsr.close();
}
// Make sure we don't auto-skip to the next song, since that
long seekpos = mPreferences.getLong("seekpos", 0);
seek(seekpos >= 0 && seekpos < duration() ? seekpos : 0);
+ Log.d(LOGTAG, "restored queue, currently at position "
+ + position() + "/" + duration()
+ + " (requested " + seekpos + ")");
int repmode = mPreferences.getInt("repeatmode", REPEAT_NONE);
if (repmode != REPEAT_ALL && repmode != REPEAT_CURRENT) {
if (shufmode != SHUFFLE_AUTO && shufmode != SHUFFLE_NORMAL) {
shufmode = SHUFFLE_NONE;
}
+ if (shufmode != SHUFFLE_NONE) {
+ // in shuffle mode we need to restore the history too
+ q = mPreferences.getString("history", "");
+ qlen = q != null ? q.length() : 0;
+ if (qlen > 1) {
+ plen = 0;
+ n = 0;
+ shift = 0;
+ mHistory.clear();
+ for (int i = 0; i < qlen; i++) {
+ char c = q.charAt(i);
+ if (c == ';') {
+ if (n >= mPlayListLen) {
+ // bogus history data
+ mHistory.clear();
+ break;
+ }
+ mHistory.add(n);
+ n = 0;
+ shift = 0;
+ } else {
+ if (c >= '0' && c <= '9') {
+ n += ((c - '0') << shift);
+ } else if (c >= 'a' && c <= 'f') {
+ n += ((10 + c - 'a') << shift);
+ } else {
+ // bogus history data
+ mHistory.clear();
+ break;
+ }
+ shift += 4;
+ }
+ }
+ }
+ }
if (shufmode == SHUFFLE_AUTO) {
if (! makeAutoShuffleList()) {
shufmode = SHUFFLE_NONE;
}
@Override
- public void onStart(Intent intent, int startId) {
+ public int onStartCommand(Intent intent, int flags, int startId) {
mServiceStartId = startId;
mDelayedStopHandler.removeCallbacksAndMessages(null);
-
- String action = intent.getAction();
- String cmd = intent.getStringExtra("command");
-
- if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
- next(true);
- } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
- prev();
- } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {
- if (isPlaying()) {
+
+ if (intent != null) {
+ String action = intent.getAction();
+ String cmd = intent.getStringExtra("command");
+ MusicUtils.debugLog("onStartCommand " + action + " / " + cmd);
+
+ if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
+ next(true);
+ } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
+ if (position() < 2000) {
+ prev();
+ } else {
+ seek(0);
+ play();
+ }
+ } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {
+ if (isPlaying()) {
+ pause();
+ } else {
+ play();
+ }
+ } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
pause();
- } else {
- play();
+ } else if (CMDSTOP.equals(cmd)) {
+ pause();
+ seek(0);
}
- } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
- pause();
- } else if (CMDSTOP.equals(cmd)) {
- pause();
- seek(0);
}
// make sure the service will shut down on its own if it was
mDelayedStopHandler.removeCallbacksAndMessages(null);
Message msg = mDelayedStopHandler.obtainMessage();
mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
+ return START_STICKY;
}
@Override
closeExternalStorageFiles(intent.getData().getPath());
} else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
mMediaMountedCount++;
- mCardId = FileUtils.getFatVolumeId(intent.getData().getPath());
+ mCardId = MusicUtils.getCardId(MediaPlaybackService.this);
reloadQueue();
notifyChange(QUEUE_CHANGED);
notifyChange(META_CHANGED);
private void notifyChange(String what) {
Intent i = new Intent(what);
- i.putExtra("id", Integer.valueOf(getAudioId()));
+ i.putExtra("id", Long.valueOf(getAudioId()));
i.putExtra("artist", getArtistName());
i.putExtra("album",getAlbumName());
i.putExtra("track", getTrackName());
// reallocate at 2x requested size so we don't
// need to grow and copy the array for every
// insert
- int [] newlist = new int[size * 2];
- int len = mPlayListLen;
+ long [] newlist = new long[size * 2];
+ int len = mPlayList != null ? mPlayList.length : mPlayListLen;
for (int i = 0; i < len; i++) {
newlist[i] = mPlayList[i];
}
}
// insert the list of songs at the specified position in the playlist
- private void addToPlayList(int [] list, int position) {
+ private void addToPlayList(long [] list, int position) {
int addlen = list.length;
if (position < 0) { // overwrite
mPlayListLen = 0;
* @param list The list of tracks to append.
* @param action NOW, NEXT or LAST
*/
- public void enqueue(int [] list, int action) {
+ public void enqueue(long [] list, int action) {
synchronized(this) {
if (action == NEXT && mPlayPos + 1 < mPlayListLen) {
addToPlayList(list, mPlayPos + 1);
* specified position is 0.
* @param list The new list of tracks.
*/
- public void open(int [] list, int position) {
+ public void open(long [] list, int position) {
synchronized (this) {
if (mShuffleMode == SHUFFLE_AUTO) {
mShuffleMode = SHUFFLE_NORMAL;
}
- int oldId = getAudioId();
+ long oldId = getAudioId();
int listlength = list.length;
boolean newlist = true;
if (mPlayListLen == listlength) {
index2 = mPlayListLen - 1;
}
if (index1 < index2) {
- int tmp = mPlayList[index1];
+ long tmp = mPlayList[index1];
for (int i = index1; i < index2; i++) {
mPlayList[i] = mPlayList[i+1];
}
mPlayPos--;
}
} else if (index2 < index1) {
- int tmp = mPlayList[index1];
+ long tmp = mPlayList[index1];
for (int i = index1; i > index2; i--) {
mPlayList[i] = mPlayList[i-1];
}
* Returns the current play list
* @return An array of integers containing the IDs of the tracks in the play list
*/
- public int [] getQueue() {
+ public long [] getQueue() {
synchronized (this) {
int len = mPlayListLen;
- int [] list = new int[len];
+ long [] list = new long[len];
for (int i = 0; i < len; i++) {
list[i] = mPlayList[i];
}
mCursor.moveToNext();
ensurePlayListCapacity(1);
mPlayListLen = 1;
- mPlayList[0] = mCursor.getInt(IDCOLIDX);
+ mPlayList[0] = mCursor.getLong(IDCOLIDX);
mPlayPos = 0;
}
}
if (!mQuietMode) {
Toast.makeText(this, R.string.playback_failed, Toast.LENGTH_SHORT).show();
}
+ Log.d(LOGTAG, "Failed to open file for playback");
}
} else {
mOpenFailedCounter = 0;
* Starts playback of a previously opened file.
*/
public void play() {
+ mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ mAudioManager.registerMediaButtonEventReceiver(new ComponentName(this.getPackageName(),
+ MediaButtonIntentReceiver.class.getName()));
+
if (mPlayer.isInitialized()) {
// if we are at the end of the song, go to the next song first
- if (mRepeatMode != REPEAT_CURRENT &&
- mPlayer.position() >= mPlayer.duration() - 1) {
+ long duration = mPlayer.duration();
+ if (mRepeatMode != REPEAT_CURRENT && duration > 2000 &&
+ mPlayer.position() >= duration - 2000) {
next(true);
}
mPlayer.start();
- setForeground(true);
- NotificationManager nm = (NotificationManager)
- getSystemService(Context.NOTIFICATION_SERVICE);
-
RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar);
views.setImageViewResource(R.id.icon, R.drawable.stat_notify_musicplayer);
if (getAudioId() < 0) {
} else {
String artist = getArtistName();
views.setTextViewText(R.id.trackname, getTrackName());
- if (artist == null || artist.equals(MediaFile.UNKNOWN_STRING)) {
+ if (artist == null || artist.equals(MediaStore.UNKNOWN_STRING)) {
artist = getString(R.string.unknown_artist_name);
}
String album = getAlbumName();
- if (album == null || album.equals(MediaFile.UNKNOWN_STRING)) {
+ if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) {
album = getString(R.string.unknown_album_name);
}
);
}
- Intent statusintent = new Intent("com.android.music.PLAYBACK_VIEWER");
- statusintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Notification status = new Notification();
status.contentView = views;
status.flags |= Notification.FLAG_ONGOING_EVENT;
status.icon = R.drawable.stat_notify_musicplayer;
status.contentIntent = PendingIntent.getActivity(this, 0,
- new Intent("com.android.music.PLAYBACK_VIEWER"), 0);
- nm.notify(PLAYBACKSERVICE_STATUS, status);
+ new Intent("com.android.music.PLAYBACK_VIEWER")
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0);
+ startForeground(PLAYBACKSERVICE_STATUS, status);
if (!mIsSupposedToBePlaying) {
+ mIsSupposedToBePlaying = true;
notifyChange(PLAYSTATE_CHANGED);
}
- mIsSupposedToBePlaying = true;
+
} else if (mPlayListLen <= 0) {
// This is mostly so that if you press 'play' on a bluetooth headset
// without every having played anything before, it will still play
}
if (remove_status_icon) {
gotoIdleState();
+ } else {
+ stopForeground(false);
}
- setForeground(false);
if (remove_status_icon) {
mIsSupposedToBePlaying = false;
}
if (isPlaying()) {
mPlayer.pause();
gotoIdleState();
- setForeground(false);
mIsSupposedToBePlaying = false;
notifyChange(PLAYSTATE_CHANGED);
saveBookmarkIfNeeded();
return;
}
+ if (mPlayListLen <= 0) {
+ Log.d(LOGTAG, "No play queue");
+ return;
+ }
+
// Store the current file in the history, but keep the history at a
// reasonable size
if (mPlayPos >= 0) {
} else {
// all done
gotoIdleState();
+ if (mIsSupposedToBePlaying) {
+ mIsSupposedToBePlaying = false;
+ notifyChange(PLAYSTATE_CHANGED);
+ }
return;
}
}
}
private void gotoIdleState() {
- NotificationManager nm =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel(PLAYBACKSERVICE_STATUS);
mDelayedStopHandler.removeCallbacksAndMessages(null);
Message msg = mDelayedStopHandler.obtainMessage();
mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
+ stopForeground(true);
}
private void saveBookmarkIfNeeded() {
for (int i = 0; i < to_add; i++) {
// pick something at random from the list
int idx = mRand.nextInt(mAutoShuffleList.length);
- Integer which = mAutoShuffleList[idx];
+ long which = mAutoShuffleList[idx];
ensurePlayListCapacity(mPlayListLen + 1);
mPlayList[mPlayListLen++] = which;
notify = true;
// A simple variation of Random that makes sure that the
// value it returns is not equal to the value it returned
// previously, unless the interval is 1.
- private class Shuffler {
+ private static class Shuffler {
private int mPrevious;
private Random mRandom = new Random();
public int nextInt(int interval) {
return false;
}
int len = c.getCount();
- int[] list = new int[len];
+ long [] list = new long[len];
for (int i = 0; i < len; i++) {
c.moveToNext();
- list[i] = c.getInt(0);
+ list[i] = c.getLong(0);
}
mAutoShuffleList = list;
return true;
* @param id The id to be removed
* @return how many instances of the track were removed
*/
- public int removeTrack(int id) {
+ public int removeTrack(long id) {
int numremoved = 0;
synchronized (this) {
for (int i = 0; i < mPlayListLen; i++) {
* Returns the rowid of the currently playing file, or -1 if
* no file is currently playing.
*/
- public int getAudioId() {
+ public long getAudioId() {
synchronized (this) {
if (mPlayPos >= 0 && mPlayer.isInitialized()) {
return mPlayList[mPlayPos];
openCurrent();
play();
notifyChange(META_CHANGED);
+ if (mShuffleMode == SHUFFLE_AUTO) {
+ doAutoShuffleUpdate();
+ }
}
}
}
}
- public int getArtistId() {
+ public long getArtistId() {
synchronized (this) {
if (mCursor == null) {
return -1;
}
- return mCursor.getInt(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID));
+ return mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID));
}
}
}
}
- public int getAlbumId() {
+ public long getAlbumId() {
synchronized (this) {
if (mCursor == null) {
return -1;
}
- return mCursor.getInt(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
+ return mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
}
}
}
public void start() {
+ MusicUtils.debugLog(new Exception("MultiPlayer.start called"));
mMediaPlayer.start();
}
mIsInitialized = false;
}
+ /**
+ * You CANNOT use this player anymore after calling release()
+ */
+ public void release() {
+ stop();
+ mMediaPlayer.release();
+ }
+
public void pause() {
mMediaPlayer.pause();
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(SERVER_DIED), 2000);
return true;
default:
+ Log.d("MultiPlayer", "Error: " + what + "," + extra);
break;
}
return false;
}
}
- private final IMediaPlaybackService.Stub mBinder = new IMediaPlaybackService.Stub()
- {
+ /*
+ * By making this a static class with a WeakReference to the Service, we
+ * ensure that the Service can be GCd even when the system process still
+ * has a remote reference to the stub.
+ */
+ static class ServiceStub extends IMediaPlaybackService.Stub {
+ WeakReference<MediaPlaybackService> mService;
+
+ ServiceStub(MediaPlaybackService service) {
+ mService = new WeakReference<MediaPlaybackService>(service);
+ }
+
public void openFileAsync(String path)
{
- MediaPlaybackService.this.openAsync(path);
+ mService.get().openAsync(path);
}
public void openFile(String path, boolean oneShot)
{
- MediaPlaybackService.this.open(path, oneShot);
+ mService.get().open(path, oneShot);
}
- public void open(int [] list, int position) {
- MediaPlaybackService.this.open(list, position);
+ public void open(long [] list, int position) {
+ mService.get().open(list, position);
}
public int getQueuePosition() {
- return MediaPlaybackService.this.getQueuePosition();
+ return mService.get().getQueuePosition();
}
public void setQueuePosition(int index) {
- MediaPlaybackService.this.setQueuePosition(index);
+ mService.get().setQueuePosition(index);
}
public boolean isPlaying() {
- return MediaPlaybackService.this.isPlaying();
+ return mService.get().isPlaying();
}
public void stop() {
- MediaPlaybackService.this.stop();
+ mService.get().stop();
}
public void pause() {
- MediaPlaybackService.this.pause();
+ mService.get().pause();
}
public void play() {
- MediaPlaybackService.this.play();
+ mService.get().play();
}
public void prev() {
- MediaPlaybackService.this.prev();
+ mService.get().prev();
}
public void next() {
- MediaPlaybackService.this.next(true);
+ mService.get().next(true);
}
public String getTrackName() {
- return MediaPlaybackService.this.getTrackName();
+ return mService.get().getTrackName();
}
public String getAlbumName() {
- return MediaPlaybackService.this.getAlbumName();
+ return mService.get().getAlbumName();
}
- public int getAlbumId() {
- return MediaPlaybackService.this.getAlbumId();
+ public long getAlbumId() {
+ return mService.get().getAlbumId();
}
public String getArtistName() {
- return MediaPlaybackService.this.getArtistName();
+ return mService.get().getArtistName();
}
- public int getArtistId() {
- return MediaPlaybackService.this.getArtistId();
+ public long getArtistId() {
+ return mService.get().getArtistId();
}
- public void enqueue(int [] list , int action) {
- MediaPlaybackService.this.enqueue(list, action);
+ public void enqueue(long [] list , int action) {
+ mService.get().enqueue(list, action);
}
- public int [] getQueue() {
- return MediaPlaybackService.this.getQueue();
+ public long [] getQueue() {
+ return mService.get().getQueue();
}
public void moveQueueItem(int from, int to) {
- MediaPlaybackService.this.moveQueueItem(from, to);
+ mService.get().moveQueueItem(from, to);
}
public String getPath() {
- return MediaPlaybackService.this.getPath();
+ return mService.get().getPath();
}
- public int getAudioId() {
- return MediaPlaybackService.this.getAudioId();
+ public long getAudioId() {
+ return mService.get().getAudioId();
}
public long position() {
- return MediaPlaybackService.this.position();
+ return mService.get().position();
}
public long duration() {
- return MediaPlaybackService.this.duration();
+ return mService.get().duration();
}
public long seek(long pos) {
- return MediaPlaybackService.this.seek(pos);
+ return mService.get().seek(pos);
}
public void setShuffleMode(int shufflemode) {
- MediaPlaybackService.this.setShuffleMode(shufflemode);
+ mService.get().setShuffleMode(shufflemode);
}
public int getShuffleMode() {
- return MediaPlaybackService.this.getShuffleMode();
+ return mService.get().getShuffleMode();
}
public int removeTracks(int first, int last) {
- return MediaPlaybackService.this.removeTracks(first, last);
+ return mService.get().removeTracks(first, last);
}
- public int removeTrack(int id) {
- return MediaPlaybackService.this.removeTrack(id);
+ public int removeTrack(long id) {
+ return mService.get().removeTrack(id);
}
public void setRepeatMode(int repeatmode) {
- MediaPlaybackService.this.setRepeatMode(repeatmode);
+ mService.get().setRepeatMode(repeatmode);
}
public int getRepeatMode() {
- return MediaPlaybackService.this.getRepeatMode();
+ return mService.get().getRepeatMode();
}
public int getMediaMountedCount() {
- return MediaPlaybackService.this.getMediaMountedCount();
+ return mService.get().getMediaMountedCount();
}
- };
+
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.println("" + mPlayListLen + " items in queue, currently at index " + mPlayPos);
+ writer.println("Currently loaded:");
+ writer.println(getArtistName());
+ writer.println(getAlbumName());
+ writer.println(getTrackName());
+ writer.println(getPath());
+ writer.println("playing: " + mIsSupposedToBePlaying);
+ writer.println("actual: " + mPlayer.mMediaPlayer.isPlaying());
+ writer.println("shuffle mode: " + mShuffleMode);
+ MusicUtils.debugDump(writer);
+ }
+
+ private final IBinder mBinder = new ServiceStub(this);
}