PageRenderTime 37ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/android/music/MediaPlaybackService.java

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