/**
  * Reconfigures MediaPlayer according to audio focus settings and starts/restarts it. This method
  * starts/restarts the MediaPlayer respecting the current audio focus state. So if we have focus,
  * it will play normally; if we don't have focus, it will either leave the MediaPlayer paused or
  * set it to a low volume, depending on what is allowed by the current focus settings. This method
  * assumes mPlayer != null, so if you are calling it, you have to do so from a context where you
  * are sure this is the case.
  */
 private void configMediaPlayerState() {
   LogHelper.d(TAG, "configMediaPlayerState. mAudioFocus=", mAudioFocus);
   if (mAudioFocus == AUDIO_NO_FOCUS_NO_DUCK) {
     // If we don't have audio focus and can't duck, we have to pause,
     if (mState == PlaybackStateCompat.STATE_PLAYING) {
       pause();
     }
   } else { // we have audio focus:
     if (mAudioFocus == AUDIO_NO_FOCUS_CAN_DUCK) {
       mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK); // we'll be relatively quiet
     } else {
       if (mMediaPlayer != null) {
         mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL); // we can be loud again
       } // else do something for remote client.
     }
     // If we were playing when we lost focus, we need to resume playing.
     if (mPlayOnFocusGain) {
       if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
         LogHelper.d(
             TAG, "configMediaPlayerState startMediaPlayer. seeking to ", mCurrentPosition);
         if (mCurrentPosition == mMediaPlayer.getCurrentPosition()) {
           mMediaPlayer.start();
           mState = PlaybackStateCompat.STATE_PLAYING;
         } else {
           mMediaPlayer.seekTo(mCurrentPosition);
           mState = PlaybackStateCompat.STATE_BUFFERING;
         }
       }
       mPlayOnFocusGain = false;
     }
   }
   if (mCallback != null) {
     mCallback.onPlaybackStatusChanged(mState);
   }
 }
 /**
  * Called when media player is done preparing.
  *
  * @see OnPreparedListener
  */
 @Override
 public void onPrepared(MediaPlayer player) {
   LogHelper.d(TAG, "onPrepared from MediaPlayer");
   // The media player is done preparing. That means we can start playing if we
   // have audio focus.
   configMediaPlayerState();
 }
  /**
   * Called by AudioManager on audio focus changes. Implementation of {@link
   * AudioManager.OnAudioFocusChangeListener}
   */
  @Override
  public void onAudioFocusChange(int focusChange) {
    LogHelper.d(TAG, "onAudioFocusChange. focusChange=", focusChange);
    if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
      // We have gained focus:
      mAudioFocus = AUDIO_FOCUSED;

    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS
        || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
        || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
      // We have lost focus. If we can duck (low playback volume), we can keep playing.
      // Otherwise, we need to pause the playback.
      boolean canDuck = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
      mAudioFocus = canDuck ? AUDIO_NO_FOCUS_CAN_DUCK : AUDIO_NO_FOCUS_NO_DUCK;

      // If we are playing, we need to reset media player by calling configMediaPlayerState
      // with mAudioFocus properly set.
      if (mState == PlaybackStateCompat.STATE_PLAYING && !canDuck) {
        // If we don't have audio focus and can't duck, we save the information that
        // we were playing, so that we can resume playback once we get the focus back.
        mPlayOnFocusGain = true;
      }
    } else {
      LogHelper.e(TAG, "onAudioFocusChange: Ignoring unsupported focusChange: ", focusChange);
    }
    configMediaPlayerState();
  }
 /** Give up the audio focus. */
 private void giveUpAudioFocus() {
   LogHelper.d(TAG, "giveUpAudioFocus");
   if (mAudioFocus == AUDIO_FOCUSED) {
     if (mAudioManager.abandonAudioFocus(this) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
       mAudioFocus = AUDIO_NO_FOCUS_NO_DUCK;
     }
   }
 }
 /**
  * Called when media player is done playing current song.
  *
  * @see OnCompletionListener
  */
 @Override
 public void onCompletion(MediaPlayer player) {
   LogHelper.d(TAG, "onCompletion from MediaPlayer");
   // The media player finished playing the current song, so we go ahead
   // and start the next.
   if (mCallback != null) {
     mCallback.onCompletion();
   }
 }
 /** Try to get the system audio focus. */
 private void tryToGetAudioFocus() {
   LogHelper.d(TAG, "tryToGetAudioFocus");
   if (mAudioFocus != AUDIO_FOCUSED) {
     int result =
         mAudioManager.requestAudioFocus(
             this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
     if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
       mAudioFocus = AUDIO_FOCUSED;
     }
   }
 }
 @Override
 public void onReceive(Context context, Intent intent) {
   if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
     LogHelper.d(TAG, "Headphones disconnected.");
     if (isPlaying()) {
       Intent i = new Intent(context, MusicService.class);
       i.setAction(MusicService.ACTION_CMD);
       i.putExtra(MusicService.CMD_NAME, MusicService.CMD_PAUSE);
       mService.startService(i);
     }
   }
 }
 /**
  * Called when MediaPlayer has completed a seek
  *
  * @see OnSeekCompleteListener
  */
 @Override
 public void onSeekComplete(MediaPlayer mp) {
   LogHelper.d(TAG, "onSeekComplete from MediaPlayer:", mp.getCurrentPosition());
   mCurrentPosition = mp.getCurrentPosition();
   if (mState == PlaybackStateCompat.STATE_BUFFERING) {
     mMediaPlayer.start();
     mState = PlaybackStateCompat.STATE_PLAYING;
   }
   if (mCallback != null) {
     mCallback.onPlaybackStatusChanged(mState);
   }
 }
  /**
   * Releases resources used by the service for playback. This includes the "foreground service"
   * status, the wake locks and possibly the MediaPlayer.
   *
   * @param releaseMediaPlayer Indicates whether the Media Player should also be released or not
   */
  private void relaxResources(boolean releaseMediaPlayer) {
    LogHelper.d(TAG, "relaxResources. releaseMediaPlayer=", releaseMediaPlayer);

    mService.stopForeground(true);

    // stop and release the Media Player, if it's available
    if (releaseMediaPlayer && mMediaPlayer != null) {
      mMediaPlayer.reset();
      mMediaPlayer.release();
      mMediaPlayer = null;
    }

    // we can also release the Wifi lock, if we're holding it
    if (mWifiLock.isHeld()) {
      mWifiLock.release();
    }
  }
  @Override
  public void seekTo(int position) {
    LogHelper.d(TAG, "seekTo called with ", position);

    if (mMediaPlayer == null) {
      // If we do not have a current media player, simply update the current position
      mCurrentPosition = position;
    } else {
      if (mMediaPlayer.isPlaying()) {
        mState = PlaybackStateCompat.STATE_BUFFERING;
      }
      mMediaPlayer.seekTo(position);
      if (mCallback != null) {
        mCallback.onPlaybackStatusChanged(mState);
      }
    }
  }
  /**
   * Makes sure the media player exists and has been reset. This will create the media player if
   * needed, or reset the existing media player if one already exists.
   */
  private void createMediaPlayerIfNeeded() {
    LogHelper.d(TAG, "createMediaPlayerIfNeeded. needed? ", (mMediaPlayer == null));
    if (mMediaPlayer == null) {
      mMediaPlayer = new MediaPlayer();

      // Make sure the media player will acquire a wake-lock while
      // playing. If we don't do that, the CPU might go to sleep while the
      // song is playing, causing playback to stop.
      mMediaPlayer.setWakeMode(mService.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

      // we want the media player to notify us when it's ready preparing,
      // and when it's done playing:
      mMediaPlayer.setOnPreparedListener(this);
      mMediaPlayer.setOnCompletionListener(this);
      mMediaPlayer.setOnErrorListener(this);
      mMediaPlayer.setOnSeekCompleteListener(this);
    } else {
      mMediaPlayer.reset();
    }
  }