@Override
 public synchronized void shuffle() {
   Collections.shuffle(downloadList);
   if (currentPlaying != null) {
     downloadList.remove(getCurrentPlayingIndex());
     downloadList.add(0, currentPlaying);
   }
   revision++;
   lifecycleSupport.serializeDownloadQueue();
   updateJukeboxPlaylist();
 }
 @Override
 public synchronized void clearIncomplete() {
   reset();
   Iterator<DownloadFile> iterator = downloadList.iterator();
   while (iterator.hasNext()) {
     DownloadFile downloadFile = iterator.next();
     if (!downloadFile.isCompleteFileAvailable()) {
       iterator.remove();
     }
   }
   lifecycleSupport.serializeDownloadQueue();
   updateJukeboxPlaylist();
 }
 @Override
 public synchronized void remove(DownloadFile downloadFile) {
   if (downloadFile == currentDownloading) {
     currentDownloading.cancelDownload();
     currentDownloading = null;
   }
   if (downloadFile == currentPlaying) {
     reset();
     setCurrentPlaying(null);
   }
   downloadList.remove(downloadFile);
   revision++;
   lifecycleSupport.serializeDownloadQueue();
   updateJukeboxPlaylist();
 }
  public synchronized void clear(boolean serialize) {
    reset();
    downloadList.clear();
    revision++;
    if (currentDownloading != null) {
      currentDownloading.cancelDownload();
      currentDownloading = null;
    }
    setCurrentPlaying(null);

    if (serialize) {
      lifecycleSupport.serializeDownloadQueue();
    }
    updateJukeboxPlaylist();
  }
  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycleSupport.onDestroy();
    mediaPlayer.release();
    shufflePlayBuffer.shutdown();
    if (equalizerController != null) {
      equalizerController.release();
    }
    if (visualizerController != null) {
      visualizerController.release();
    }

    instance = null;
  }
  @Override
  public synchronized void download(
      List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext) {
    shufflePlay = false;
    int offset = 1;

    if (songs.isEmpty()) {
      return;
    }

    if (save) {
      for (MusicDirectory.Entry song : songs) {
        DownloadFile downloadFile = forSong(song);
        downloadFile.pin();
        if (!downloadFile.isWorkDone() && !downloadList.contains(downloadFile)) {
          downloadList.add(downloadFile);
        }
      }
    } else if (playNext) {
      if (autoplay && getCurrentPlayingIndex() >= 0) {
        offset = 0;
      }
      for (MusicDirectory.Entry song : songs) {
        DownloadFile downloadFile = new DownloadFile(this, song);
        downloadList.add(getCurrentPlayingIndex() + offset, downloadFile);
        offset++;
      }

    } else {
      for (MusicDirectory.Entry song : songs) {
        DownloadFile downloadFile = new DownloadFile(this, song);
        downloadList.add(downloadFile);
      }
    }

    revision++;
    updateJukeboxPlaylist();

    if (autoplay) {
      play(0);
    } else {
      if (currentPlaying == null) {
        currentPlaying = downloadList.get(0);
      }
      checkDownloads();
    }
    lifecycleSupport.serializeDownloadQueue();
  }
  synchronized void setPlayerState(PlayerState playerState) {
    LOG.info(this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")");

    if (playerState == PAUSED) {
      lifecycleSupport.serializeDownloadQueue();
    }

    Util.broadcastPlaybackStatusChange(this, playerState);

    this.playerState = playerState;

    if (playerState == STARTED) {
      scrobbler.scrobble(this, currentPlaying, false);
      NotificationUtil.setNotificationHiddenByUser(this, false);
    } else if (playerState == COMPLETED) {
      scrobbler.scrobble(this, currentPlaying, true);
    }

    MusicDirectory.Entry song = currentPlaying == null ? null : currentPlaying.getSong();
    NotificationUtil.updateNotification(this, this, handler, song, this.playerState == STARTED);
  }
  @Override
  public void onCreate() {
    Util.setUncaughtExceptionHandler(this);
    super.onCreate();

    mediaPlayer = new MediaPlayer();
    mediaPlayer.setWakeMode(this, PowerManager.PARTIAL_WAKE_LOCK);

    mediaPlayer.setOnErrorListener(
        new MediaPlayer.OnErrorListener() {
          @Override
          public boolean onError(MediaPlayer mediaPlayer, int what, int more) {
            handleError(new Exception("MediaPlayer error: " + what + " (" + more + ")"));
            return false;
          }
        });

    if (equalizerAvailable) {
      equalizerController = new EqualizerController(this, mediaPlayer);
      if (!equalizerController.isAvailable()) {
        equalizerController = null;
      } else {
        equalizerController.loadSettings();
      }
    }
    if (visualizerAvailable) {
      visualizerController = new VisualizerController(this, mediaPlayer);
      if (!visualizerController.isAvailable()) {
        visualizerController = null;
      }
    }

    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
    wakeLock.setReferenceCounted(false);

    instance = this;
    lifecycleSupport.onCreate();
  }
  protected synchronized void checkDownloads() {

    if (!Util.isExternalStoragePresent() || !lifecycleSupport.isExternalStorageAvailable()) {
      return;
    }

    if (shufflePlay) {
      checkShufflePlay();
    }

    if (jukeboxEnabled || !Util.isNetworkConnected(this)) {
      return;
    }

    if (downloadList.isEmpty()) {
      return;
    }

    // Need to download current playing?
    if (currentPlaying != null
        && currentPlaying != currentDownloading
        && !currentPlaying.isCompleteFileAvailable()) {

      // Cancel current download, if necessary.
      if (currentDownloading != null) {
        currentDownloading.cancelDownload();
      }

      currentDownloading = currentPlaying;
      currentDownloading.download();
      cleanupCandidates.add(currentDownloading);
    }

    // Find a suitable target for download.
    else if (currentDownloading == null
        || currentDownloading.isWorkDone()
        || currentDownloading.isFailed()) {

      int n = size();
      if (n == 0) {
        return;
      }

      int preloaded = 0;

      int start = currentPlaying == null ? 0 : getCurrentPlayingIndex();
      int i = start;
      do {
        DownloadFile downloadFile = downloadList.get(i);
        if (!downloadFile.isWorkDone()) {
          if (downloadFile.shouldSave() || preloaded < Util.getPreloadCount(this)) {
            currentDownloading = downloadFile;
            currentDownloading.download();
            cleanupCandidates.add(currentDownloading);
            break;
          }
        } else if (currentPlaying != downloadFile) {
          preloaded++;
        }

        i = (i + 1) % n;
      } while (i != start);
    }

    // Delete obsolete .partial and .complete files.
    cleanup();
  }
  private synchronized void doPlay(final DownloadFile downloadFile, int position, boolean start) {
    try {
      final File file =
          downloadFile.isCompleteFileAvailable()
              ? downloadFile.getCompleteFile()
              : downloadFile.getPartialFile();
      downloadFile.updateModificationDate();
      mediaPlayer.setOnCompletionListener(null);
      mediaPlayer.reset();
      setPlayerState(IDLE);
      mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
      mediaPlayer.setDataSource(file.getPath());
      setPlayerState(PREPARING);
      mediaPlayer.prepare();
      setPlayerState(PREPARED);

      mediaPlayer.setOnCompletionListener(
          new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {

              // Acquire a temporary wakelock, since when we return from
              // this callback the MediaPlayer will release its wakelock
              // and allow the device to go to sleep.
              wakeLock.acquire(60000);

              setPlayerState(COMPLETED);

              // If COMPLETED and not playing partial file, we are *really" finished
              // with the song and can move on to the next.
              if (!file.equals(downloadFile.getPartialFile())) {
                onSongCompleted();
                return;
              }

              // If file is not completely downloaded, restart the playback from the current
              // position.
              int pos = mediaPlayer.getCurrentPosition();
              synchronized (DownloadServiceImpl.this) {

                // Work-around for apparent bug on certain phones: If close (less than ten seconds)
                // to the end
                // of the song, skip to the next rather than restarting it.
                Integer duration =
                    downloadFile.getSong().getDuration() == null
                        ? null
                        : downloadFile.getSong().getDuration() * 1000;
                if (duration != null) {
                  if (Math.abs(duration - pos) < 10000) {
                    LOG.info("Skipping restart from " + pos + " of " + duration);
                    onSongCompleted();
                    return;
                  }
                }

                LOG.info("Requesting restart from " + pos + " of " + duration);
                reset();
                bufferTask = new BufferTask(downloadFile, pos);
                bufferTask.start();
              }
            }
          });

      if (position != 0) {
        LOG.info("Restarting player from position " + position);
        mediaPlayer.seekTo(position);
      }

      if (start) {
        mediaPlayer.start();
        setPlayerState(STARTED);
      } else {
        setPlayerState(PAUSED);
      }
      lifecycleSupport.serializeDownloadQueue();

    } catch (Exception x) {
      handleError(x);
    }
  }
 @Override
 public void onStart(Intent intent, int startId) {
   super.onStart(intent, startId);
   lifecycleSupport.onStart(intent);
 }