PageRenderTime 85ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/src/org/abrantix/rockon/rockonnggl/RockOnNextGenService.java

https://github.com/wmbest2/rockonnggl
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

  1. /*
  2. * Adapted from the Android stock Music app
  3. */
  4. package org.abrantix.rockon.rockonnggl;
  5. import android.app.Notification;
  6. import android.app.NotificationManager;
  7. import android.app.PendingIntent;
  8. import android.app.Service;
  9. import android.appwidget.AppWidgetManager;
  10. import android.content.ContentResolver;
  11. import android.content.ContentUris;
  12. import android.content.ContentValues;
  13. import android.content.Context;
  14. import android.content.Intent;
  15. import android.content.IntentFilter;
  16. import android.content.BroadcastReceiver;
  17. import android.content.SharedPreferences;
  18. import android.content.SharedPreferences.Editor;
  19. import android.database.Cursor;
  20. import android.database.sqlite.SQLiteException;
  21. import android.media.AudioManager;
  22. import android.media.MediaPlayer;
  23. import android.net.Uri;
  24. import android.os.Environment;
  25. import android.os.Handler;
  26. import android.os.IBinder;
  27. import android.os.Message;
  28. import android.os.PowerManager;
  29. import android.os.RemoteException;
  30. import android.os.SystemClock;
  31. import android.os.PowerManager.WakeLock;
  32. import android.preference.PreferenceManager;
  33. import android.provider.MediaStore;
  34. import android.telephony.PhoneStateListener;
  35. import android.telephony.TelephonyManager;
  36. import android.util.Log;
  37. import android.widget.RemoteViews;
  38. import android.widget.Toast;
  39. import java.io.File;
  40. import java.io.IOException;
  41. import java.lang.ref.WeakReference;
  42. import java.util.Random;
  43. import java.util.Vector;
  44. import java.util.jar.Pack200.Unpacker;
  45. import org.abrantix.rockon.rockonnggl.IRockOnNextGenService;
  46. import org.abrantix.rockon.rockonnggl.R;
  47. /**
  48. * Provides "background" audio playback capabilities, allowing the
  49. * user to switch between activities without stopping playback.
  50. */
  51. public class RockOnNextGenService extends Service {
  52. private static final String TAG = "RockOnNextGenService";
  53. public static final int PLAYBACKSERVICE_STATUS = 1;
  54. private static final int TRACK_ENDED = 1;
  55. private static final int RELEASE_WAKELOCK = 2;
  56. private static final int SERVER_DIED = 3;
  57. private static final int FADEIN = 4;
  58. private static final int MAX_HISTORY_SIZE = 100;
  59. private static boolean mLock = false;
  60. private static String mScrobblerName = "none";
  61. private int mRockOnShuffleMode = Constants.SHUFFLE_NONE;
  62. private int mRockOnRepeatMode = Constants.REPEAT_NONE;
  63. private int mPlaylistId = Constants.PLAYLIST_UNKNOWN;
  64. private MultiPlayer mPlayer;
  65. private String mFileToPlay;
  66. private int mShuffleMode = Constants.SHUFFLE_NONE;
  67. private int mRepeatMode = Constants.REPEAT_NONE;
  68. private int mMediaMountedCount = 0;
  69. private long [] mAutoShuffleList = null;
  70. private boolean mOneShot; //??
  71. private long [] mPlayList = null;
  72. private int mPlayListLen = 0;
  73. private Vector<Integer> mHistory = new Vector<Integer>(MAX_HISTORY_SIZE);
  74. private Cursor mCursor;
  75. private int mPlayPos = -1;
  76. private final Shuffler mRand = new Shuffler();
  77. private int mOpenFailedCounter = 0;
  78. String[] mCursorCols = new String[] {
  79. "audio._id AS _id", // index must match IDCOLIDX below
  80. MediaStore.Audio.Media.ARTIST,
  81. MediaStore.Audio.Media.ALBUM,
  82. MediaStore.Audio.Media.TITLE,
  83. MediaStore.Audio.Media.DATA,
  84. MediaStore.Audio.Media.MIME_TYPE,
  85. MediaStore.Audio.Media.ALBUM_ID,
  86. MediaStore.Audio.Media.ARTIST_ID
  87. };
  88. private final static int IDCOLIDX = 0;
  89. private BroadcastReceiver mUnmountReceiver = null;
  90. private WakeLock mWakeLock;
  91. private int mServiceStartId = -1;
  92. private boolean mServiceInUse = false;
  93. private boolean mResumeAfterCall = false;
  94. private boolean mIsSupposedToBePlaying = false;
  95. private boolean mQuietMode = false;
  96. private SharedPreferences mPreferences;
  97. // We use this to distinguish between different cards when saving/restoring playlists.
  98. // This will have to change if we want to support multiple simultaneous cards.
  99. private int mCardId;
  100. private RockOnNextGenAppWidgetProvider mAppWidgetProvider =
  101. RockOnNextGenAppWidgetProvider.getInstance();
  102. private RockOnNextGenAppWidgetProvider3x3 mAppWidgetProvider3x3 =
  103. RockOnNextGenAppWidgetProvider3x3.getInstance();
  104. private RockOnNextGenAppWidgetProvider4x4 mAppWidgetProvider4x4 =
  105. RockOnNextGenAppWidgetProvider4x4.getInstance();
  106. private RockOnNextGenAppWidgetProvider4x1 mAppWidgetProvider4x1 =
  107. RockOnNextGenAppWidgetProvider4x1.getInstance();
  108. private ScreenOnIntentReceiver mScreenOnReceiver;
  109. // interval after which we stop the service when idle
  110. private static final int IDLE_DELAY = 60000;
  111. private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
  112. @Override
  113. public void onCallStateChanged(int state, String incomingNumber) {
  114. if (state == TelephonyManager.CALL_STATE_RINGING) {
  115. AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  116. int ringvolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
  117. if (ringvolume > 0) {
  118. mResumeAfterCall = (isPlaying() || mResumeAfterCall) && (getAudioId() >= 0);
  119. pause();
  120. }
  121. } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
  122. // pause the music while a conversation is in progress
  123. mResumeAfterCall = (isPlaying() || mResumeAfterCall) && (getAudioId() >= 0);
  124. pause();
  125. } else if (state == TelephonyManager.CALL_STATE_IDLE) {
  126. // start playing again
  127. if (mResumeAfterCall) {
  128. // resume playback only if music was playing
  129. // when the call was answered
  130. startAndFadeIn();
  131. mResumeAfterCall = false;
  132. }
  133. }
  134. }
  135. };
  136. private void startAndFadeIn() {
  137. mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
  138. }
  139. private Handler mMediaplayerHandler = new Handler() {
  140. float mCurrentVolume = 1.0f;
  141. @Override
  142. public void handleMessage(Message msg) {
  143. switch (msg.what) {
  144. case FADEIN:
  145. if (!isPlaying()) {
  146. mCurrentVolume = 0f;
  147. mPlayer.setVolume(mCurrentVolume);
  148. play();
  149. mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
  150. } else {
  151. mCurrentVolume += 0.01f;
  152. if (mCurrentVolume < 1.0f) {
  153. mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
  154. } else {
  155. mCurrentVolume = 1.0f;
  156. }
  157. mPlayer.setVolume(mCurrentVolume);
  158. }
  159. break;
  160. case SERVER_DIED:
  161. if (mIsSupposedToBePlaying) {
  162. next(true);
  163. } else {
  164. // the server died when we were idle, so just
  165. // reopen the same song (it will start again
  166. // from the beginning though when the user
  167. // restarts)
  168. openCurrent();
  169. }
  170. break;
  171. case TRACK_ENDED:
  172. if (mRepeatMode == Constants.REPEAT_CURRENT) {
  173. seek(0);
  174. play();
  175. } else if (!mOneShot) {
  176. next(false);
  177. } else {
  178. notifyChange(Constants.PLAYBACK_COMPLETE);
  179. mIsSupposedToBePlaying = false;
  180. }
  181. break;
  182. case RELEASE_WAKELOCK:
  183. mWakeLock.release();
  184. break;
  185. default:
  186. break;
  187. }
  188. }
  189. };
  190. private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
  191. @Override
  192. public void onReceive(Context context, Intent intent) {
  193. String action = intent.getAction();
  194. String cmd = intent.getStringExtra("command");
  195. Log.i(TAG, cmd);
  196. if (Constants.CMDNEXT.equals(cmd) || Constants.NEXT_ACTION.equals(action)) {
  197. next(true);
  198. } else if (Constants.CMDPREVIOUS.equals(cmd) || Constants.PREVIOUS_ACTION.equals(action)) {
  199. prev();
  200. } else if (Constants.CMDTOGGLEPAUSE.equals(cmd) || Constants.TOGGLEPAUSE_ACTION.equals(action)) {
  201. if (isPlaying()) {
  202. pause();
  203. } else {
  204. play();
  205. }
  206. } else if (Constants.CMDPAUSE.equals(cmd) || Constants.PAUSE_ACTION.equals(action)) {
  207. pause();
  208. } else if (Constants.CMDSTOP.equals(cmd)) {
  209. pause();
  210. seek(0);
  211. }
  212. else if (RockOnNextGenAppWidgetProvider.CMDAPPWIDGETUPDATE.equals(cmd)) {
  213. // Someone asked us to refresh a set of specific widgets, probably
  214. // because they were just added.
  215. int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
  216. if(mAppWidgetProvider != null)
  217. mAppWidgetProvider.performUpdate(RockOnNextGenService.this, appWidgetIds);
  218. if(mAppWidgetProvider3x3 != null)
  219. mAppWidgetProvider3x3.performUpdate(RockOnNextGenService.this, appWidgetIds);
  220. if(mAppWidgetProvider4x4 != null)
  221. mAppWidgetProvider4x4.performUpdate(RockOnNextGenService.this, appWidgetIds);
  222. if(mAppWidgetProvider4x1 != null)
  223. mAppWidgetProvider4x1.performUpdate(RockOnNextGenService.this, appWidgetIds);
  224. }
  225. }
  226. };
  227. public RockOnNextGenService() {
  228. }
  229. @Override
  230. public void onCreate() {
  231. super.onCreate();
  232. Log.i(TAG, "SERVICE onCreate");
  233. // mPreferences = getSharedPreferences("Music", MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
  234. mPreferences = getSharedPreferences("CubedMusic", MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
  235. //PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  236. mCardId = 0; // should be the serial number of the sd card
  237. // mCardId = FileUtils.getFatVolumeId(Environment.getExternalStorageDirectory().getPath());
  238. registerExternalStorageListener();
  239. registerScreenOnReceiver();
  240. // Needs to be done in this thread, since otherwise ApplicationContext.getPowerManager() crashes.
  241. mPlayer = new MultiPlayer();
  242. mPlayer.setHandler(mMediaplayerHandler);
  243. reloadQueue();
  244. IntentFilter commandFilter = new IntentFilter();
  245. commandFilter.addAction(Constants.SERVICECMD);
  246. commandFilter.addAction(Constants.TOGGLEPAUSE_ACTION);
  247. commandFilter.addAction(Constants.PAUSE_ACTION);
  248. commandFilter.addAction(Constants.NEXT_ACTION);
  249. commandFilter.addAction(Constants.PREVIOUS_ACTION);
  250. // commandFilter.addAction(Constants.SHUFFLE_AUTO_ACTION);
  251. // commandFilter.addAction(Constants.SHUFFLE_NONE_ACTION);
  252. // commandFilter.addAction(Constants.REPEAT_CURRENT_ACTION);
  253. // commandFilter.addAction(Constants.REPEAT_NONE_ACTION);
  254. registerReceiver(mIntentReceiver, commandFilter);
  255. TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  256. tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
  257. PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
  258. mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
  259. mWakeLock.setReferenceCounted(false);
  260. // If the service was idle, but got killed before it stopped itself, the
  261. // system will relaunch it. Make sure it gets stopped again in that case.
  262. Message msg = mDelayedStopHandler.obtainMessage();
  263. mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
  264. }
  265. @Override
  266. public void onDestroy() {
  267. // Check that we're not being destroyed while something is still playing.
  268. if (isPlaying()) {
  269. Log.i(TAG, "Service being destroyed while still playing.");
  270. }
  271. // release all MediaPlayer resources, including the native player and wakelocks
  272. mPlayer.release();
  273. mPlayer = null;
  274. // make sure there aren't any other messages coming
  275. mDelayedStopHandler.removeCallbacksAndMessages(null);
  276. mMediaplayerHandler.removeCallbacksAndMessages(null);
  277. if (mCursor != null) {
  278. mCursor.close();
  279. mCursor = null;
  280. }
  281. TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  282. tmgr.listen(mPhoneStateListener, 0);
  283. unregisterReceiver(mIntentReceiver);
  284. if (mUnmountReceiver != null) {
  285. unregisterReceiver(mUnmountReceiver);
  286. mUnmountReceiver = null;
  287. }
  288. unregisterScreenOnReceiver();
  289. mWakeLock.release();
  290. super.onDestroy();
  291. }
  292. private final char hexdigits [] = new char [] {
  293. '0', '1', '2', '3',
  294. '4', '5', '6', '7',
  295. '8', '9', 'a', 'b',
  296. 'c', 'd', 'e', 'f'
  297. };
  298. private void saveQueue(boolean full) {
  299. if (mOneShot) {
  300. return;
  301. }
  302. Editor ed = mPreferences.edit();
  303. //long start = System.currentTimeMillis();
  304. if(mPlayList != null && mPlayList.length > 0)
  305. ed.putLong("audioId", mPlayList[0]);
  306. if (full) {
  307. StringBuilder q = new StringBuilder();
  308. // The current playlist is saved as a list of "reverse hexadecimal"
  309. // numbers, which we can generate faster than normal decimal or
  310. // hexadecimal numbers, which in turn allows us to save the playlist
  311. // more often without worrying too much about performance.
  312. // (saving the full state takes about 40 ms under no-load conditions
  313. // on the phone)
  314. int len = mPlayListLen;
  315. for (int i = 0; i < len; i++) {
  316. long n = mPlayList[i];
  317. if (n == 0) {
  318. q.append("0;");
  319. } else {
  320. while (n != 0) {
  321. int digit = (int)(n & 0xf);
  322. n >>= 4;
  323. q.append(hexdigits[digit]);
  324. }
  325. q.append(";");
  326. }
  327. }
  328. //Log.i("@@@@ service", "created queue string in " + (System.currentTimeMillis() - start) + " ms");
  329. ed.putString("queue", q.toString());
  330. ed.putInt("cardid", mCardId);
  331. if (mShuffleMode != Constants.SHUFFLE_NONE) {
  332. // In shuffle mode we need to save the history too
  333. len = mHistory.size();
  334. q.setLength(0);
  335. for (int i = 0; i < len; i++) {
  336. int n = mHistory.get(i);
  337. if (n == 0) {
  338. q.append("0;");
  339. } else {
  340. while (n != 0) {
  341. int digit = (n & 0xf);
  342. n >>= 4;
  343. q.append(hexdigits[digit]);
  344. }
  345. q.append(";");
  346. }
  347. }
  348. ed.putString("history", q.toString());
  349. }
  350. }
  351. ed.putInt("curpos", mPlayPos);
  352. if (mPlayer.isInitialized()) {
  353. ed.putLong("seekpos", mPlayer.position());
  354. }
  355. ed.putInt("repeatmode", mRepeatMode);
  356. ed.putInt("shufflemode", mShuffleMode);
  357. // ed.putInt(Constants.prefkey_mPlaylistId, mPlaylistId);
  358. ed.putInt("rockonrepeatmode", mRockOnRepeatMode);
  359. ed.putInt("rockonshufflemode", mRockOnShuffleMode);
  360. ed.commit();
  361. //Log.i("@@@@ service", "saved state in " + (System.currentTimeMillis() - start) + " ms");
  362. }
  363. private void reloadQueue() {
  364. String q = null;
  365. // the playlist should always be restored! or should it?
  366. mPlaylistId = mPreferences.getInt(
  367. Constants.prefkey_mPlaylistId,
  368. Constants.PLAYLIST_ALL);
  369. // sanity check
  370. if(mPlaylistId == Constants.PLAYLIST_UNKNOWN)
  371. mPlaylistId = Constants.PLAYLIST_ALL;
  372. boolean newstyle = false;
  373. int id = mCardId;
  374. if (mPreferences.contains("cardid")) {
  375. newstyle = true;
  376. id = mPreferences.getInt("cardid", ~mCardId);
  377. }
  378. if (id == mCardId) {
  379. // Only restore the saved playlist if the card is still
  380. // the same one as when the playlist was saved
  381. q = mPreferences.getString("queue", "");
  382. }
  383. int qlen = q != null ? q.length() : 0;
  384. if (qlen > 1) {
  385. //Log.i("@@@@ service", "loaded queue: " + q);
  386. int plen = 0;
  387. int n = 0;
  388. int shift = 0;
  389. for (int i = 0; i < qlen; i++) {
  390. char c = q.charAt(i);
  391. if (c == ';') {
  392. ensurePlayListCapacity(plen + 1);
  393. mPlayList[plen] = n;
  394. plen++;
  395. n = 0;
  396. shift = 0;
  397. } else {
  398. if (c >= '0' && c <= '9') {
  399. n += ((c - '0') << shift);
  400. } else if (c >= 'a' && c <= 'f') {
  401. n += ((10 + c - 'a') << shift);
  402. } else {
  403. // bogus playlist data
  404. plen = 0;
  405. break;
  406. }
  407. shift += 4;
  408. }
  409. }
  410. mPlayListLen = plen;
  411. int pos = mPreferences.getInt("curpos", 0);
  412. if (pos < 0 || pos >= mPlayListLen) {
  413. // The saved playlist is bogus, discard it
  414. mPlayListLen = 0;
  415. return;
  416. }
  417. mPlayPos = pos;
  418. // When reloadQueue is called in response to a card-insertion,
  419. // we might not be able to query the media provider right away.
  420. // To deal with this, try querying for the current file, and if
  421. // that fails, wait a while and try again. If that too fails,
  422. // assume there is a problem and don't restore the state.
  423. Cursor crsr = getContentResolver().query(
  424. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  425. new String [] {"_id"},
  426. "_id=" + mPlayList[mPlayPos],
  427. null,
  428. null);
  429. // MusicUtils.query(this,
  430. // MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  431. // new String [] {"_id"}, "_id=" + mPlayList[mPlayPos] , null, null);
  432. if (crsr == null || crsr.getCount() == 0) {
  433. // wait a bit and try again
  434. SystemClock.sleep(3000);
  435. crsr = getContentResolver().query(
  436. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  437. mCursorCols, "_id=" + mPlayList[mPlayPos] , null, null);
  438. }
  439. if (crsr != null) {
  440. crsr.close();
  441. }
  442. // Make sure we don't auto-skip to the next song, since that
  443. // also starts playback. What could happen in that case is:
  444. // - music is paused
  445. // - go to UMS and delete some files, including the currently playing one
  446. // - come back from UMS
  447. // (time passes)
  448. // - music app is killed for some reason (out of memory)
  449. // - music service is restarted, service restores state, doesn't find
  450. // the "current" file, goes to the next and: playback starts on its
  451. // own, potentially at some random inconvenient time.
  452. mOpenFailedCounter = 20;
  453. mQuietMode = true;
  454. openCurrent();
  455. mQuietMode = false;
  456. if (!mPlayer.isInitialized()) {
  457. // couldn't restore the saved state
  458. mPlayListLen = 0;
  459. return;
  460. }
  461. long seekpos = mPreferences.getLong("seekpos", 0);
  462. seek(seekpos >= 0 && seekpos < duration() ? seekpos : 0);
  463. int repmode = mPreferences.getInt("repeatmode", Constants.REPEAT_NONE);
  464. if (repmode != Constants.REPEAT_ALL && repmode != Constants.REPEAT_CURRENT) {
  465. repmode = Constants.REPEAT_NONE;
  466. }
  467. mRepeatMode = repmode;
  468. // we passed this to the beginning of the method
  469. // mPlaylistId = mPreferences.getInt(
  470. // Constants.prefkey_mPlaylistId,
  471. // Constants.PLAYLIST_ALL);
  472. int rockonrepmode = mPreferences.getInt("rockonrepeatmode", Constants.REPEAT_NONE);
  473. if (rockonrepmode != Constants.REPEAT_ALL && rockonrepmode != Constants.REPEAT_CURRENT) {
  474. rockonrepmode = Constants.REPEAT_NONE;
  475. }
  476. mRockOnRepeatMode = rockonrepmode;
  477. int shufmode = mPreferences.getInt("shufflemode", Constants.SHUFFLE_NONE);
  478. if (shufmode != Constants.SHUFFLE_AUTO && shufmode != Constants.SHUFFLE_NORMAL) {
  479. shufmode = Constants.SHUFFLE_NONE;
  480. }
  481. if (shufmode != Constants.SHUFFLE_NONE) {
  482. // in shuffle mode we need to restore the history too
  483. q = mPreferences.getString("history", "");
  484. qlen = q != null ? q.length() : 0;
  485. if (qlen > 1) {
  486. plen = 0;
  487. n = 0;
  488. shift = 0;
  489. mHistory.clear();
  490. for (int i = 0; i < qlen; i++) {
  491. char c = q.charAt(i);
  492. if (c == ';') {
  493. if (n >= mPlayListLen) {
  494. // bogus history data
  495. mHistory.clear();
  496. break;
  497. }
  498. mHistory.add(n);
  499. n = 0;
  500. shift = 0;
  501. } else {
  502. if (c >= '0' && c <= '9') {
  503. n += ((c - '0') << shift);
  504. } else if (c >= 'a' && c <= 'f') {
  505. n += ((10 + c - 'a') << shift);
  506. } else {
  507. // bogus history data
  508. mHistory.clear();
  509. break;
  510. }
  511. shift += 4;
  512. }
  513. }
  514. }
  515. }
  516. if (shufmode == Constants.SHUFFLE_AUTO) {
  517. shufmode = Constants.SHUFFLE_NORMAL;
  518. // if (! makeAutoShuffleList()) {
  519. // shufmode = Constants.SHUFFLE_NONE;
  520. // }
  521. }
  522. mShuffleMode = shufmode;
  523. int rockonshufmode = mPreferences.getInt("rockonshufflemode", Constants.SHUFFLE_NONE);
  524. if (rockonshufmode != Constants.SHUFFLE_AUTO && rockonshufmode != Constants.SHUFFLE_NORMAL) {
  525. rockonshufmode = Constants.SHUFFLE_NONE;
  526. }
  527. if (rockonshufmode != Constants.SHUFFLE_NONE) {
  528. // in shuffle mode we need to restore the history too
  529. q = mPreferences.getString("history", "");
  530. qlen = q != null ? q.length() : 0;
  531. if (qlen > 1) {
  532. plen = 0;
  533. n = 0;
  534. shift = 0;
  535. mHistory.clear();
  536. for (int i = 0; i < qlen; i++) {
  537. char c = q.charAt(i);
  538. if (c == ';') {
  539. if (n >= mPlayListLen) {
  540. // bogus history data
  541. mHistory.clear();
  542. break;
  543. }
  544. mHistory.add(n);
  545. n = 0;
  546. shift = 0;
  547. } else {
  548. if (c >= '0' && c <= '9') {
  549. n += ((c - '0') << shift);
  550. } else if (c >= 'a' && c <= 'f') {
  551. n += ((10 + c - 'a') << shift);
  552. } else {
  553. // bogus history data
  554. mHistory.clear();
  555. break;
  556. }
  557. shift += 4;
  558. }
  559. }
  560. }
  561. }
  562. if (rockonshufmode == Constants.SHUFFLE_AUTO) {
  563. rockonshufmode = Constants.SHUFFLE_NORMAL;
  564. // if (! makeAutoShuffleList()) {
  565. // rockonshufmode = Constants.SHUFFLE_NONE;
  566. // }
  567. }
  568. mRockOnShuffleMode = rockonshufmode;
  569. }
  570. }
  571. @Override
  572. public IBinder onBind(Intent intent) {
  573. mDelayedStopHandler.removeCallbacksAndMessages(null);
  574. mServiceInUse = true;
  575. return mBinder;
  576. }
  577. @Override
  578. public void onRebind(Intent intent) {
  579. mDelayedStopHandler.removeCallbacksAndMessages(null);
  580. mServiceInUse = true;
  581. }
  582. @Override
  583. public void onStart(Intent intent, int startId){
  584. this.onStartCommand(intent, 0, startId);
  585. }
  586. public int onStartCommand(Intent intent, int flags, int startId) {
  587. mServiceStartId = startId;
  588. mDelayedStopHandler.removeCallbacksAndMessages(null);
  589. Log.i(TAG, "Receiving intent in service");
  590. if (intent != null) {
  591. String action = intent.getAction();
  592. String cmd = intent.getStringExtra(Constants.CMDNAME);
  593. Log.i(TAG, "Receiving start command: "+cmd);
  594. if (Constants.CMDNEXT.equals(cmd) || Constants.NEXT_ACTION.equals(action)) {
  595. next(true);
  596. } else if (Constants.CMDPREVIOUS.equals(cmd) || Constants.PREVIOUS_ACTION.equals(action)) {
  597. prev();
  598. } else if (Constants.CMDTOGGLEPAUSE.equals(cmd) || Constants.TOGGLEPAUSE_ACTION.equals(action)) {
  599. if (isPlaying()) {
  600. pause();
  601. } else {
  602. play();
  603. }
  604. } else if (Constants.CMDPAUSE.equals(cmd) || Constants.PAUSE_ACTION.equals(action)) {
  605. pause();
  606. } else if (Constants.CMDSTOP.equals(cmd)) {
  607. pause();
  608. seek(0);
  609. } else if(Constants.SHUFFLE_NORMAL_ACTION.equals(action)){
  610. this.setShuffleMode(Constants.SHUFFLE_NORMAL);
  611. } else if(Constants.SHUFFLE_NONE_ACTION.equals(action)){
  612. this.setShuffleMode(Constants.SHUFFLE_NONE);
  613. } else if(Constants.REPEAT_CURRENT_ACTION.equals(action)){
  614. this.setRepeatMode(Constants.REPEAT_CURRENT);
  615. } else if(Constants.REPEAT_NONE_ACTION.equals(action)){
  616. this.setRepeatMode(Constants.REPEAT_NONE);
  617. }
  618. }
  619. // make sure the service will shut down on its own if it was
  620. // just started but not bound to and nothing is playing
  621. mDelayedStopHandler.removeCallbacksAndMessages(null);
  622. Message msg = mDelayedStopHandler.obtainMessage();
  623. mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
  624. return Constants.START_STICKY;
  625. }
  626. @Override
  627. public boolean onUnbind(Intent intent) {
  628. mServiceInUse = false;
  629. // Take a snapshot of the current playlist
  630. saveQueue(true);
  631. if (isPlaying() || mResumeAfterCall) {
  632. // something is currently playing, or will be playing once
  633. // an in-progress call ends, so don't stop the service now.
  634. return true;
  635. }
  636. // If there is a playlist but playback is paused, then wait a while
  637. // before stopping the service, so that pause/resume isn't slow.
  638. // Also delay stopping the service if we're transitioning between tracks.
  639. if (mPlayListLen > 0 || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
  640. Message msg = mDelayedStopHandler.obtainMessage();
  641. mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY);
  642. return true;
  643. }
  644. // No active playlist, OK to stop the service right now
  645. setForeground(false);
  646. stopSelf(mServiceStartId);
  647. return true;
  648. }
  649. private Handler mDelayedStopHandler = new Handler() {
  650. @Override
  651. public void handleMessage(Message msg) {
  652. // Check again to make sure nothing is playing right now
  653. if (isPlaying() || mResumeAfterCall || mServiceInUse
  654. || mMediaplayerHandler.hasMessages(TRACK_ENDED)) {
  655. return;
  656. }
  657. // save the queue again, because it might have changed
  658. // since the user exited the music app (because of
  659. // party-shuffle or because the play-position changed)
  660. saveQueue(true);
  661. setForeground(false);
  662. stopSelf(mServiceStartId);
  663. }
  664. };
  665. /**
  666. * Called when we receive a ACTION_MEDIA_EJECT notification.
  667. *
  668. * @param storagePath path to mount point for the removed media
  669. */
  670. public void closeExternalStorageFiles(String storagePath) {
  671. // stop playback and clean up if the SD card is going to be unmounted.
  672. stop(true);
  673. notifyChange(Constants.QUEUE_CHANGED);
  674. notifyChange(Constants.META_CHANGED);
  675. }
  676. /**
  677. * Registers an intent to listen for ACTION_MEDIA_EJECT notifications.
  678. * The intent will call closeExternalStorageFiles() if the external media
  679. * is going to be ejected, so applications can clean up any files they have open.
  680. */
  681. public void registerExternalStorageListener() {
  682. if (mUnmountReceiver == null) {
  683. mUnmountReceiver = new BroadcastReceiver() {
  684. @Override
  685. public void onReceive(Context context, Intent intent) {
  686. String action = intent.getAction();
  687. if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
  688. saveQueue(true);
  689. mOneShot = true; // This makes us not save the state again later,
  690. // which would be wrong because the song ids and
  691. // card id might not match.
  692. closeExternalStorageFiles(intent.getData().getPath());
  693. } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
  694. mMediaMountedCount++;
  695. mCardId = 0;
  696. // mCardId = FileUtils.getFatVolumeId(intent.getData().getPath());
  697. reloadQueue();
  698. notifyChange(Constants.QUEUE_CHANGED);
  699. notifyChange(Constants.META_CHANGED);
  700. }
  701. }
  702. };
  703. IntentFilter iFilter = new IntentFilter();
  704. iFilter.addAction(Intent.ACTION_MEDIA_EJECT);
  705. iFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
  706. iFilter.addDataScheme("file");
  707. registerReceiver(mUnmountReceiver, iFilter);
  708. }
  709. }
  710. /**
  711. * Notify the change-receivers that something has changed.
  712. * The intent that is sent contains the following data
  713. * for the currently playing track:
  714. * "id" - Integer: the database row ID
  715. * "artist" - String: the name of the artist
  716. * "album" - String: the name of the album
  717. * "track" - String: the name of the track
  718. * The intent has an action that is one of
  719. * "com.android.music.metachanged"
  720. * "com.android.music.queuechanged",
  721. * "com.android.music.playbackcomplete"
  722. * "com.android.music.playstatechanged"
  723. * respectively indicating that a new track has
  724. * started playing, that the playback queue has
  725. * changed, that playback has stopped because
  726. * the last file in the list has been played,
  727. * or that the play-state changed (paused/resumed).
  728. */
  729. private void notifyChange(String what) {
  730. Intent i = new Intent(what);
  731. i.putExtra("id", Long.valueOf(getAudioId()));
  732. i.putExtra("artist", getArtistName());
  733. i.putExtra("album",getAlbumName());
  734. i.putExtra("track", getTrackName());
  735. sendBroadcast(i);
  736. if (what.equals(Constants.QUEUE_CHANGED)) {
  737. saveQueue(true);
  738. } else {
  739. saveQueue(false);
  740. }
  741. // Share this notification directly with our widgets
  742. if(mAppWidgetProvider != null)
  743. mAppWidgetProvider.notifyChange(this, what);
  744. if(mAppWidgetProvider3x3 != null)
  745. mAppWidgetProvider3x3.notifyChange(this, what);
  746. if(mAppWidgetProvider4x4 != null)
  747. mAppWidgetProvider4x4.notifyChange(this, what);
  748. if(mAppWidgetProvider4x1 != null)
  749. mAppWidgetProvider4x1.notifyChange(this, what);
  750. }
  751. private void sendScrobbleBroadcast(int state) {
  752. // check that state is a valid state
  753. if (state != Constants.SCROBBLE_PLAYSTATE_START &&
  754. state != Constants.SCROBBLE_PLAYSTATE_RESUME &&
  755. state != Constants.SCROBBLE_PLAYSTATE_PAUSE &&
  756. state != Constants.SCROBBLE_PLAYSTATE_COMPLETE) {
  757. Log.e(TAG, "Trying to send scrobble with invalid state: " + state);
  758. return;
  759. }
  760. // String who = PreferenceManager.getDefaultSharedPreferences(this).getString(
  761. // getString(R.string.preference_scrobble_list_key),
  762. // getString(R.string.preference_scrobble_value_dont));
  763. sendGTalkStatusBroadcast(state);
  764. // check if scrobbling is enabled, and to whom we should send the broadcast
  765. if (mScrobblerName.equals(getString(R.string.preference_scrobble_value_sls)))
  766. {
  767. sendScrobbleBroadcastSLS(state);
  768. }
  769. else if (mScrobblerName.equals(getString(R.string.preference_scrobble_value_sd)))
  770. {
  771. sendScrobbleBroadcastSD(state);
  772. }
  773. }
  774. private void sendGTalkStatusBroadcast(int state) {
  775. Log.d(TAG, "Sending scrobble broadcast to GTalkStatus");
  776. Intent i = new Intent("com.gtalkstatus.android.updaterintent");
  777. i.putExtra("app-name", "RockOn NextGen"); // TODO: what is the name of this app?
  778. i.putExtra("app-package", "org.abrantix.rockon.rockonnggl");
  779. String playstate = "";
  780. switch(state)
  781. {
  782. case Constants.SCROBBLE_PLAYSTATE_START:
  783. case Constants.SCROBBLE_PLAYSTATE_RESUME:
  784. playstate = "is_playing";
  785. break;
  786. default:
  787. playstate = "not_playing";
  788. }
  789. i.putExtra("state", playstate);
  790. i.putExtra("artist", getArtistName());
  791. i.putExtra("album",getAlbumName());
  792. i.putExtra("track", getTrackName());
  793. i.putExtra("duration", (int)(duration()/1000));
  794. sendBroadcast(i);
  795. }
  796. private void sendScrobbleBroadcastSLS(int state) {
  797. Log.d(TAG, "Sending scrobble broadcast to SLS");
  798. Intent i = new Intent(Constants.SCROBBLE_SLS_API);
  799. i.putExtra("app-name", "RockOn NextGen"); // TODO: what is the name of this app?
  800. i.putExtra("app-package", "org.abrantix.rockon.rockonnggl");
  801. i.putExtra("state", state);
  802. i.putExtra("artist", getArtistName());
  803. i.putExtra("album",getAlbumName());
  804. i.putExtra("track", getTrackName());
  805. i.putExtra("duration", (int)(duration()/1000));
  806. sendBroadcast(i);
  807. }
  808. private void sendScrobbleBroadcastSD(int state) {
  809. Log.d(TAG, "Sending scrobble broadcast to SD");
  810. Intent i = new Intent(Constants.SCROBBLE_SD_API);
  811. boolean playing = false;
  812. if (state == Constants.SCROBBLE_PLAYSTATE_START ||
  813. state == Constants.SCROBBLE_PLAYSTATE_RESUME) {
  814. playing = true;
  815. }
  816. i.putExtra("playing", playing);
  817. i.putExtra("id", (int)getAudioId());
  818. sendBroadcast(i);
  819. }
  820. private void ensurePlayListCapacity(int size) {
  821. if (mPlayList == null || size > mPlayList.length) {
  822. // reallocate at 2x requested size so we don't
  823. // need to grow and copy the array for every
  824. // insert
  825. long [] newlist = new long[size * 2];
  826. int len = mPlayList != null ? mPlayList.length : mPlayListLen;
  827. for (int i = 0; i < len; i++) {
  828. newlist[i] = mPlayList[i];
  829. }
  830. mPlayList = newlist;
  831. }
  832. // FIXME: shrink the array when the needed size is much smaller
  833. // than the allocated size
  834. }
  835. // // add to the current point of the playing list
  836. // private void insertToPlayList(long[] list, int position){
  837. // int addlen = list.length;
  838. // }
  839. // insert the list of songs at the specified position in the playlist
  840. private void addToPlayList(long [] list, int position)
  841. {
  842. // Log.i(TAG, "===============================");
  843. // Log.i(TAG, "list.length: "+list.length+" position: "+position);
  844. // Log.i(TAG, "mPlaylistLen: "+mPlayListLen+" mPlaylist.length: "+mPlayList.length);
  845. int addlen = list.length;
  846. if (position < 0) { // overwrite
  847. mPlayListLen = 0;
  848. position = 0;
  849. }
  850. ensurePlayListCapacity(mPlayListLen + addlen);
  851. if (position > mPlayListLen) {
  852. position = mPlayListLen;
  853. }
  854. // move part of list after insertion point
  855. int tailsize = mPlayListLen - position;
  856. for (int i = tailsize ; i > 0 ; i--) {
  857. mPlayList[position + i + addlen] = mPlayList[position + i];
  858. // TODO: need to update history??????????
  859. }
  860. // copy list into playlist
  861. for (int i = 0; i < addlen; i++) {
  862. mPlayList[position + i] = list[i];
  863. }
  864. mPlayListLen += addlen;
  865. }
  866. /**
  867. * Appends a list of tracks to the current playlist.
  868. * If nothing is playing currently, playback will be started at
  869. * the first track.
  870. * If the action is NOW, playback will switch to the first of
  871. * the new tracks immediately.
  872. * @param list The list of tracks to append.
  873. * @param action NOW, NEXT or LAST
  874. */
  875. public void enqueue(long [] list, int action) {
  876. synchronized(this) {
  877. Log.i(TAG, "yep, action: "+action);
  878. if (action == Constants.NEXT && mPlayPos + 1 < mPlayListLen) {
  879. addToPlayList(list, mPlayPos + 1);
  880. notifyChange(Constants.QUEUE_CHANGED);
  881. } else {
  882. // action == LAST || action == NOW || mPlayPos + 1 == mPlayListLen
  883. if(action == Constants.NOW)
  884. addToPlayList(list, mPlayPos + 1);
  885. else if(action == Constants.LAST)
  886. addToPlayList(list, Integer.MAX_VALUE);
  887. notifyChange(Constants.QUEUE_CHANGED);
  888. if (action == Constants.NOW) {
  889. // mPlayPos = mPlayListLen - list.length;
  890. Log.i(TAG, "yep, opening: "+mPlayPos);
  891. mPlayPos = mPlayPos + 1;
  892. openCurrent();
  893. play();
  894. notifyChange(Constants.META_CHANGED);
  895. return;
  896. }
  897. }
  898. if (mPlayPos < 0) {
  899. mPlayPos = 0;
  900. openCurrent();
  901. play();
  902. notifyChange(Constants.META_CHANGED);
  903. }
  904. }
  905. }
  906. /**
  907. * Replaces the current playlist with a new list,
  908. * and prepares for starting playback at the specified
  909. * position in the list, or a random position if the
  910. * specified position is 0.
  911. * @param list The new list of tracks.
  912. */
  913. public void open(long [] list, int position) {
  914. synchronized (this) {
  915. if (mShuffleMode == Constants.SHUFFLE_AUTO) {
  916. mShuffleMode = Constants.SHUFFLE_NORMAL;
  917. }
  918. long oldId = getAudioId();
  919. int listlength = list.length;
  920. boolean newlist = true;
  921. if (mPlayListLen == listlength) {
  922. // possible fast path: list might be the same
  923. newlist = false;
  924. for (int i = 0; i < listlength; i++) {
  925. if (list[i] != mPlayList[i]) {
  926. newlist = true;
  927. break;
  928. }
  929. }
  930. }
  931. if (newlist) {
  932. addToPlayList(list, -1);
  933. notifyChange(Constants.QUEUE_CHANGED);
  934. }
  935. int oldpos = mPlayPos;
  936. if (position >= 0) {
  937. mPlayPos = position;
  938. } else {
  939. mPlayPos = mRand.nextInt(mPlayListLen);
  940. }
  941. mHistory.clear();
  942. saveBookmarkIfNeeded();
  943. openCurrent();
  944. if (oldId != getAudioId()) {
  945. notifyChange(Constants.META_CHANGED);
  946. }
  947. }
  948. }
  949. /**
  950. * Moves the item at index1 to index2.
  951. * @param index1
  952. * @param index2
  953. */
  954. public void moveQueueItem(int index1, int index2) {
  955. synchronized (this) {
  956. if (index1 >= mPlayListLen) {
  957. index1 = mPlayListLen - 1;
  958. }
  959. if (index2 >= mPlayListLen) {
  960. index2 = mPlayListLen - 1;
  961. }
  962. if (index1 < index2) {
  963. long tmp = mPlayList[index1];
  964. for (int i = index1; i < index2; i++) {
  965. mPlayList[i] = mPlayList[i+1];
  966. }
  967. mPlayList[index2] = tmp;
  968. if (mPlayPos == index1) {
  969. mPlayPos = index2;
  970. } else if (mPlayPos >= index1 && mPlayPos <= index2) {
  971. mPlayPos--;
  972. }
  973. } else if (index2 < index1) {
  974. long tmp = mPlayList[index1];
  975. for (int i = index1; i > index2; i--) {
  976. mPlayList[i] = mPlayList[i-1];
  977. }
  978. mPlayList[index2] = tmp;
  979. if (mPlayPos == index1) {
  980. mPlayPos = index2;
  981. } else if (mPlayPos >= index2 && mPlayPos <= index1) {
  982. mPlayPos++;
  983. }
  984. }
  985. notifyChange(Constants.QUEUE_CHANGED);
  986. }
  987. }
  988. /**
  989. * Returns the current play list
  990. * @return An array of integers containing the IDs of the tracks in the play list
  991. */
  992. public long [] getQueue() {
  993. synchronized (this) {
  994. int len = mPlayListLen;
  995. long [] list = new long[len];
  996. for (int i = 0; i < len; i++) {
  997. list[i] = mPlayList[i];
  998. }
  999. return list;
  1000. }
  1001. }
  1002. public long [] getOutstandingQueue(){
  1003. synchronized (this) {
  1004. long [] list;
  1005. if(mPlayListLen <= 0){
  1006. list = null;
  1007. // Log.i(TAG, "list.length: "+list.length+" mPlayPos: "+mPlayPos+" mPlayListLen: "+mPlayListLen);
  1008. } else if(mShuffleMode == Constants.SHUFFLE_NONE){
  1009. list = new long[mPlayListLen - mPlayPos + 1];
  1010. for(int i = mPlayPos; i<mPlayListLen; i++){
  1011. list[i-mPlayPos] = mPlayList[i];
  1012. }
  1013. } else {
  1014. // check which elements on the playlist have not been played yet
  1015. int numUnplayed = mPlayListLen;
  1016. long[] tmplist = new long[mPlayListLen];
  1017. for(int i=0; i<mPlayListLen; i++){
  1018. tmplist[i] = mPlayList[i];
  1019. if(mPlayList[i]<0)
  1020. numUnplayed--;
  1021. }
  1022. for(int i=0; i<mHistory.size(); i++){
  1023. if(mHistory.get(i).intValue() < tmplist.length)
  1024. {
  1025. if(tmplist[mHistory.get(i).intValue()] >= 0){
  1026. // always list the currently playing song
  1027. if(mHistory.get(i).intValue() != mPlayPos){
  1028. tmplist[mHistory.get(i).intValue()] = -1;
  1029. numUnplayed--;
  1030. }
  1031. }
  1032. }
  1033. }
  1034. // pass the unplayed item to our result list
  1035. list = new long[numUnplayed];
  1036. // put the currently playing song on top
  1037. list[0] = mPlayList[mPlayPos];
  1038. int cnt = 1;
  1039. for(int i=0; i<mPlayListLen; i++){
  1040. if(tmplist[i]>=0 && i != mPlayPos){ // currently playing song already added
  1041. Log.i(TAG, "numUnPlayed: "+numUnplayed + "cnt: "+cnt);
  1042. list[cnt] = tmplist[i];
  1043. cnt++;
  1044. }
  1045. }
  1046. }
  1047. return list;
  1048. }
  1049. }
  1050. private void openCurrent() {
  1051. synchronized (this) {
  1052. if (mCursor != null) {
  1053. mCursor.close();
  1054. mCursor = null;
  1055. }
  1056. if (mPlayListLen == 0) {
  1057. return;
  1058. }
  1059. stop(false);
  1060. String id = String.valueOf(mPlayList[mPlayPos]);
  1061. mCursor = getContentResolver().query(
  1062. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  1063. mCursorCols, "_id=" + id , null, null);
  1064. if (mCursor != null) {
  1065. mCursor.moveToFirst();
  1066. open(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id, false);
  1067. // go to bookmark if needed
  1068. // if (isPodcast()) {
  1069. // long bookmark = getBookmark();
  1070. // // Start playing a little bit before the bookmark,
  1071. // // so it's easier to get back in to the narrative.
  1072. // seek(bookmark - 5000);
  1073. // }
  1074. }
  1075. }
  1076. }
  1077. public void openAsync(String path) {
  1078. synchronized (this) {
  1079. if (path == null) {
  1080. return;
  1081. }
  1082. mRepeatMode = Constants.REPEAT_NONE;
  1083. ensurePlayListCapacity(1);
  1084. mPlayListLen = 1;
  1085. mPlayPos = -1;
  1086. mFileToPlay = path;
  1087. mCursor = null;
  1088. mPlayer.setDataSourceAsync(mFileToPlay);
  1089. mOneShot = true;
  1090. }
  1091. }
  1092. /**
  1093. * Opens the specified file and readies it for playback.
  1094. *
  1095. * @param path The full path of the file to be opened.
  1096. * @param oneshot when set to true, playback will stop after this file completes, instead
  1097. * of moving on to the next track in the list
  1098. */
  1099. public void open(String path, boolean oneshot) {
  1100. synchronized (this) {
  1101. if (path == null) {
  1102. return;
  1103. }
  1104. if (oneshot) {
  1105. mRepeatMode = Constants.REPEAT_NONE;
  1106. ensurePlayListCapacity(1);
  1107. mPlayListLen = 1;
  1108. mPlayPos = -1;
  1109. }
  1110. // if mCursor is null, try to associate path with a database cursor
  1111. if (mCursor == null) {
  1112. ContentResolver resolver = getContentResolver();
  1113. Uri uri;
  1114. String where;
  1115. String selectionArgs[];
  1116. if (path.startsWith("content://media/")) {
  1117. uri = Uri.parse(path);
  1118. where = null;
  1119. selectionArgs = null;
  1120. } else {
  1121. uri = MediaStore.Audio.Media.getContentUriForPath(path);
  1122. where = MediaStore.Audio.Media.DATA + "=?";
  1123. selectionArgs = new String[] { path };
  1124. }
  1125. try {
  1126. mCursor = resolver.query(uri, mCursorCols, where, selectionArgs, null);
  1127. if (mCursor != null) {
  1128. if (mCursor.getCount() == 0) {
  1129. mCursor.close();
  1130. mCursor = null;
  1131. } else {
  1132. mCursor.moveToNext();
  1133. ensurePlayListCapacity(1);
  1134. mPlayListLen = 1;
  1135. mPlayList[0] = mCursor.getLong(IDCOLIDX);
  1136. mPlayPos = 0;
  1137. }
  1138. }
  1139. } catch (UnsupportedOperationException ex) {
  1140. }
  1141. }
  1142. mFileToPlay = path;
  1143. mPlayer.setDataSource(mFileToPlay);
  1144. mOneShot = oneshot;
  1145. if (! mPlayer.isInitialized()) {
  1146. stop(true);
  1147. if (mOpenFailedCounter++ < 10 && mPlayListLen > 1) {
  1148. // beware: this ends up being recursive because next() calls open() again.
  1149. next(false);
  1150. }
  1151. if (! mPlayer.isInitialized() && mOpenFailedCounter != 0) {
  1152. // need to make sure we only shows this once
  1153. mOpenFailedCounter = 0;
  1154. if (!mQuietMode) {
  1155. // Toast.makeText(this, R.string.playback_failed, Toast.LENGTH_SHORT).show();
  1156. }
  1157. }
  1158. } else {

Large files files are truncated, but you can click here to view the full file