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