Merge MusicPlayer back into PlaybackService
This commit is contained in:
parent
40ebd4ec1b
commit
0a95320bce
@ -1,452 +0,0 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class MusicPlayer implements Runnable, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final int NOTIFICATION_ID = 2;
|
||||
|
||||
public static final int STATE_NORMAL = 0;
|
||||
public static final int STATE_NO_MEDIA = 1;
|
||||
public static final int STATE_PLAYING = 2;
|
||||
|
||||
public IPlaybackService.Stub mBinder = new IPlaybackService.Stub() {
|
||||
public Song[] getCurrentSongs()
|
||||
{
|
||||
return new Song[] { getSong(-1), getSong(0), getSong(1) };
|
||||
}
|
||||
|
||||
public Song getSong(int delta)
|
||||
{
|
||||
return MusicPlayer.this.getSong(delta);
|
||||
}
|
||||
|
||||
public int getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
public int getPosition()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return mMediaPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
public int getDuration()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return mMediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
public void nextSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, 1, 0));
|
||||
}
|
||||
|
||||
public void previousSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, -1, 0));
|
||||
}
|
||||
|
||||
public void togglePlayback()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(PLAY_PAUSE, 0, 0));
|
||||
}
|
||||
|
||||
public void registerWatcher(IMusicPlayerWatcher watcher)
|
||||
{
|
||||
if (watcher != null)
|
||||
mWatchers.register(watcher);
|
||||
}
|
||||
|
||||
public void unregisterWatcher(IMusicPlayerWatcher watcher)
|
||||
{
|
||||
if (watcher != null)
|
||||
mWatchers.unregister(watcher);
|
||||
}
|
||||
|
||||
public void seekToProgress(int progress)
|
||||
{
|
||||
if (mMediaPlayer == null || !mMediaPlayer.isPlaying())
|
||||
return;
|
||||
|
||||
long position = (long)mMediaPlayer.getDuration() * progress / 1000;
|
||||
mMediaPlayer.seekTo((int)position);
|
||||
}
|
||||
};
|
||||
|
||||
public void queueSong(int songId)
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(QUEUE_ITEM, ITEM_SONG, songId));
|
||||
}
|
||||
|
||||
public void stopQueueing()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(QUEUE_ITEM, ITEM_RESET, 0));
|
||||
}
|
||||
|
||||
private PlaybackService mService;
|
||||
private RemoteCallbackList<IMusicPlayerWatcher> mWatchers;
|
||||
|
||||
private boolean mHeadsetOnly;
|
||||
private boolean mUseRemotePlayer;
|
||||
|
||||
private Handler mHandler;
|
||||
private MediaPlayer mMediaPlayer;
|
||||
private Random mRandom;
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
private int[] mSongs;
|
||||
private ArrayList<Song> mSongTimeline;
|
||||
private int mCurrentSong = -1;
|
||||
private int mQueuePos = 0;
|
||||
private boolean mPlugged = true;
|
||||
private int mState = STATE_NORMAL;
|
||||
|
||||
public MusicPlayer(PlaybackService service)
|
||||
{
|
||||
mService = service;
|
||||
mWatchers = new RemoteCallbackList<IMusicPlayerWatcher>();
|
||||
mSongTimeline = new ArrayList<Song>();
|
||||
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
private static final int SET_SONG = 0;
|
||||
private static final int PLAY_PAUSE = 1;
|
||||
private static final int HEADSET_PLUGGED = 2;
|
||||
private static final int HEADSET_PREF_CHANGED = 3;
|
||||
private static final int QUEUE_ITEM = 4;
|
||||
private static final int TRACK_CHANGED = 5;
|
||||
private static final int RELEASE_WAKE_LOCK = 6;
|
||||
private static final int HANDLE_PLAY = 7;
|
||||
private static final int HANDLE_PAUSE = 8;
|
||||
private static final int RETRIEVE_SONGS = 9;
|
||||
private static final int REMOTE_PLAYER_PREF_CHANGED = 10;
|
||||
|
||||
private static final int ITEM_SONG = 0;
|
||||
private static final int ITEM_RESET = 1;
|
||||
|
||||
public void run()
|
||||
{
|
||||
Looper.prepare();
|
||||
|
||||
mMediaPlayer = new MediaPlayer();
|
||||
mRandom = new Random();
|
||||
|
||||
mHandler = new Handler() {
|
||||
public void handleMessage(Message message)
|
||||
{
|
||||
switch (message.what) {
|
||||
case SET_SONG:
|
||||
setCurrentSong(message.arg1);
|
||||
break;
|
||||
case PLAY_PAUSE:
|
||||
setPlaying(!mMediaPlayer.isPlaying());
|
||||
break;
|
||||
case HEADSET_PLUGGED:
|
||||
boolean plugged = message.arg1 == 1;
|
||||
if (plugged != mPlugged) {
|
||||
mPlugged = plugged;
|
||||
if (mCurrentSong == -1)
|
||||
return;
|
||||
if (!plugged && mMediaPlayer.isPlaying())
|
||||
setPlaying(false);
|
||||
}
|
||||
break;
|
||||
case HEADSET_PREF_CHANGED:
|
||||
mHeadsetOnly = message.arg1 == 1;
|
||||
if (mHeadsetOnly && !mPlugged && mMediaPlayer.isPlaying())
|
||||
pause();
|
||||
break;
|
||||
case QUEUE_ITEM:
|
||||
switch (message.arg1) {
|
||||
case ITEM_SONG:
|
||||
int i = mCurrentSong + 1 + mQueuePos++;
|
||||
Song song = new Song(message.arg2);
|
||||
Toast.makeText(Tumult.getContext(), "Enqueued " + song.title, Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (i < mSongTimeline.size())
|
||||
mSongTimeline.set(i, song);
|
||||
else
|
||||
mSongTimeline.add(song);
|
||||
break;
|
||||
case ITEM_RESET:
|
||||
mQueuePos = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TRACK_CHANGED:
|
||||
setCurrentSong(+1);
|
||||
break;
|
||||
case RELEASE_WAKE_LOCK:
|
||||
if (mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
break;
|
||||
case HANDLE_PLAY:
|
||||
setState(STATE_PLAYING);
|
||||
mService.startForegroundCompat(NOTIFICATION_ID, createNotification());
|
||||
break;
|
||||
case HANDLE_PAUSE:
|
||||
setState(STATE_NORMAL);
|
||||
mService.stopForegroundCompat(0);
|
||||
break;
|
||||
case RETRIEVE_SONGS:
|
||||
retrieveSongs();
|
||||
break;
|
||||
case REMOTE_PLAYER_PREF_CHANGED:
|
||||
mUseRemotePlayer = message.arg1 == 1;
|
||||
if (mState == STATE_PLAYING)
|
||||
mService.startForegroundCompat(NOTIFICATION_ID, createNotification());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_HEADSET_PLUG);
|
||||
filter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
|
||||
filter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mService.registerReceiver(mReceiver, filter);
|
||||
|
||||
PowerManager powerManager = (PowerManager)mService.getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TumultSongChangeLock");
|
||||
|
||||
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mMediaPlayer.setWakeMode(mService, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
mMediaPlayer.setOnCompletionListener(this);
|
||||
retrieveSongs();
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mService);
|
||||
mHeadsetOnly = settings.getBoolean("headset_only", false);
|
||||
mUseRemotePlayer = settings.getBoolean("remote_player", true);
|
||||
settings.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
setCurrentSong(1);
|
||||
|
||||
NotificationManager manager = (NotificationManager)mService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.notify(NOTIFICATION_ID, createNotification());
|
||||
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
public void release()
|
||||
{
|
||||
if (mMediaPlayer != null) {
|
||||
if (mMediaPlayer.isPlaying())
|
||||
mMediaPlayer.pause();
|
||||
mMediaPlayer.release();
|
||||
mMediaPlayer = null;
|
||||
}
|
||||
|
||||
mService.unregisterReceiver(mReceiver);
|
||||
mService.stopForegroundCompat(NOTIFICATION_ID);
|
||||
|
||||
if (mWakeLock != null && mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
}
|
||||
|
||||
public void setState(int state)
|
||||
{
|
||||
if (mState == state)
|
||||
return;
|
||||
|
||||
int oldState = mState;
|
||||
mState = state;
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).stateChanged(oldState, mState);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
}
|
||||
|
||||
private void retrieveSongs()
|
||||
{
|
||||
mSongs = Song.getAllSongs();
|
||||
if (mSongs == null) {
|
||||
setState(STATE_NO_MEDIA);
|
||||
} else {
|
||||
if (mState == STATE_NO_MEDIA)
|
||||
setState(STATE_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
private Notification createNotification()
|
||||
{
|
||||
Song song = getSong(0);
|
||||
|
||||
RemoteViews views = new RemoteViews(mService.getPackageName(), R.layout.statusbar);
|
||||
views.setImageViewResource(R.id.icon, R.drawable.status_icon);
|
||||
views.setTextViewText(R.id.title, song.title);
|
||||
views.setTextViewText(R.id.artist, song.artist);
|
||||
|
||||
Notification notification = new Notification();
|
||||
notification.contentView = views;
|
||||
notification.icon = R.drawable.status_icon;
|
||||
notification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
Intent intent = new Intent(mService, mUseRemotePlayer ? RemoteActivity.class : NowPlayingActivity.class);
|
||||
notification.contentIntent = PendingIntent.getActivity(mService, 0, intent, 0);
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
private void play()
|
||||
{
|
||||
if (mHeadsetOnly && !mPlugged)
|
||||
return;
|
||||
|
||||
mMediaPlayer.start();
|
||||
mHandler.sendEmptyMessage(HANDLE_PLAY);
|
||||
}
|
||||
|
||||
private void pause()
|
||||
{
|
||||
mMediaPlayer.pause();
|
||||
mHandler.sendEmptyMessage(HANDLE_PAUSE);
|
||||
}
|
||||
|
||||
private void setPlaying(boolean play)
|
||||
{
|
||||
if (play)
|
||||
play();
|
||||
else
|
||||
pause();
|
||||
}
|
||||
|
||||
private void setCurrentSong(int delta)
|
||||
{
|
||||
Song song = getSong(delta);
|
||||
if (song == null)
|
||||
return;
|
||||
|
||||
mCurrentSong += delta;
|
||||
|
||||
try {
|
||||
mMediaPlayer.reset();
|
||||
mMediaPlayer.setDataSource(song.path);
|
||||
mMediaPlayer.prepare();
|
||||
if (mState == STATE_PLAYING)
|
||||
play();
|
||||
} catch (IOException e) {
|
||||
Log.e("Tumult", "IOException", e);
|
||||
}
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).songChanged(song);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
|
||||
getSong(+2);
|
||||
|
||||
while (mCurrentSong > 15) {
|
||||
mSongTimeline.remove(0);
|
||||
--mCurrentSong;
|
||||
}
|
||||
}
|
||||
|
||||
public void onCompletion(MediaPlayer player)
|
||||
{
|
||||
mWakeLock.acquire();
|
||||
mHandler.sendEmptyMessage(TRACK_CHANGED);
|
||||
mHandler.sendEmptyMessage(RELEASE_WAKE_LOCK);
|
||||
}
|
||||
|
||||
private Song randomSong()
|
||||
{
|
||||
return new Song(mSongs[mRandom.nextInt(mSongs.length)]);
|
||||
}
|
||||
|
||||
private synchronized Song getSong(int delta)
|
||||
{
|
||||
int pos = mCurrentSong + delta;
|
||||
|
||||
if (pos < 0)
|
||||
return null;
|
||||
|
||||
int size = mSongTimeline.size();
|
||||
if (pos > size)
|
||||
return null;
|
||||
|
||||
if (pos == size) {
|
||||
if (mSongs == null)
|
||||
return null;
|
||||
mSongTimeline.add(randomSong());
|
||||
}
|
||||
|
||||
return mSongTimeline.get(pos);
|
||||
}
|
||||
|
||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context content, Intent intent)
|
||||
{
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_HEADSET_PLUG.equals(action) && mHandler != null) {
|
||||
int plugged = intent.getIntExtra("state", 0) == 130 ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PLUGGED, plugged, 0));
|
||||
} else if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)
|
||||
|| Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)) {
|
||||
mHandler.sendEmptyMessage(RETRIEVE_SONGS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences settings, String key)
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
|
||||
if ("headset_only".equals(key)) {
|
||||
int arg = settings.getBoolean(key, false) ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PREF_CHANGED, arg, 0));
|
||||
} else if ("remote_player".equals(key)) {
|
||||
int arg = settings.getBoolean(key, true) ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(REMOTE_PLAYER_PREF_CHANGED, arg, 0));
|
||||
}
|
||||
}
|
||||
}
|
@ -130,19 +130,19 @@ public class NowPlayingActivity extends Activity implements ServiceConnection, V
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case MusicPlayer.STATE_NORMAL:
|
||||
case PlaybackService.STATE_NORMAL:
|
||||
mControlsBottom.setVisibility(View.VISIBLE);
|
||||
// fall through
|
||||
case MusicPlayer.STATE_PLAYING:
|
||||
case PlaybackService.STATE_PLAYING:
|
||||
removeMessageOverlay();
|
||||
|
||||
if (!mHandler.hasMessages(HIDE))
|
||||
mControlsBottom.setVisibility(View.GONE);
|
||||
|
||||
mSeekBar.setEnabled(state == MusicPlayer.STATE_PLAYING);
|
||||
mPlayPauseButton.setImageResource(state == MusicPlayer.STATE_PLAYING ? R.drawable.pause : R.drawable.play);
|
||||
mSeekBar.setEnabled(state == PlaybackService.STATE_PLAYING);
|
||||
mPlayPauseButton.setImageResource(state == PlaybackService.STATE_PLAYING ? R.drawable.pause : R.drawable.play);
|
||||
break;
|
||||
case MusicPlayer.STATE_NO_MEDIA:
|
||||
case PlaybackService.STATE_NO_MEDIA:
|
||||
makeMessageOverlay();
|
||||
|
||||
RelativeLayout.LayoutParams layoutParams =
|
||||
@ -353,7 +353,7 @@ public class NowPlayingActivity extends Activity implements ServiceConnection, V
|
||||
if (view == mCoverView) {
|
||||
if (mControlsTop.getVisibility() == View.VISIBLE) {
|
||||
mControlsTop.setVisibility(View.GONE);
|
||||
if (mState == MusicPlayer.STATE_PLAYING)
|
||||
if (mState == PlaybackService.STATE_PLAYING)
|
||||
mControlsBottom.setVisibility(View.GONE);
|
||||
} else {
|
||||
mControlsTop.setVisibility(View.VISIBLE);
|
||||
@ -383,7 +383,7 @@ public class NowPlayingActivity extends Activity implements ServiceConnection, V
|
||||
switch (message.what) {
|
||||
case HIDE:
|
||||
mControlsTop.setVisibility(View.GONE);
|
||||
if (mState == MusicPlayer.STATE_PLAYING)
|
||||
if (mState == PlaybackService.STATE_PLAYING)
|
||||
mControlsBottom.setVisibility(View.GONE);
|
||||
break;
|
||||
case UPDATE_PROGRESS:
|
||||
|
@ -1,27 +1,122 @@
|
||||
package org.kreed.tumult;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class PlaybackService extends Service {
|
||||
private MusicPlayer mPlayer;
|
||||
public class PlaybackService extends Service implements Runnable, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final int NOTIFICATION_ID = 2;
|
||||
|
||||
public static final int STATE_NORMAL = 0;
|
||||
public static final int STATE_NO_MEDIA = 1;
|
||||
public static final int STATE_PLAYING = 2;
|
||||
|
||||
private RemoteCallbackList<IMusicPlayerWatcher> mWatchers;
|
||||
private NotificationManager mNotificationManager;
|
||||
private Method mStartForeground;
|
||||
private Method mStopForeground;
|
||||
|
||||
public IPlaybackService.Stub mBinder = new IPlaybackService.Stub() {
|
||||
public Song[] getCurrentSongs()
|
||||
{
|
||||
return new Song[] { getSong(-1), getSong(0), getSong(1) };
|
||||
}
|
||||
|
||||
public Song getSong(int delta)
|
||||
{
|
||||
return PlaybackService.this.getSong(delta);
|
||||
}
|
||||
|
||||
public int getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
public int getPosition()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return mMediaPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
public int getDuration()
|
||||
{
|
||||
if (mMediaPlayer == null)
|
||||
return 0;
|
||||
return mMediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
public void nextSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, 1, 0));
|
||||
}
|
||||
|
||||
public void previousSong()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(SET_SONG, -1, 0));
|
||||
}
|
||||
|
||||
public void togglePlayback()
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(PLAY_PAUSE, 0, 0));
|
||||
}
|
||||
|
||||
public void registerWatcher(IMusicPlayerWatcher watcher)
|
||||
{
|
||||
if (watcher != null)
|
||||
mWatchers.register(watcher);
|
||||
}
|
||||
|
||||
public void unregisterWatcher(IMusicPlayerWatcher watcher)
|
||||
{
|
||||
if (watcher != null)
|
||||
mWatchers.unregister(watcher);
|
||||
}
|
||||
|
||||
public void seekToProgress(int progress)
|
||||
{
|
||||
if (mMediaPlayer == null || !mMediaPlayer.isPlaying())
|
||||
return;
|
||||
long position = (long)mMediaPlayer.getDuration() * progress / 1000;
|
||||
mMediaPlayer.seekTo((int)position);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent)
|
||||
{
|
||||
if (mPlayer == null)
|
||||
return null;
|
||||
return mPlayer.mBinder;
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -35,29 +130,41 @@ public class PlaybackService extends Service {
|
||||
// Running on an older platform.
|
||||
mStartForeground = mStopForeground = null;
|
||||
}
|
||||
|
||||
mPlayer = new MusicPlayer(this);
|
||||
|
||||
mWatchers = new RemoteCallbackList<IMusicPlayerWatcher>();
|
||||
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onStart(Intent intent, int flags)
|
||||
{
|
||||
int id;
|
||||
if (mHandler == null)
|
||||
return;
|
||||
|
||||
if ((id = intent.getIntExtra("songId", -1)) != -1)
|
||||
mPlayer.queueSong(id);
|
||||
else
|
||||
mPlayer.stopQueueing();
|
||||
int id = intent.getIntExtra("songId", -1);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(QUEUE_ITEM, id, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
if (mPlayer != null) {
|
||||
mPlayer.release();
|
||||
mPlayer = null;
|
||||
|
||||
if (mMediaPlayer != null) {
|
||||
MediaPlayer player = mMediaPlayer;
|
||||
mMediaPlayer = null;
|
||||
|
||||
if (player.isPlaying())
|
||||
player.pause();
|
||||
player.release();
|
||||
}
|
||||
|
||||
unregisterReceiver(mReceiver);
|
||||
stopForegroundCompat(NOTIFICATION_ID);
|
||||
|
||||
if (mWakeLock != null && mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
}
|
||||
|
||||
public void startForegroundCompat(int id, Notification notification)
|
||||
@ -93,4 +200,304 @@ public class PlaybackService extends Service {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context content, Intent intent)
|
||||
{
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_HEADSET_PLUG.equals(action) && mHandler != null) {
|
||||
int plugged = intent.getIntExtra("state", 0) == 130 ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PLUGGED, plugged, 0));
|
||||
} else if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)
|
||||
|| Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)) {
|
||||
mHandler.sendEmptyMessage(RETRIEVE_SONGS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences settings, String key)
|
||||
{
|
||||
if (mHandler == null)
|
||||
return;
|
||||
|
||||
if ("headset_only".equals(key)) {
|
||||
int arg = settings.getBoolean(key, false) ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(HEADSET_PREF_CHANGED, arg, 0));
|
||||
} else if ("remote_player".equals(key)) {
|
||||
int arg = settings.getBoolean(key, true) ? 1 : 0;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(REMOTE_PLAYER_PREF_CHANGED, arg, 0));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean mHeadsetOnly;
|
||||
private boolean mUseRemotePlayer;
|
||||
|
||||
private Handler mHandler;
|
||||
private MediaPlayer mMediaPlayer;
|
||||
private Random mRandom;
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
private int[] mSongs;
|
||||
private ArrayList<Song> mSongTimeline;
|
||||
private int mCurrentSong = -1;
|
||||
private int mQueuePos = 0;
|
||||
private boolean mPlugged = true;
|
||||
private int mState = STATE_NORMAL;
|
||||
|
||||
private static final int SET_SONG = 0;
|
||||
private static final int PLAY_PAUSE = 1;
|
||||
private static final int HEADSET_PLUGGED = 2;
|
||||
private static final int HEADSET_PREF_CHANGED = 3;
|
||||
private static final int QUEUE_ITEM = 4;
|
||||
private static final int TRACK_CHANGED = 5;
|
||||
private static final int RELEASE_WAKE_LOCK = 6;
|
||||
private static final int HANDLE_PLAY = 7;
|
||||
private static final int HANDLE_PAUSE = 8;
|
||||
private static final int RETRIEVE_SONGS = 9;
|
||||
private static final int REMOTE_PLAYER_PREF_CHANGED = 10;
|
||||
|
||||
public void run()
|
||||
{
|
||||
Looper.prepare();
|
||||
|
||||
mMediaPlayer = new MediaPlayer();
|
||||
mSongTimeline = new ArrayList<Song>();
|
||||
mRandom = new Random();
|
||||
|
||||
mHandler = new Handler() {
|
||||
public void handleMessage(Message message)
|
||||
{
|
||||
switch (message.what) {
|
||||
case SET_SONG:
|
||||
setCurrentSong(message.arg1);
|
||||
break;
|
||||
case PLAY_PAUSE:
|
||||
setPlaying(!mMediaPlayer.isPlaying());
|
||||
break;
|
||||
case HEADSET_PLUGGED:
|
||||
boolean plugged = message.arg1 == 1;
|
||||
if (plugged != mPlugged) {
|
||||
mPlugged = plugged;
|
||||
if (mCurrentSong == -1)
|
||||
return;
|
||||
if (!plugged && mMediaPlayer.isPlaying())
|
||||
setPlaying(false);
|
||||
}
|
||||
break;
|
||||
case HEADSET_PREF_CHANGED:
|
||||
mHeadsetOnly = message.arg1 == 1;
|
||||
if (mHeadsetOnly && !mPlugged && mMediaPlayer.isPlaying())
|
||||
pause();
|
||||
break;
|
||||
case QUEUE_ITEM:
|
||||
if (message.arg1 == -1) {
|
||||
mQueuePos = 0;
|
||||
} else {
|
||||
Song song = new Song(message.arg1);
|
||||
Toast.makeText(Tumult.getContext(), "Enqueued " + song.title, Toast.LENGTH_SHORT).show();
|
||||
|
||||
int i = mCurrentSong + 1 + mQueuePos++;
|
||||
if (i < mSongTimeline.size())
|
||||
mSongTimeline.set(i, song);
|
||||
else
|
||||
mSongTimeline.add(song);
|
||||
}
|
||||
break;
|
||||
case TRACK_CHANGED:
|
||||
setCurrentSong(+1);
|
||||
break;
|
||||
case RELEASE_WAKE_LOCK:
|
||||
if (mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
break;
|
||||
case HANDLE_PLAY:
|
||||
setState(STATE_PLAYING);
|
||||
startForegroundCompat(NOTIFICATION_ID, createNotification());
|
||||
break;
|
||||
case HANDLE_PAUSE:
|
||||
setState(STATE_NORMAL);
|
||||
stopForegroundCompat(0);
|
||||
break;
|
||||
case RETRIEVE_SONGS:
|
||||
retrieveSongs();
|
||||
break;
|
||||
case REMOTE_PLAYER_PREF_CHANGED:
|
||||
mUseRemotePlayer = message.arg1 == 1;
|
||||
if (mState == STATE_PLAYING)
|
||||
startForegroundCompat(NOTIFICATION_ID, createNotification());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_HEADSET_PLUG);
|
||||
filter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
|
||||
filter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
registerReceiver(mReceiver, filter);
|
||||
|
||||
PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TumultSongChangeLock");
|
||||
|
||||
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mMediaPlayer.setWakeMode(this, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
mMediaPlayer.setOnCompletionListener(this);
|
||||
retrieveSongs();
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mHeadsetOnly = settings.getBoolean("headset_only", false);
|
||||
mUseRemotePlayer = settings.getBoolean("remote_player", true);
|
||||
settings.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
setCurrentSong(1);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, createNotification());
|
||||
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
|
||||
public void setState(int state)
|
||||
{
|
||||
if (mState == state)
|
||||
return;
|
||||
|
||||
int oldState = mState;
|
||||
mState = state;
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).stateChanged(oldState, mState);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
}
|
||||
|
||||
private void retrieveSongs()
|
||||
{
|
||||
mSongs = Song.getAllSongs();
|
||||
if (mSongs == null) {
|
||||
setState(STATE_NO_MEDIA);
|
||||
} else {
|
||||
if (mState == STATE_NO_MEDIA)
|
||||
setState(STATE_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
private Notification createNotification()
|
||||
{
|
||||
Song song = getSong(0);
|
||||
|
||||
RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar);
|
||||
views.setImageViewResource(R.id.icon, R.drawable.status_icon);
|
||||
views.setTextViewText(R.id.title, song.title);
|
||||
views.setTextViewText(R.id.artist, song.artist);
|
||||
|
||||
Notification notification = new Notification();
|
||||
notification.contentView = views;
|
||||
notification.icon = R.drawable.status_icon;
|
||||
notification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
Intent intent = new Intent(this, mUseRemotePlayer ? RemoteActivity.class : NowPlayingActivity.class);
|
||||
notification.contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
private void play()
|
||||
{
|
||||
if (mHeadsetOnly && !mPlugged)
|
||||
return;
|
||||
|
||||
mMediaPlayer.start();
|
||||
mHandler.sendEmptyMessage(HANDLE_PLAY);
|
||||
}
|
||||
|
||||
private void pause()
|
||||
{
|
||||
mMediaPlayer.pause();
|
||||
mHandler.sendEmptyMessage(HANDLE_PAUSE);
|
||||
}
|
||||
|
||||
private void setPlaying(boolean play)
|
||||
{
|
||||
if (play)
|
||||
play();
|
||||
else
|
||||
pause();
|
||||
}
|
||||
|
||||
private void setCurrentSong(int delta)
|
||||
{
|
||||
Song song = getSong(delta);
|
||||
if (song == null)
|
||||
return;
|
||||
|
||||
mCurrentSong += delta;
|
||||
|
||||
try {
|
||||
mMediaPlayer.reset();
|
||||
mMediaPlayer.setDataSource(song.path);
|
||||
mMediaPlayer.prepare();
|
||||
if (mState == STATE_PLAYING)
|
||||
play();
|
||||
} catch (IOException e) {
|
||||
Log.e("Tumult", "IOException", e);
|
||||
}
|
||||
|
||||
int i = mWatchers.beginBroadcast();
|
||||
while (--i != -1) {
|
||||
try {
|
||||
mWatchers.getBroadcastItem(i).songChanged(song);
|
||||
} catch (RemoteException e) {
|
||||
// Null elements will be removed automatically
|
||||
}
|
||||
}
|
||||
mWatchers.finishBroadcast();
|
||||
|
||||
getSong(+2);
|
||||
|
||||
while (mCurrentSong > 15) {
|
||||
mSongTimeline.remove(0);
|
||||
--mCurrentSong;
|
||||
}
|
||||
}
|
||||
|
||||
public void onCompletion(MediaPlayer player)
|
||||
{
|
||||
mWakeLock.acquire();
|
||||
mHandler.sendEmptyMessage(TRACK_CHANGED);
|
||||
mHandler.sendEmptyMessage(RELEASE_WAKE_LOCK);
|
||||
}
|
||||
|
||||
private Song randomSong()
|
||||
{
|
||||
return new Song(mSongs[mRandom.nextInt(mSongs.length)]);
|
||||
}
|
||||
|
||||
private synchronized Song getSong(int delta)
|
||||
{
|
||||
if (mSongTimeline == null)
|
||||
return null;
|
||||
|
||||
int pos = mCurrentSong + delta;
|
||||
|
||||
if (pos < 0)
|
||||
return null;
|
||||
|
||||
int size = mSongTimeline.size();
|
||||
if (pos > size)
|
||||
return null;
|
||||
|
||||
if (pos == size) {
|
||||
if (mSongs == null)
|
||||
return null;
|
||||
mSongTimeline.add(randomSong());
|
||||
}
|
||||
|
||||
return mSongTimeline.get(pos);
|
||||
}
|
||||
}
|
@ -101,10 +101,10 @@ public class RemoteActivity extends Activity implements ServiceConnection, View.
|
||||
|
||||
private void setState(int state)
|
||||
{
|
||||
if (state == MusicPlayer.STATE_NO_MEDIA)
|
||||
if (state == PlaybackService.STATE_NO_MEDIA)
|
||||
finish();
|
||||
|
||||
mPlayPauseButton.setImageResource(state == MusicPlayer.STATE_PLAYING ? R.drawable.pause : R.drawable.play);
|
||||
mPlayPauseButton.setImageResource(state == PlaybackService.STATE_PLAYING ? R.drawable.pause : R.drawable.play);
|
||||
}
|
||||
|
||||
private IMusicPlayerWatcher mWatcher = new IMusicPlayerWatcher.Stub() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user