private synchronized void cleanup() {
   Iterator<DownloadFile> iterator = cleanupCandidates.iterator();
   while (iterator.hasNext()) {
     DownloadFile downloadFile = iterator.next();
     if (downloadFile != currentPlaying && downloadFile != currentDownloading) {
       if (downloadFile.cleanup()) {
         iterator.remove();
       }
     }
   }
 }
    public BufferTask(DownloadFile downloadFile, int position) {
      this.downloadFile = downloadFile;
      this.position = position;
      partialFile = downloadFile.getPartialFile();

      // Calculate roughly how many bytes BUFFER_LENGTH_SECONDS corresponds to.
      int bitRate = downloadFile.getBitRate();
      long byteCount = Math.max(100000, bitRate * 1024 / 8 * BUFFER_LENGTH_SECONDS);

      // Find out how large the file should grow before resuming playback.
      expectedFileSize = partialFile.length() + byteCount;
    }
 @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 DownloadFile forSong(MusicDirectory.Entry song) {
    for (DownloadFile downloadFile : downloadList) {
      if (downloadFile.getSong().equals(song)) {
        return downloadFile;
      }
    }

    DownloadFile downloadFile = downloadFileCache.get(song);
    if (downloadFile == null) {
      downloadFile = new DownloadFile(this, song);
      downloadFileCache.put(song, downloadFile);
    }
    return downloadFile;
  }
  @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 setCurrentPlaying(DownloadFile currentPlaying) {
    this.currentPlaying = currentPlaying;

    MusicDirectory.Entry song = currentPlaying != null ? currentPlaying.getSong() : null;
    Util.broadcastNewTrackInfo(this, song);
    NotificationUtil.updateNotification(this, this, handler, song, song != null);
  }
 public void restore(
     List<MusicDirectory.Entry> songs, int currentPlayingIndex, int currentPlayingPosition) {
   download(songs, false, false, false);
   if (currentPlayingIndex != -1) {
     play(currentPlayingIndex, false);
     if (currentPlaying.isCompleteFileAvailable()) {
       doPlay(currentPlaying, currentPlayingPosition, false);
     }
   }
 }
  @Override
  public void setJukeboxEnabled(boolean jukeboxEnabled) {
    this.jukeboxEnabled = jukeboxEnabled;
    jukeboxService.setEnabled(jukeboxEnabled);
    if (jukeboxEnabled) {
      reset();

      // Cancel current download, if necessary.
      if (currentDownloading != null) {
        currentDownloading.cancelDownload();
      }
    }
  }
 @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();
  }
    private boolean bufferComplete() {
      boolean completeFileAvailable = downloadFile.isCompleteFileAvailable();
      long size = partialFile.length();

      LOG.info(
          "Buffering "
              + partialFile
              + " ("
              + size
              + "/"
              + expectedFileSize
              + ", "
              + completeFileAvailable
              + ")");
      return completeFileAvailable || size >= expectedFileSize;
    }
 @Override
 public synchronized int getPlayerDuration() {
   if (currentPlaying != null) {
     Integer duration = currentPlaying.getSong().getDuration();
     if (duration != null) {
       return duration * 1000;
     }
   }
   if (playerState != IDLE && playerState != DOWNLOADING && playerState != PlayerState.PREPARING) {
     try {
       return mediaPlayer.getDuration();
     } catch (Exception x) {
       handleError(x);
     }
   }
   return 0;
 }
  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);
  }
  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);
    }
  }