MediaService.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /**
  2. * ownCloud Android client application
  3. *
  4. * @author David A. Velasco
  5. * Copyright (C) 2015 ownCloud Inc.
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2,
  9. * as published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. package com.owncloud.android.media;
  21. import android.accounts.Account;
  22. import android.app.Notification;
  23. import android.app.NotificationManager;
  24. import android.app.PendingIntent;
  25. import android.app.Service;
  26. import android.content.Context;
  27. import android.content.Intent;
  28. import android.media.AudioManager;
  29. import android.media.MediaPlayer;
  30. import android.media.MediaPlayer.OnCompletionListener;
  31. import android.media.MediaPlayer.OnErrorListener;
  32. import android.media.MediaPlayer.OnPreparedListener;
  33. import android.net.wifi.WifiManager;
  34. import android.net.wifi.WifiManager.WifiLock;
  35. import android.os.IBinder;
  36. import android.os.PowerManager;
  37. import android.widget.Toast;
  38. import java.io.IOException;
  39. import com.owncloud.android.R;
  40. import com.owncloud.android.datamodel.OCFile;
  41. import com.owncloud.android.lib.common.utils.Log_OC;
  42. import com.owncloud.android.ui.activity.FileActivity;
  43. import com.owncloud.android.ui.activity.FileDisplayActivity;
  44. /**
  45. * Service that handles media playback, both audio and video.
  46. *
  47. * Waits for Intents which signal the service to perform specific operations: Play, Pause,
  48. * Rewind, etc.
  49. */
  50. public class MediaService extends Service implements OnCompletionListener, OnPreparedListener,
  51. OnErrorListener, AudioManager.OnAudioFocusChangeListener {
  52. private static final String TAG = MediaService.class.getSimpleName();
  53. private static final String MY_PACKAGE = MediaService.class.getPackage() != null ? MediaService.class.getPackage().getName() : "com.owncloud.android.media";
  54. /// Intent actions that we are prepared to handle
  55. public static final String ACTION_PLAY_FILE = MY_PACKAGE + ".action.PLAY_FILE";
  56. public static final String ACTION_STOP_ALL = MY_PACKAGE + ".action.STOP_ALL";
  57. /// Keys to add extras to the action
  58. public static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
  59. public static final String EXTRA_ACCOUNT = MY_PACKAGE + ".extra.ACCOUNT";
  60. public static String EXTRA_START_POSITION = MY_PACKAGE + ".extra.START_POSITION";
  61. public static final String EXTRA_PLAY_ON_LOAD = MY_PACKAGE + ".extra.PLAY_ON_LOAD";
  62. /** Error code for specific messages - see regular error codes at {@link MediaPlayer} */
  63. public static final int OC_MEDIA_ERROR = 0;
  64. /** Time To keep the control panel visible when the user does not use it */
  65. public static final int MEDIA_CONTROL_SHORT_LIFE = 4000;
  66. /** Time To keep the control panel visible when the user does not use it */
  67. public static final int MEDIA_CONTROL_PERMANENT = 0;
  68. /** Volume to set when audio focus is lost and ducking is allowed */
  69. private static final float DUCK_VOLUME = 0.1f;
  70. /** Media player instance */
  71. private MediaPlayer mPlayer = null;
  72. /** Reference to the system AudioManager */
  73. private AudioManager mAudioManager = null;
  74. /** Values to indicate the state of the service */
  75. enum State {
  76. STOPPED,
  77. PREPARING,
  78. PLAYING,
  79. PAUSED
  80. };
  81. /** Current state */
  82. private State mState = State.STOPPED;
  83. /** Possible focus values */
  84. enum AudioFocus {
  85. NO_FOCUS,
  86. NO_FOCUS_CAN_DUCK,
  87. FOCUS
  88. }
  89. /** Current focus state */
  90. private AudioFocus mAudioFocus = AudioFocus.NO_FOCUS;
  91. /** 'True' when the current song is streaming from the network */
  92. private boolean mIsStreaming = false;
  93. /** Wifi lock kept to prevents the device from shutting off the radio when streaming a file. */
  94. private WifiLock mWifiLock;
  95. private static final String MEDIA_WIFI_LOCK_TAG = MY_PACKAGE + ".WIFI_LOCK";
  96. /** Notification to keep in the notification bar while a song is playing */
  97. private NotificationManager mNotificationManager;
  98. private Notification mNotification = null;
  99. /** File being played */
  100. private OCFile mFile;
  101. /** Account holding the file being played */
  102. private Account mAccount;
  103. /** Flag signaling if the audio should be played immediately when the file is prepared */
  104. protected boolean mPlayOnPrepared;
  105. /** Position, in miliseconds, where the audio should be started */
  106. private int mStartPosition;
  107. /** Interface to access the service through binding */
  108. private IBinder mBinder;
  109. /** Control panel shown to the user to control the playback, to register through binding */
  110. private MediaControlView mMediaController;
  111. /**
  112. * Helper method to get an error message suitable to show to users for errors occurred in media playback,
  113. *
  114. * @param context A context to access string resources.
  115. * @param what See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
  116. * @param extra See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
  117. * @return Message suitable to users.
  118. */
  119. public static String getMessageForMediaError(Context context, int what, int extra) {
  120. int messageId;
  121. if (what == OC_MEDIA_ERROR) {
  122. messageId = extra;
  123. } else if (extra == MediaPlayer.MEDIA_ERROR_UNSUPPORTED) {
  124. /* Added in API level 17
  125. Bitstream is conforming to the related coding standard or file spec, but the media framework does not support the feature.
  126. Constant Value: -1010 (0xfffffc0e)
  127. */
  128. messageId = R.string.media_err_unsupported;
  129. } else if (extra == MediaPlayer.MEDIA_ERROR_IO) {
  130. /* Added in API level 17
  131. File or network related operation errors.
  132. Constant Value: -1004 (0xfffffc14)
  133. */
  134. messageId = R.string.media_err_io;
  135. } else if (extra == MediaPlayer.MEDIA_ERROR_MALFORMED) {
  136. /* Added in API level 17
  137. Bitstream is not conforming to the related coding standard or file spec.
  138. Constant Value: -1007 (0xfffffc11)
  139. */
  140. messageId = R.string.media_err_malformed;
  141. } else if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
  142. /* Added in API level 17
  143. Some operation takes too long to complete, usually more than 3-5 seconds.
  144. Constant Value: -110 (0xffffff92)
  145. */
  146. messageId = R.string.media_err_timeout;
  147. } else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
  148. /* Added in API level 3
  149. The video is streamed and its container is not valid for progressive playback i.e the video's index (e.g moov atom) is not at the start of the file.
  150. Constant Value: 200 (0x000000c8)
  151. */
  152. messageId = R.string.media_err_invalid_progressive_playback;
  153. } else {
  154. /* MediaPlayer.MEDIA_ERROR_UNKNOWN
  155. Added in API level 1
  156. Unspecified media player error.
  157. Constant Value: 1 (0x00000001)
  158. */
  159. /* MediaPlayer.MEDIA_ERROR_SERVER_DIED)
  160. Added in API level 1
  161. Media server died. In this case, the application must release the MediaPlayer object and instantiate a new one.
  162. Constant Value: 100 (0x00000064)
  163. */
  164. messageId = R.string.media_err_unknown;
  165. }
  166. return context.getString(messageId);
  167. }
  168. /**
  169. * Initialize a service instance
  170. *
  171. * {@inheritDoc}
  172. */
  173. @Override
  174. public void onCreate() {
  175. super.onCreate();
  176. Log_OC.d(TAG, "Creating ownCloud media service");
  177. mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)).
  178. createWifiLock(WifiManager.WIFI_MODE_FULL, MEDIA_WIFI_LOCK_TAG);
  179. mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
  180. mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
  181. mBinder = new MediaServiceBinder(this);
  182. }
  183. /**
  184. * Entry point for Intents requesting actions, sent here via startService.
  185. *
  186. * {@inheritDoc}
  187. */
  188. @Override
  189. public int onStartCommand(Intent intent, int flags, int startId) {
  190. String action = intent.getAction();
  191. if (action.equals(ACTION_PLAY_FILE)) {
  192. processPlayFileRequest(intent);
  193. } else if (action.equals(ACTION_STOP_ALL)) {
  194. processStopRequest(true);
  195. }
  196. return START_NOT_STICKY; // don't want it to restart in case it's killed.
  197. }
  198. /**
  199. * Processes a request to play a media file received as a parameter
  200. *
  201. * TODO If a new request is received when a file is being prepared, it is ignored. Is this what we want?
  202. *
  203. * @param intent Intent received in the request with the data to identify the file to play.
  204. */
  205. private void processPlayFileRequest(Intent intent) {
  206. if (mState != State.PREPARING) {
  207. mFile = intent.getExtras().getParcelable(EXTRA_FILE);
  208. mAccount = intent.getExtras().getParcelable(EXTRA_ACCOUNT);
  209. mPlayOnPrepared = intent.getExtras().getBoolean(EXTRA_PLAY_ON_LOAD, false);
  210. mStartPosition = intent.getExtras().getInt(EXTRA_START_POSITION, 0);
  211. tryToGetAudioFocus();
  212. playMedia();
  213. }
  214. }
  215. /**
  216. * Processes a request to play a media file.
  217. */
  218. protected void processPlayRequest() {
  219. // request audio focus
  220. tryToGetAudioFocus();
  221. // actually play the song
  222. if (mState == State.STOPPED) {
  223. // (re)start playback
  224. playMedia();
  225. } else if (mState == State.PAUSED) {
  226. // continue playback
  227. mState = State.PLAYING;
  228. setUpAsForeground(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
  229. configAndStartMediaPlayer();
  230. }
  231. }
  232. /**
  233. * Makes sure the media player exists and has been reset. This will create the media player
  234. * if needed. reset the existing media player if one already exists.
  235. */
  236. protected void createMediaPlayerIfNeeded() {
  237. if (mPlayer == null) {
  238. mPlayer = new MediaPlayer();
  239. // make sure the CPU won't go to sleep while media is playing
  240. mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
  241. // the media player will notify the service when it's ready preparing, and when it's done playing
  242. mPlayer.setOnPreparedListener(this);
  243. mPlayer.setOnCompletionListener(this);
  244. mPlayer.setOnErrorListener(this);
  245. } else {
  246. mPlayer.reset();
  247. }
  248. }
  249. /**
  250. * Processes a request to pause the current playback
  251. */
  252. protected void processPauseRequest() {
  253. if (mState == State.PLAYING) {
  254. mState = State.PAUSED;
  255. mPlayer.pause();
  256. releaseResources(false); // retain media player in pause
  257. // TODO polite audio focus, instead of keep it owned; or not?
  258. }
  259. }
  260. /**
  261. * Processes a request to stop the playback.
  262. *
  263. * @param force When 'true', the playback is stopped no matter the value of mState
  264. */
  265. protected void processStopRequest(boolean force) {
  266. if (mState != State.PREPARING || force) {
  267. mState = State.STOPPED;
  268. mFile = null;
  269. mAccount = null;
  270. releaseResources(true);
  271. giveUpAudioFocus();
  272. stopSelf(); // service is no longer necessary
  273. }
  274. }
  275. /**
  276. * Releases resources used by the service for playback. This includes the "foreground service"
  277. * status and notification, the wake locks and possibly the MediaPlayer.
  278. *
  279. * @param releaseMediaPlayer Indicates whether the Media Player should also be released or not
  280. */
  281. protected void releaseResources(boolean releaseMediaPlayer) {
  282. // stop being a foreground service
  283. stopForeground(true);
  284. // stop and release the Media Player, if it's available
  285. if (releaseMediaPlayer && mPlayer != null) {
  286. mPlayer.reset();
  287. mPlayer.release();
  288. mPlayer = null;
  289. }
  290. // release the Wifi lock, if holding it
  291. if (mWifiLock.isHeld()) {
  292. mWifiLock.release();
  293. }
  294. }
  295. /**
  296. * Fully releases the audio focus.
  297. */
  298. private void giveUpAudioFocus() {
  299. if (mAudioFocus == AudioFocus.FOCUS
  300. && mAudioManager != null
  301. && AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.abandonAudioFocus(this)) {
  302. mAudioFocus = AudioFocus.NO_FOCUS;
  303. }
  304. }
  305. /**
  306. * Reconfigures MediaPlayer according to audio focus settings and starts/restarts it.
  307. */
  308. protected void configAndStartMediaPlayer() {
  309. if (mPlayer == null) {
  310. throw new IllegalStateException("mPlayer is NULL");
  311. }
  312. if (mAudioFocus == AudioFocus.NO_FOCUS) {
  313. if (mPlayer.isPlaying()) {
  314. mPlayer.pause(); // have to be polite; but mState is not changed, to resume when focus is received again
  315. }
  316. } else {
  317. if (mAudioFocus == AudioFocus.NO_FOCUS_CAN_DUCK) {
  318. mPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME);
  319. } else {
  320. mPlayer.setVolume(1.0f, 1.0f); // full volume
  321. }
  322. if (!mPlayer.isPlaying()) {
  323. mPlayer.start();
  324. }
  325. }
  326. }
  327. /**
  328. * Requests the audio focus to the Audio Manager
  329. */
  330. private void tryToGetAudioFocus() {
  331. if (mAudioFocus != AudioFocus.FOCUS
  332. && mAudioManager != null
  333. && (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.requestAudioFocus( this,
  334. AudioManager.STREAM_MUSIC,
  335. AudioManager.AUDIOFOCUS_GAIN))
  336. ) {
  337. mAudioFocus = AudioFocus.FOCUS;
  338. }
  339. }
  340. /**
  341. * Starts playing the current media file.
  342. */
  343. protected void playMedia() {
  344. mState = State.STOPPED;
  345. releaseResources(false); // release everything except MediaPlayer
  346. try {
  347. if (mFile == null) {
  348. Toast.makeText(this, R.string.media_err_nothing_to_play, Toast.LENGTH_LONG).show();
  349. processStopRequest(true);
  350. return;
  351. } else if (mAccount == null) {
  352. Toast.makeText(this, R.string.media_err_not_in_owncloud, Toast.LENGTH_LONG).show();
  353. processStopRequest(true);
  354. return;
  355. }
  356. createMediaPlayerIfNeeded();
  357. mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  358. String url = mFile.getStoragePath();
  359. /* Streaming is not possible right now
  360. if (url == null || url.length() <= 0) {
  361. url = AccountUtils.constructFullURLForAccount(this, mAccount) + mFile.getRemotePath();
  362. }
  363. mIsStreaming = url.startsWith("http:") || url.startsWith("https:");
  364. */
  365. mIsStreaming = false;
  366. mPlayer.setDataSource(url);
  367. mState = State.PREPARING;
  368. setUpAsForeground(String.format(getString(R.string.media_state_loading), mFile.getFileName()));
  369. // starts preparing the media player in background
  370. mPlayer.prepareAsync();
  371. // prevent the Wifi from going to sleep when streaming
  372. if (mIsStreaming) {
  373. mWifiLock.acquire();
  374. } else if (mWifiLock.isHeld()) {
  375. mWifiLock.release();
  376. }
  377. } catch (SecurityException e) {
  378. Log_OC.e(TAG, "SecurityException playing " + mAccount.name + mFile.getRemotePath(), e);
  379. Toast.makeText(this, String.format(getString(R.string.media_err_security_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
  380. processStopRequest(true);
  381. } catch (IOException e) {
  382. Log_OC.e(TAG, "IOException playing " + mAccount.name + mFile.getRemotePath(), e);
  383. Toast.makeText(this, String.format(getString(R.string.media_err_io_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
  384. processStopRequest(true);
  385. } catch (IllegalStateException e) {
  386. Log_OC.e(TAG, "IllegalStateException " + mAccount.name + mFile.getRemotePath(), e);
  387. Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
  388. processStopRequest(true);
  389. } catch (IllegalArgumentException e) {
  390. Log_OC.e(TAG, "IllegalArgumentException " + mAccount.name + mFile.getRemotePath(), e);
  391. Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
  392. processStopRequest(true);
  393. }
  394. }
  395. /** Called when media player is done playing current song. */
  396. public void onCompletion(MediaPlayer player) {
  397. Toast.makeText(this, String.format(getString(R.string.media_event_done, mFile.getFileName())), Toast.LENGTH_LONG).show();
  398. if (mMediaController != null) {
  399. // somebody is still bound to the service
  400. player.seekTo(0);
  401. processPauseRequest();
  402. mMediaController.updatePausePlay();
  403. } else {
  404. // nobody is bound
  405. processStopRequest(true);
  406. }
  407. return;
  408. }
  409. /**
  410. * Called when media player is done preparing.
  411. *
  412. * Time to start.
  413. */
  414. public void onPrepared(MediaPlayer player) {
  415. mState = State.PLAYING;
  416. updateNotification(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
  417. if (mMediaController != null) {
  418. mMediaController.setEnabled(true);
  419. }
  420. player.seekTo(mStartPosition);
  421. configAndStartMediaPlayer();
  422. if (!mPlayOnPrepared) {
  423. processPauseRequest();
  424. }
  425. if (mMediaController != null) {
  426. mMediaController.updatePausePlay();
  427. }
  428. }
  429. /**
  430. * Updates the status notification
  431. */
  432. @SuppressWarnings("deprecation")
  433. private void updateNotification(String content) {
  434. // TODO check if updating the Intent is really necessary
  435. Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
  436. showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
  437. showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
  438. showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  439. mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
  440. (int)System.currentTimeMillis(),
  441. showDetailsIntent,
  442. PendingIntent.FLAG_UPDATE_CURRENT);
  443. mNotification.when = System.currentTimeMillis();
  444. //mNotification.contentView.setTextViewText(R.id.status_text, content);
  445. String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
  446. mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
  447. mNotificationManager.notify(R.string.media_notif_ticker, mNotification);
  448. }
  449. /**
  450. * Configures the service as a foreground service.
  451. *
  452. * The system will avoid finishing the service as much as possible when resources as low.
  453. *
  454. * A notification must be created to keep the user aware of the existance of the service.
  455. */
  456. @SuppressWarnings("deprecation")
  457. private void setUpAsForeground(String content) {
  458. /// creates status notification
  459. // TODO put a progress bar to follow the playback progress
  460. mNotification = new Notification();
  461. mNotification.icon = android.R.drawable.ic_media_play;
  462. //mNotification.tickerText = text;
  463. mNotification.when = System.currentTimeMillis();
  464. mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
  465. //mNotification.contentView.setTextViewText(R.id.status_text, "ownCloud Music Player"); // NULL POINTER
  466. //mNotification.contentView.setTextViewText(R.id.status_text, getString(R.string.downloader_download_in_progress_content));
  467. /// includes a pending intent in the notification showing the details view of the file
  468. Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
  469. showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
  470. showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
  471. showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  472. mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
  473. (int)System.currentTimeMillis(),
  474. showDetailsIntent,
  475. PendingIntent.FLAG_UPDATE_CURRENT);
  476. //mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
  477. String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
  478. mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
  479. startForeground(R.string.media_notif_ticker, mNotification);
  480. }
  481. /**
  482. * Called when there's an error playing media.
  483. *
  484. * Warns the user about the error and resets the media player.
  485. */
  486. public boolean onError(MediaPlayer mp, int what, int extra) {
  487. Log_OC.e(TAG, "Error in audio playback, what = " + what + ", extra = " + extra);
  488. String message = getMessageForMediaError(this, what, extra);
  489. Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
  490. processStopRequest(true);
  491. return true;
  492. }
  493. /**
  494. * Called by the system when another app tries to play some sound.
  495. *
  496. * {@inheritDoc}
  497. */
  498. @Override
  499. public void onAudioFocusChange(int focusChange) {
  500. if (focusChange > 0) {
  501. // focus gain; check AudioManager.AUDIOFOCUS_* values
  502. mAudioFocus = AudioFocus.FOCUS;
  503. // restart media player with new focus settings
  504. if (mState == State.PLAYING)
  505. configAndStartMediaPlayer();
  506. } else if (focusChange < 0) {
  507. // focus loss; check AudioManager.AUDIOFOCUS_* values
  508. boolean canDuck = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK == focusChange;
  509. mAudioFocus = canDuck ? AudioFocus.NO_FOCUS_CAN_DUCK : AudioFocus.NO_FOCUS;
  510. // start/restart/pause media player with new focus settings
  511. if (mPlayer != null && mPlayer.isPlaying())
  512. configAndStartMediaPlayer();
  513. }
  514. }
  515. /**
  516. * Called when the service is finished for final clean-up.
  517. *
  518. * {@inheritDoc}
  519. */
  520. @Override
  521. public void onDestroy() {
  522. mState = State.STOPPED;
  523. releaseResources(true);
  524. giveUpAudioFocus();
  525. super.onDestroy();
  526. }
  527. /**
  528. * Provides a binder object that clients can use to perform operations on the MediaPlayer managed by the MediaService.
  529. */
  530. @Override
  531. public IBinder onBind(Intent arg) {
  532. return mBinder;
  533. }
  534. /**
  535. * Called when ALL the bound clients were onbound.
  536. *
  537. * The service is destroyed if playback stopped or paused
  538. */
  539. @Override
  540. public boolean onUnbind(Intent intent) {
  541. if (mState == State.PAUSED || mState == State.STOPPED) {
  542. processStopRequest(false);
  543. }
  544. return false; // not accepting rebinding (default behaviour)
  545. }
  546. /**
  547. * Accesses the current MediaPlayer instance in the service.
  548. *
  549. * To be handled carefully. Visibility is protected to be accessed only
  550. *
  551. * @return Current MediaPlayer instance handled by MediaService.
  552. */
  553. protected MediaPlayer getPlayer() {
  554. return mPlayer;
  555. }
  556. /**
  557. * Accesses the current OCFile loaded in the service.
  558. *
  559. * @return The current OCFile loaded in the service.
  560. */
  561. protected OCFile getCurrentFile() {
  562. return mFile;
  563. }
  564. /**
  565. * Accesses the current {@link State} of the MediaService.
  566. *
  567. * @return The current {@link State} of the MediaService.
  568. */
  569. protected State getState() {
  570. return mState;
  571. }
  572. protected void setMediaContoller(MediaControlView mediaController) {
  573. mMediaController = mediaController;
  574. }
  575. protected MediaControlView getMediaController() {
  576. return mMediaController;
  577. }
  578. }