/src/org/abrantix/rockon/rockonnggl/RockOnNextGenService.java
Java | 2680 lines | 1898 code | 223 blank | 559 comment | 484 complexity | 341b5d66323f99a40fd671c730899a12 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /*
- * Adapted from the Android stock Music app
- */
- package org.abrantix.rockon.rockonnggl;
- import android.app.Notification;
- import android.app.NotificationManager;
- import android.app.PendingIntent;
- import android.app.Service;
- import android.appwidget.AppWidgetManager;
- import android.content.ContentResolver;
- import android.content.ContentUris;
- import android.content.ContentValues;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.content.BroadcastReceiver;
- import android.content.SharedPreferences;
- import android.content.SharedPreferences.Editor;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteException;
- import android.media.AudioManager;
- import android.media.MediaPlayer;
- import android.net.Uri;
- import android.os.Environment;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.PowerManager;
- import android.os.RemoteException;
- import android.os.SystemClock;
- import android.os.PowerManager.WakeLock;
- import android.preference.PreferenceManager;
- 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 java.io.File;
- import java.io.IOException;
- import java.lang.ref.WeakReference;
- import java.util.Random;
- import java.util.Vector;
- import java.util.jar.Pack200.Unpacker;
- import org.abrantix.rockon.rockonnggl.IRockOnNextGenService;
- import org.abrantix.rockon.rockonnggl.R;
- /**
- * Provides "background" audio playback capabilities, allowing the
- * user to switch between activities without stopping playback.
- */
- public class RockOnNextGenService extends Service {
- private static final String TAG = "RockOnNextGenService";
- public static final int PLAYBACKSERVICE_STATUS = 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 = 100;
-
- private static boolean mLock = false;
- private static String mScrobblerName = "none";
-
- private int mRockOnShuffleMode = Constants.SHUFFLE_NONE;
- private int mRockOnRepeatMode = Constants.REPEAT_NONE;
- private int mPlaylistId = Constants.PLAYLIST_UNKNOWN;
-
- private MultiPlayer mPlayer;
- private String mFileToPlay;
- private int mShuffleMode = Constants.SHUFFLE_NONE;
- private int mRepeatMode = Constants.REPEAT_NONE;
- private int mMediaMountedCount = 0;
- private long [] mAutoShuffleList = null;
- private boolean mOneShot; //??
- private long [] mPlayList = null;
- private int mPlayListLen = 0;
- private Vector<Integer> mHistory = new Vector<Integer>(MAX_HISTORY_SIZE);
- private Cursor mCursor;
- private int mPlayPos = -1;
- private final Shuffler mRand = new Shuffler();
- private int mOpenFailedCounter = 0;
- String[] mCursorCols = new String[] {
- "audio._id AS _id", // index must match IDCOLIDX below
- MediaStore.Audio.Media.ARTIST,
- MediaStore.Audio.Media.ALBUM,
- MediaStore.Audio.Media.TITLE,
- MediaStore.Audio.Media.DATA,
- MediaStore.Audio.Media.MIME_TYPE,
- MediaStore.Audio.Media.ALBUM_ID,
- MediaStore.Audio.Media.ARTIST_ID
- };
- private final static int IDCOLIDX = 0;
- private BroadcastReceiver mUnmountReceiver = null;
- private WakeLock mWakeLock;
- private int mServiceStartId = -1;
- private boolean mServiceInUse = false;
- private boolean mResumeAfterCall = false;
- private boolean mIsSupposedToBePlaying = false;
- private boolean mQuietMode = 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.
- private int mCardId;
-
- private RockOnNextGenAppWidgetProvider mAppWidgetProvider =
- RockOnNextGenAppWidgetProvider.getInstance();
- private RockOnNextGenAppWidgetProvider3x3 mAppWidgetProvider3x3 =
- RockOnNextGenAppWidgetProvider3x3.getInstance();
- private RockOnNextGenAppWidgetProvider4x4 mAppWidgetProvider4x4 =
- RockOnNextGenAppWidgetProvider4x4.getInstance();
- private RockOnNextGenAppWidgetProvider4x1 mAppWidgetProvider4x1 =
- RockOnNextGenAppWidgetProvider4x1.getInstance();
-
- private ScreenOnIntentReceiver mScreenOnReceiver;
-
- // interval after which we stop the service when idle
- private static final int IDLE_DELAY = 60000;
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- 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;
- }
- }
- }
- };
-
- private void startAndFadeIn() {
- mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
- }
-
- private Handler mMediaplayerHandler = new Handler() {
- float mCurrentVolume = 1.0f;
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case FADEIN:
- if (!isPlaying()) {
- mCurrentVolume = 0f;
- mPlayer.setVolume(mCurrentVolume);
- play();
- mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
- } else {
- mCurrentVolume += 0.01f;
- if (mCurrentVolume < 1.0f) {
- mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
- } else {
- mCurrentVolume = 1.0f;
- }
- mPlayer.setVolume(mCurrentVolume);
- }
- break;
- case SERVER_DIED:
- if (mIsSupposedToBePlaying) {
- next(true);
- } else {
- // the server died when we were idle, so just
- // reopen the same song (it will start again
- // from the beginning though when the user
- // restarts)
- openCurrent();
- }
- break;
- case TRACK_ENDED:
- if (mRepeatMode == Constants.REPEAT_CURRENT) {
- seek(0);
- play();
- } else if (!mOneShot) {
- next(false);
- } else {
- notifyChange(Constants.PLAYBACK_COMPLETE);
- mIsSupposedToBePlaying = false;
- }
- break;
- case RELEASE_WAKELOCK:
- mWakeLock.release();
- break;
- default:
- break;
- }
- }
- };
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- String cmd = intent.getStringExtra("command");
- Log.i(TAG, cmd);
- if (Constants.CMDNEXT.equals(cmd) || Constants.NEXT_ACTION.equals(action)) {
- next(true);
- } else if (Constants.CMDPREVIOUS.equals(cmd) || Constants.PREVIOUS_ACTION.equals(action)) {
- prev();
- } else if (Constants.CMDTOGGLEPAUSE.equals(cmd) || Constants.TOGGLEPAUSE_ACTION.equals(action)) {
- if (isPlaying()) {
- pause();
- } else {
- play();
- }
- } else if (Constants.CMDPAUSE.equals(cmd) || Constants.PAUSE_ACTION.equals(action)) {
- pause();
- } else if (Constants.CMDSTOP.equals(cmd)) {
- pause();
- seek(0);
- }
- else if (RockOnNextGenAppWidgetProvider.CMDAPPWIDGETUPDATE.equals(cmd)) {
- // Someone asked us to refresh a set of specific widgets, probably
- // because they were just added.
- int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
- if(mAppWidgetProvider != null)
- mAppWidgetProvider.performUpdate(RockOnNextGenService.this, appWidgetIds);
- if(mAppWidgetProvider3x3 != null)
- mAppWidgetProvider3x3.performUpdate(RockOnNextGenService.this, appWidgetIds);
- if(mAppWidgetProvider4x4 != null)
- mAppWidgetProvider4x4.performUpdate(RockOnNextGenService.this, appWidgetIds);
- if(mAppWidgetProvider4x1 != null)
- mAppWidgetProvider4x1.performUpdate(RockOnNextGenService.this, appWidgetIds);
- }
- }
- };
- public RockOnNextGenService() {
- }
- @Override
- public void onCreate() {
- super.onCreate();
-
- Log.i(TAG, "SERVICE onCreate");
-
- // mPreferences = getSharedPreferences("Music", MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
- mPreferences = getSharedPreferences("CubedMusic", MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
- //PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
-
- mCardId = 0; // should be the serial number of the sd card
- // mCardId = FileUtils.getFatVolumeId(Environment.getExternalStorageDirectory().getPath());
-
- registerExternalStorageListener();
- registerScreenOnReceiver();
- // Needs to be done in this thread, since otherwise ApplicationContext.getPowerManager() crashes.
- mPlayer = new MultiPlayer();
- mPlayer.setHandler(mMediaplayerHandler);
- reloadQueue();
-
- IntentFilter commandFilter = new IntentFilter();
- commandFilter.addAction(Constants.SERVICECMD);
- commandFilter.addAction(Constants.TOGGLEPAUSE_ACTION);
- commandFilter.addAction(Constants.PAUSE_ACTION);
- commandFilter.addAction(Constants.NEXT_ACTION);
- commandFilter.addAction(Constants.PREVIOUS_ACTION);
- // commandFilter.addAction(Constants.SHUFFLE_AUTO_ACTION);
- // commandFilter.addAction(Constants.SHUFFLE_NONE_ACTION);
- // commandFilter.addAction(Constants.REPEAT_CURRENT_ACTION);
- // commandFilter.addAction(Constants.REPEAT_NONE_ACTION);
- registerReceiver(mIntentReceiver, commandFilter);
-
- 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);
- // If the service was idle, but got killed before it stopped itself, the
- // system will relaunch it. Make sure it gets stopped again in that case.
- Message msg = mDelayedStopHandler.obtainMessage();
- mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
- }
-
- @Override
- public void onDestroy() {
-
- // Check that we're not being destroyed while something is still playing.
- if (isPlaying()) {
- Log.i(TAG, "Service being destroyed while still playing.");
- }
- // release all MediaPlayer resources, including the native player and wakelocks
- mPlayer.release();
- mPlayer = null;
-
- // 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;
- }
-
- unregisterScreenOnReceiver();
-
- mWakeLock.release();
- super.onDestroy();
- }
-
- private final char hexdigits [] = new char [] {
- '0', '1', '2', '3',
- '4', '5', '6', '7',
- '8', '9', 'a', 'b',
- 'c', 'd', 'e', 'f'
- };
- private void saveQueue(boolean full) {
- if (mOneShot) {
- return;
- }
- Editor ed = mPreferences.edit();
- //long start = System.currentTimeMillis();
- if(mPlayList != null && mPlayList.length > 0)
- ed.putLong("audioId", mPlayList[0]);
- if (full) {
- StringBuilder q = new StringBuilder();
-
- // The current playlist is saved as a list of "reverse hexadecimal"
- // numbers, which we can generate faster than normal decimal or
- // hexadecimal numbers, which in turn allows us to save the playlist
- // more often without worrying too much about performance.
- // (saving the full state takes about 40 ms under no-load conditions
- // on the phone)
- int len = mPlayListLen;
- for (int i = 0; i < len; i++) {
- long n = mPlayList[i];
- if (n == 0) {
- q.append("0;");
- } else {
- while (n != 0) {
- int digit = (int)(n & 0xf);
- n >>= 4;
- q.append(hexdigits[digit]);
- }
- q.append(";");
- }
- }
- //Log.i("@@@@ service", "created queue string in " + (System.currentTimeMillis() - start) + " ms");
- ed.putString("queue", q.toString());
- ed.putInt("cardid", mCardId);
- if (mShuffleMode != Constants.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()) {
- ed.putLong("seekpos", mPlayer.position());
- }
- ed.putInt("repeatmode", mRepeatMode);
- ed.putInt("shufflemode", mShuffleMode);
- // ed.putInt(Constants.prefkey_mPlaylistId, mPlaylistId);
- ed.putInt("rockonrepeatmode", mRockOnRepeatMode);
- ed.putInt("rockonshufflemode", mRockOnShuffleMode);
- ed.commit();
-
- //Log.i("@@@@ service", "saved state in " + (System.currentTimeMillis() - start) + " ms");
- }
- private void reloadQueue() {
- String q = null;
-
- // the playlist should always be restored! or should it?
- mPlaylistId = mPreferences.getInt(
- Constants.prefkey_mPlaylistId,
- Constants.PLAYLIST_ALL);
- // sanity check
- if(mPlaylistId == Constants.PLAYLIST_UNKNOWN)
- mPlaylistId = Constants.PLAYLIST_ALL;
-
- boolean newstyle = false;
- int id = mCardId;
- if (mPreferences.contains("cardid")) {
- newstyle = true;
- id = mPreferences.getInt("cardid", ~mCardId);
- }
- if (id == mCardId) {
- // Only restore the saved playlist if the card is still
- // the same one as when the playlist was saved
- q = mPreferences.getString("queue", "");
- }
- int qlen = q != null ? q.length() : 0;
- if (qlen > 1) {
- //Log.i("@@@@ service", "loaded queue: " + q);
- 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 {
- 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 = plen;
- int pos = mPreferences.getInt("curpos", 0);
- if (pos < 0 || pos >= mPlayListLen) {
- // The saved playlist is bogus, discard it
- mPlayListLen = 0;
- return;
- }
- mPlayPos = pos;
-
- // When reloadQueue is called in response to a card-insertion,
- // we might not be able to query the media provider right away.
- // 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 crsr = getContentResolver().query(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
- new String [] {"_id"},
- "_id=" + mPlayList[mPlayPos],
- null,
- null);
- // MusicUtils.query(this,
- // MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
- // new String [] {"_id"}, "_id=" + mPlayList[mPlayPos] , null, null);
- if (crsr == null || crsr.getCount() == 0) {
- // wait a bit and try again
- SystemClock.sleep(3000);
- crsr = getContentResolver().query(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
- mCursorCols, "_id=" + mPlayList[mPlayPos] , null, null);
- }
- if (crsr != null) {
- crsr.close();
- }
- // Make sure we don't auto-skip to the next song, since that
- // also starts playback. What could happen in that case is:
- // - music is paused
- // - go to UMS and delete some files, including the currently playing one
- // - come back from UMS
- // (time passes)
- // - music app is killed for some reason (out of memory)
- // - music service is restarted, service restores state, doesn't find
- // the "current" file, goes to the next and: playback starts on its
- // own, potentially at some random inconvenient time.
- mOpenFailedCounter = 20;
- mQuietMode = true;
- openCurrent();
- mQuietMode = false;
-
- if (!mPlayer.isInitialized()) {
- // couldn't restore the saved state
- mPlayListLen = 0;
- return;
- }
-
- long seekpos = mPreferences.getLong("seekpos", 0);
- seek(seekpos >= 0 && seekpos < duration() ? seekpos : 0);
-
- int repmode = mPreferences.getInt("repeatmode", Constants.REPEAT_NONE);
- if (repmode != Constants.REPEAT_ALL && repmode != Constants.REPEAT_CURRENT) {
- repmode = Constants.REPEAT_NONE;
- }
- mRepeatMode = repmode;
- // we passed this to the beginning of the method
- // mPlaylistId = mPreferences.getInt(
- // Constants.prefkey_mPlaylistId,
- // Constants.PLAYLIST_ALL);
-
- int rockonrepmode = mPreferences.getInt("rockonrepeatmode", Constants.REPEAT_NONE);
- if (rockonrepmode != Constants.REPEAT_ALL && rockonrepmode != Constants.REPEAT_CURRENT) {
- rockonrepmode = Constants.REPEAT_NONE;
- }
- mRockOnRepeatMode = rockonrepmode;
-
- int shufmode = mPreferences.getInt("shufflemode", Constants.SHUFFLE_NONE);
- if (shufmode != Constants.SHUFFLE_AUTO && shufmode != Constants.SHUFFLE_NORMAL) {
- shufmode = Constants.SHUFFLE_NONE;
- }
- if (shufmode != Constants.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 == Constants.SHUFFLE_AUTO) {
- shufmode = Constants.SHUFFLE_NORMAL;
- // if (! makeAutoShuffleList()) {
- // shufmode = Constants.SHUFFLE_NONE;
- // }
- }
- mShuffleMode = shufmode;
-
- int rockonshufmode = mPreferences.getInt("rockonshufflemode", Constants.SHUFFLE_NONE);
- if (rockonshufmode != Constants.SHUFFLE_AUTO && rockonshufmode != Constants.SHUFFLE_NORMAL) {
- rockonshufmode = Constants.SHUFFLE_NONE;
- }
- if (rockonshufmode != Constants.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 (rockonshufmode == Constants.SHUFFLE_AUTO) {
- rockonshufmode = Constants.SHUFFLE_NORMAL;
- // if (! makeAutoShuffleList()) {
- // rockonshufmode = Constants.SHUFFLE_NONE;
- // }
- }
- mRockOnShuffleMode = rockonshufmode;
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- mDelayedStopHandler.removeCallbacksAndMessages(null);
- mServiceInUse = true;
- return mBinder;
- }
- @Override
- public void onRebind(Intent intent) {
- mDelayedStopHandler.removeCallbacksAndMessages(null);
- mServiceInUse = true;
- }
-
- @Override
- public void onStart(Intent intent, int startId){
- this.onStartCommand(intent, 0, startId);
- }
-
- public int onStartCommand(Intent intent, int flags, int startId) {
- mServiceStartId = startId;
- mDelayedStopHandler.removeCallbacksAndMessages(null);
- Log.i(TAG, "Receiving intent in service");
-
- if (intent != null) {
- String action = intent.getAction();
- String cmd = intent.getStringExtra(Constants.CMDNAME);
- Log.i(TAG, "Receiving start command: "+cmd);
-
- if (Constants.CMDNEXT.equals(cmd) || Constants.NEXT_ACTION.equals(action)) {
- next(true);
- } else if (Constants.CMDPREVIOUS.equals(cmd) || Constants.PREVIOUS_ACTION.equals(action)) {
- prev();
- } else if (Constants.CMDTOGGLEPAUSE.equals(cmd) || Constants.TOGGLEPAUSE_ACTION.equals(action)) {
- if (isPlaying()) {
- pause();
- } else {
- play();
- }
- } else if (Constants.CMDPAUSE.equals(cmd) || Constants.PAUSE_ACTION.equals(action)) {
- pause();
- } else if (Constants.CMDSTOP.equals(cmd)) {
- pause();
- seek(0);
- } else if(Constants.SHUFFLE_NORMAL_ACTION.equals(action)){
- this.setShuffleMode(Constants.SHUFFLE_NORMAL);
- } else if(Constants.SHUFFLE_NONE_ACTION.equals(action)){
- this.setShuffleMode(Constants.SHUFFLE_NONE);
- } else if(Constants.REPEAT_CURRENT_ACTION.equals(action)){
- this.setRepeatMode(Constants.REPEAT_CURRENT);
- } else if(Constants.REPEAT_NONE_ACTION.equals(action)){
- this.setRepeatMode(Constants.REPEAT_NONE);
- }
- }
-
- // make sure the service will shut down on its own if it was
- // just started but not bound to and nothing is playing
- mDelayedStopHandler.removeCallbacksAndMessages(null);
- Message msg = mDelayedStopHandler.obtainMessage();
- mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
-
- return Constants.START_STICKY;
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- mServiceInUse = false;
- // Take a snapshot of the current playlist
- saveQueue(true);
- if (isPlaying() || mResumeAfterCall) {
- // something is currently playing, or will be playing once
- // an in-progress call ends, so don't stop the service now.
- return true;
- }
-
- // If there is a playlist but playback is paused, then wait a while
- // before stopping the service, so that pause/resume isn't slow.
- // Also delay stopping the service if we're transitioning between tracks.
- if (mPlayListLen > 0 || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
- Message msg = mDelayedStopHandler.obtainMessage();
- mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
- return true;
- }
-
- // No active playlist, OK to stop the service right now
- setForeground(false);
- stopSelf(mServiceStartId);
- return true;
- }
-
- private Handler mDelayedStopHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // Check again to make sure nothing is playing right now
- if (isPlaying() || mResumeAfterCall || mServiceInUse
- || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
- return;
- }
- // save the queue again, because it might have changed
- // since the user exited the music app (because of
- // party-shuffle or because the play-position changed)
- saveQueue(true);
- setForeground(false);
- stopSelf(mServiceStartId);
- }
- };
-
- /**
- * Called when we receive a ACTION_MEDIA_EJECT notification.
- *
- * @param storagePath path to mount point for the removed media
- */
- public void closeExternalStorageFiles(String storagePath) {
- // stop playback and clean up if the SD card is going to be unmounted.
- stop(true);
- notifyChange(Constants.QUEUE_CHANGED);
- notifyChange(Constants.META_CHANGED);
- }
- /**
- * Registers an intent to listen for ACTION_MEDIA_EJECT notifications.
- * The intent will call closeExternalStorageFiles() if the external media
- * is going to be ejected, so applications can clean up any files they have open.
- */
- public void registerExternalStorageListener() {
- if (mUnmountReceiver == null) {
- mUnmountReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
- saveQueue(true);
- mOneShot = true; // This makes us not save the state again later,
- // which would be wrong because the song ids and
- // card id might not match.
- closeExternalStorageFiles(intent.getData().getPath());
- } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
- mMediaMountedCount++;
- mCardId = 0;
- // mCardId = FileUtils.getFatVolumeId(intent.getData().getPath());
- reloadQueue();
- notifyChange(Constants.QUEUE_CHANGED);
- notifyChange(Constants.META_CHANGED);
- }
- }
- };
- IntentFilter iFilter = new IntentFilter();
- iFilter.addAction(Intent.ACTION_MEDIA_EJECT);
- iFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
- iFilter.addDataScheme("file");
- registerReceiver(mUnmountReceiver, iFilter);
- }
- }
- /**
- * Notify the change-receivers that something has changed.
- * The intent that is sent contains the following data
- * for the currently playing track:
- * "id" - Integer: the database row ID
- * "artist" - String: the name of the artist
- * "album" - String: the name of the album
- * "track" - String: the name of the track
- * The intent has an action that is one of
- * "com.android.music.metachanged"
- * "com.android.music.queuechanged",
- * "com.android.music.playbackcomplete"
- * "com.android.music.playstatechanged"
- * respectively indicating that a new track has
- * started playing, that the playback queue has
- * changed, that playback has stopped because
- * the last file in the list has been played,
- * or that the play-state changed (paused/resumed).
- */
- private void notifyChange(String what) {
-
- Intent i = new Intent(what);
- i.putExtra("id", Long.valueOf(getAudioId()));
- i.putExtra("artist", getArtistName());
- i.putExtra("album",getAlbumName());
- i.putExtra("track", getTrackName());
- sendBroadcast(i);
-
- if (what.equals(Constants.QUEUE_CHANGED)) {
- saveQueue(true);
- } else {
- saveQueue(false);
- }
-
- // Share this notification directly with our widgets
- if(mAppWidgetProvider != null)
- mAppWidgetProvider.notifyChange(this, what);
- if(mAppWidgetProvider3x3 != null)
- mAppWidgetProvider3x3.notifyChange(this, what);
- if(mAppWidgetProvider4x4 != null)
- mAppWidgetProvider4x4.notifyChange(this, what);
- if(mAppWidgetProvider4x1 != null)
- mAppWidgetProvider4x1.notifyChange(this, what);
-
- }
-
- private void sendScrobbleBroadcast(int state) {
- // check that state is a valid state
- if (state != Constants.SCROBBLE_PLAYSTATE_START &&
- state != Constants.SCROBBLE_PLAYSTATE_RESUME &&
- state != Constants.SCROBBLE_PLAYSTATE_PAUSE &&
- state != Constants.SCROBBLE_PLAYSTATE_COMPLETE) {
- Log.e(TAG, "Trying to send scrobble with invalid state: " + state);
- return;
- }
-
- // String who = PreferenceManager.getDefaultSharedPreferences(this).getString(
- // getString(R.string.preference_scrobble_list_key),
- // getString(R.string.preference_scrobble_value_dont));
-
- sendGTalkStatusBroadcast(state);
- // check if scrobbling is enabled, and to whom we should send the broadcast
- if (mScrobblerName.equals(getString(R.string.preference_scrobble_value_sls)))
- {
- sendScrobbleBroadcastSLS(state);
- }
- else if (mScrobblerName.equals(getString(R.string.preference_scrobble_value_sd)))
- {
- sendScrobbleBroadcastSD(state);
- }
- }
-
- private void sendGTalkStatusBroadcast(int state) {
- Log.d(TAG, "Sending scrobble broadcast to GTalkStatus");
- Intent i = new Intent("com.gtalkstatus.android.updaterintent");
-
- i.putExtra("app-name", "RockOn NextGen"); // TODO: what is the name of this app?
- i.putExtra("app-package", "org.abrantix.rockon.rockonnggl");
-
- String playstate = "";
- switch(state)
- {
- case Constants.SCROBBLE_PLAYSTATE_START:
- case Constants.SCROBBLE_PLAYSTATE_RESUME:
- playstate = "is_playing";
- break;
- default:
- playstate = "not_playing";
- }
-
- i.putExtra("state", playstate);
- i.putExtra("artist", getArtistName());
- i.putExtra("album",getAlbumName());
- i.putExtra("track", getTrackName());
- i.putExtra("duration", (int)(duration()/1000));
-
- sendBroadcast(i);
- }
-
- private void sendScrobbleBroadcastSLS(int state) {
- Log.d(TAG, "Sending scrobble broadcast to SLS");
- Intent i = new Intent(Constants.SCROBBLE_SLS_API);
-
- i.putExtra("app-name", "RockOn NextGen"); // TODO: what is the name of this app?
- i.putExtra("app-package", "org.abrantix.rockon.rockonnggl");
-
- i.putExtra("state", state);
-
- i.putExtra("artist", getArtistName());
- i.putExtra("album",getAlbumName());
- i.putExtra("track", getTrackName());
- i.putExtra("duration", (int)(duration()/1000));
-
- sendBroadcast(i);
- }
-
- private void sendScrobbleBroadcastSD(int state) {
- Log.d(TAG, "Sending scrobble broadcast to SD");
- Intent i = new Intent(Constants.SCROBBLE_SD_API);
-
- boolean playing = false;
- if (state == Constants.SCROBBLE_PLAYSTATE_START ||
- state == Constants.SCROBBLE_PLAYSTATE_RESUME) {
- playing = true;
- }
- i.putExtra("playing", playing);
-
- i.putExtra("id", (int)getAudioId());
-
- sendBroadcast(i);
- }
- private void ensurePlayListCapacity(int size) {
- if (mPlayList == null || size > mPlayList.length) {
- // reallocate at 2x requested size so we don't
- // need to grow and copy the array for every
- // insert
- long [] newlist = new long[size * 2];
- int len = mPlayList != null ? mPlayList.length : mPlayListLen;
- for (int i = 0; i < len; i++) {
- newlist[i] = mPlayList[i];
- }
- mPlayList = newlist;
- }
- // FIXME: shrink the array when the needed size is much smaller
- // than the allocated size
- }
-
- // // add to the current point of the playing list
- // private void insertToPlayList(long[] list, int position){
- // int addlen = list.length;
- // }
-
- // insert the list of songs at the specified position in the playlist
- private void addToPlayList(long [] list, int position)
- {
- // Log.i(TAG, "===============================");
- // Log.i(TAG, "list.length: "+list.length+" position: "+position);
- // Log.i(TAG, "mPlaylistLen: "+mPlayListLen+" mPlaylist.length: "+mPlayList.length);
- int addlen = list.length;
- if (position < 0) { // overwrite
- mPlayListLen = 0;
- position = 0;
- }
- ensurePlayListCapacity(mPlayListLen + addlen);
- if (position > mPlayListLen) {
- position = mPlayListLen;
- }
-
- // move part of list after insertion point
- int tailsize = mPlayListLen - position;
- for (int i = tailsize ; i > 0 ; i--) {
- mPlayList[position + i + addlen] = mPlayList[position + i];
- // TODO: need to update history??????????
- }
-
- // copy list into playlist
- for (int i = 0; i < addlen; i++) {
- mPlayList[position + i] = list[i];
- }
- mPlayListLen += addlen;
- }
-
- /**
- * Appends a list of tracks to the current playlist.
- * If nothing is playing currently, playback will be started at
- * the first track.
- * If the action is NOW, playback will switch to the first of
- * the new tracks immediately.
- * @param list The list of tracks to append.
- * @param action NOW, NEXT or LAST
- */
- public void enqueue(long [] list, int action) {
- synchronized(this) {
- Log.i(TAG, "yep, action: "+action);
- if (action == Constants.NEXT && mPlayPos + 1 < mPlayListLen) {
- addToPlayList(list, mPlayPos + 1);
- notifyChange(Constants.QUEUE_CHANGED);
- } else {
- // action == LAST || action == NOW || mPlayPos + 1 == mPlayListLen
- if(action == Constants.NOW)
- addToPlayList(list, mPlayPos + 1);
- else if(action == Constants.LAST)
- addToPlayList(list, Integer.MAX_VALUE);
- notifyChange(Constants.QUEUE_CHANGED);
- if (action == Constants.NOW) {
- // mPlayPos = mPlayListLen - list.length;
- Log.i(TAG, "yep, opening: "+mPlayPos);
- mPlayPos = mPlayPos + 1;
- openCurrent();
- play();
- notifyChange(Constants.META_CHANGED);
- return;
- }
- }
- if (mPlayPos < 0) {
- mPlayPos = 0;
- openCurrent();
- play();
- notifyChange(Constants.META_CHANGED);
- }
- }
- }
- /**
- * Replaces the current playlist with a new list,
- * and prepares for starting playback at the specified
- * position in the list, or a random position if the
- * specified position is 0.
- * @param list The new list of tracks.
- */
- public void open(long [] list, int position) {
- synchronized (this) {
- if (mShuffleMode == Constants.SHUFFLE_AUTO) {
- mShuffleMode = Constants.SHUFFLE_NORMAL;
- }
- long oldId = getAudioId();
- int listlength = list.length;
- boolean newlist = true;
- if (mPlayListLen == listlength) {
- // possible fast path: list might be the same
- newlist = false;
- for (int i = 0; i < listlength; i++) {
- if (list[i] != mPlayList[i]) {
- newlist = true;
- break;
- }
- }
- }
- if (newlist) {
- addToPlayList(list, -1);
- notifyChange(Constants.QUEUE_CHANGED);
- }
- int oldpos = mPlayPos;
- if (position >= 0) {
- mPlayPos = position;
- } else {
- mPlayPos = mRand.nextInt(mPlayListLen);
- }
- mHistory.clear();
- saveBookmarkIfNeeded();
- openCurrent();
- if (oldId != getAudioId()) {
- notifyChange(Constants.META_CHANGED);
- }
- }
- }
-
- /**
- * Moves the item at index1 to index2.
- * @param index1
- * @param index2
- */
- public void moveQueueItem(int index1, int index2) {
- synchronized (this) {
- if (index1 >= mPlayListLen) {
- index1 = mPlayListLen - 1;
- }
- if (index2 >= mPlayListLen) {
- index2 = mPlayListLen - 1;
- }
- if (index1 < index2) {
- long tmp = mPlayList[index1];
- for (int i = index1; i < index2; i++) {
- mPlayList[i] = mPlayList[i+1];
- }
- mPlayList[index2] = tmp;
- if (mPlayPos == index1) {
- mPlayPos = index2;
- } else if (mPlayPos >= index1 && mPlayPos <= index2) {
- mPlayPos--;
- }
- } else if (index2 < index1) {
- long tmp = mPlayList[index1];
- for (int i = index1; i > index2; i--) {
- mPlayList[i] = mPlayList[i-1];
- }
- mPlayList[index2] = tmp;
- if (mPlayPos == index1) {
- mPlayPos = index2;
- } else if (mPlayPos >= index2 && mPlayPos <= index1) {
- mPlayPos++;
- }
- }
- notifyChange(Constants.QUEUE_CHANGED);
- }
- }
- /**
- * Returns the current play list
- * @return An array of integers containing the IDs of the tracks in the play list
- */
- public long [] getQueue() {
- synchronized (this) {
- int len = mPlayListLen;
- long [] list = new long[len];
- for (int i = 0; i < len; i++) {
- list[i] = mPlayList[i];
- }
- return list;
- }
- }
-
- public long [] getOutstandingQueue(){
- synchronized (this) {
- long [] list;
- if(mPlayListLen <= 0){
- list = null;
- // Log.i(TAG, "list.length: "+list.length+" mPlayPos: "+mPlayPos+" mPlayListLen: "+mPlayListLen);
- } else if(mShuffleMode == Constants.SHUFFLE_NONE){
- list = new long[mPlayListLen - mPlayPos + 1];
- for(int i = mPlayPos; i<mPlayListLen; i++){
- list[i-mPlayPos] = mPlayList[i];
- }
- } else {
- // check which elements on the playlist have not been played yet
- int numUnplayed = mPlayListLen;
- long[] tmplist = new long[mPlayListLen];
- for(int i=0; i<mPlayListLen; i++){
- tmplist[i] = mPlayList[i];
- if(mPlayList[i]<0)
- numUnplayed--;
- }
- for(int i=0; i<mHistory.size(); i++){
- if(mHistory.get(i).intValue() < tmplist.length)
- {
- if(tmplist[mHistory.get(i).intValue()] >= 0){
- // always list the currently playing song
- if(mHistory.get(i).intValue() != mPlayPos){
- tmplist[mHistory.get(i).intValue()] = -1;
- numUnplayed--;
- }
- }
- }
- }
- // pass the unplayed item to our result list
- list = new long[numUnplayed];
- // put the currently playing song on top
- list[0] = mPlayList[mPlayPos];
- int cnt = 1;
- for(int i=0; i<mPlayListLen; i++){
- if(tmplist[i]>=0 && i != mPlayPos){ // currently playing song already added
- Log.i(TAG, "numUnPlayed: "+numUnplayed + "cnt: "+cnt);
- list[cnt] = tmplist[i];
- cnt++;
- }
- }
- }
- return list;
- }
- }
- private void openCurrent() {
- synchronized (this) {
- if (mCursor != null) {
- mCursor.close();
- mCursor = null;
- }
- if (mPlayListLen == 0) {
- return;
- }
- stop(false);
- String id = String.valueOf(mPlayList[mPlayPos]);
-
- mCursor = getContentResolver().query(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
- mCursorCols, "_id=" + id , null, null);
- if (mCursor != null) {
- mCursor.moveToFirst();
- open(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id, false);
- // go to bookmark if needed
- // if (isPodcast()) {
- // long bookmark = getBookmark();
- // // Start playing a little bit before the bookmark,
- // // so it's easier to get back in to the narrative.
- // seek(bookmark - 5000);
- // }
- }
- }
- }
- public void openAsync(String path) {
- synchronized (this) {
- if (path == null) {
- return;
- }
-
- mRepeatMode = Constants.REPEAT_NONE;
- ensurePlayListCapacity(1);
- mPlayListLen = 1;
- mPlayPos = -1;
-
- mFileToPlay = path;
- mCursor = null;
- mPlayer.setDataSourceAsync(mFileToPlay);
- mOneShot = true;
- }
- }
-
- /**
- * Opens the specified file and readies it for playback.
- *
- * @param path The full path of the file to be opened.
- * @param oneshot when set to true, playback will stop after this file completes, instead
- * of moving on to the next track in the list
- */
- public void open(String path, boolean oneshot) {
- synchronized (this) {
- if (path == null) {
- return;
- }
-
- if (oneshot) {
- mRepeatMode = Constants.REPEAT_NONE;
- ensurePlayListCapacity(1);
- mPlayListLen = 1;
- mPlayPos = -1;
- }
-
- // if mCursor is null, try to associate path with a database cursor
- if (mCursor == null) {
- ContentResolver resolver = getContentResolver();
- Uri uri;
- String where;
- String selectionArgs[];
- if (path.startsWith("content://media/")) {
- uri = Uri.parse(path);
- where = null;
- selectionArgs = null;
- } else {
- uri = MediaStore.Audio.Media.getContentUriForPath(path);
- where = MediaStore.Audio.Media.DATA + "=?";
- selectionArgs = new String[] { path };
- }
-
- try {
- mCursor = resolver.query(uri, mCursorCols, where, selectionArgs, null);
- if (mCursor != null) {
- if (mCursor.getCount() == 0) {
- mCursor.close();
- mCursor = null;
- } else {
- mCursor.moveToNext();
- ensurePlayListCapacity(1);
- mPlayListLen = 1;
- mPlayList[0] = mCursor.getLong(IDCOLIDX);
- mPlayPos = 0;
- }
- }
- } catch (UnsupportedOperationException ex) {
- }
- }
- mFileToPlay = path;
- mPlayer.setDataSource(mFileToPlay);
- mOneShot = oneshot;
- if (! mPlayer.isInitialized()) {
- stop(true);
- if (mOpenFailedCounter++ < 10 && mPlayListLen > 1) {
- // beware: this ends up being recursive because next() calls open() again.
- next(false);
- }
- if (! mPlayer.isInitialized() && mOpenFailedCounter != 0) {
- // need to make sure we only shows this once
- mOpenFailedCounter = 0;
- if (!mQuietMode) {
- // Toast.makeText(this, R.string.playback_failed, Toast.LENGTH_SHORT).show();
- }
- }
- } else {
- …
Large files files are truncated, but you can click here to view the full file