Exemple #1
0
  public static Task createDownloadTask(
      ApiSession sess,
      String servicename,
      Path local,
      CommonPath remote,
      Struct storeParams,
      boolean allowResume) {
    DownloadFile work = new DownloadFile();
    work.session = sess;

    RecordStruct params =
        new RecordStruct(
            new FieldStruct("LocalPath", local),
            new FieldStruct("RemotePath", remote),
            new FieldStruct("ServiceName", servicename),
            new FieldStruct("TransferParams", storeParams));

    if (allowResume && Files.exists(local)) {
      try {
        params.setField("Offset", Files.size(local));
      } catch (IOException x) {
        Logger.error("Unable to get file size for: " + local);
        return null;
      }
    }

    return new Task()
        .withTitle("Download file " + local)
        .withWork(work)
        .withSubContext()
        .withParams(params)
        .withTimeout(1)
        .withDeadline(0);
  }
 public Drawable getDrawable() {
   if (downloadFile.exists()) {
     return AttachedImageDrawable.drawableFromPath(this, downloadFile.getFile().getAbsolutePath());
   }
   AvatarData.asyncRequestDownload(userId);
   return getDefaultDrawable();
 }
  private void tableSelectionChanged() {

    if (selectedDownload != null) selectedDownload.deleteObserver(DownloadGUI.this);

    if (!clearing && table.getSelectedRow() > -1) {
      selectedDownload = tableModel.getDownload(table.getSelectedRow());
      selectedDownload.addObserver(DownloadGUI.this);
      updateButtons();
    }
  }
 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();
 }
Exemple #7
0
 @NonNull
 public Drawable getDrawable() {
   Drawable drawable = MyImageCache.getAvatarDrawable(this, downloadFile.getFilePath());
   if (drawable == MyDrawableCache.BROKEN) {
     return getDefaultDrawable();
   } else if (drawable != null) {
     return drawable;
   }
   if (!downloadFile.exists()) {
     AvatarData.asyncRequestDownload(userId);
   }
   return getDefaultDrawable();
 }
  @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 serializeDownloadQueue() {
    State state = new State();
    for (DownloadFile downloadFile : downloadService.getDownloads()) {
      state.songs.add(downloadFile.getSong());
    }
    state.currentPlayingIndex = downloadService.getCurrentPlayingIndex();
    state.currentPlayingPosition = downloadService.getPlayerPosition();

    Log.i(
        TAG,
        "Serialized currentPlayingIndex: "
            + state.currentPlayingIndex
            + ", currentPlayingPosition: "
            + state.currentPlayingPosition);
    FileUtil.serialize(downloadService, state, FILENAME_DOWNLOADS_SER);
  }
 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);
     }
   }
 }
  /**
   * Update the progress bar of the currently downloaded file in main window. Only update if progess
   * has at least increased by one percent of the total file size of the downloaded file.
   *
   * @param downloadedBytes The current amount of downloaded bytes
   * @param lastProgBarUpdate The byte count at the last progress bar update
   * @param file The download file
   * @return The byte count at the last progress bar update
   */
  private int updateProgressBar(int downloadedBytes, int lastProgBarUpdate, DownloadFile file) {
    int totalSize = (int) file.getTotalFileSize();

    // only update progess bar if progess has at least increased by one percent
    int diff = downloadedBytes - lastProgBarUpdate;
    int onePercent = (int) totalSize / 100;
    if (diff >= onePercent) {
      final String filename = file.getFilename();
      final int db = downloadedBytes;
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              mainApp.updateDownloadQueue(filename, db);
            }
          });

      return downloadedBytes; // prog bar updated, so return the new byte count
    }

    return lastProgBarUpdate; // no update, so return the previous byte count
  }
  @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();
      }
    }
  }
  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 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();
 }
    private boolean bufferComplete() {
      boolean completeFileAvailable = downloadFile.isCompleteFileAvailable();
      long size = partialFile.length();

      LOG.info(
          "Buffering "
              + partialFile
              + " ("
              + size
              + "/"
              + expectedFileSize
              + ", "
              + completeFileAvailable
              + ")");
      return completeFileAvailable || size >= expectedFileSize;
    }
  /**
   * Download .zip file specified by url, then unzip it to a folder in external storage.
   *
   * @param url
   */
  private void downloadAllAssets(String url) {
    // File zipDir = ExternalStorage.get
    // Temp folder for holding asset during download
    File zipDir = ExternalStorage.getSDCacheDir(this, "tmp");
    // File path to store .zip file before unzipping
    File zipFile = new File(zipDir.getPath() + "/temp.zip");
    // Folder to hold unzipped output
    File outputDir = ExternalStorage.getSDCacheDir(this, "unzipped");

    try {
      DownloadFile.download(url, zipFile, zipDir);
      unzipFile(zipFile, outputDir);
    } finally {
      zipFile.delete();
    }
  }
 @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);
  }
  private void updateButtons() {
    if (selectedDownload != null) {
      int status = selectedDownload.getStatus();
      switch (status) {
        case DownloadFile.DOWNLOADING:
          pauseButton.setEnabled(true);
          resumeButton.setEnabled(false);
          cancelButton.setEnabled(true);
          clearButton.setEnabled(false);
          break;
        case DownloadFile.PAUSED:
          pauseButton.setEnabled(false);
          resumeButton.setEnabled(true);
          cancelButton.setEnabled(true);
          clearButton.setEnabled(false);
          break;
        case DownloadFile.ERROR:
          pauseButton.setEnabled(false);
          resumeButton.setEnabled(true);
          cancelButton.setEnabled(false);
          clearButton.setEnabled(true);
          break;
        default:
          pauseButton.setEnabled(false);
          resumeButton.setEnabled(false);
          cancelButton.setEnabled(false);
          clearButton.setEnabled(true);
      }
    } else {

      pauseButton.setEnabled(false);
      resumeButton.setEnabled(false);
      cancelButton.setEnabled(false);
      clearButton.setEnabled(false);
    }
  }
  /**
   * This method is called when a whole download file has been finished downloading. It updates main
   * application window and starts the decoding thread.
   *
   * @param dlFile The DownloadFile object that is finished
   */
  private void handleFinishedDlFile(final DownloadFile dlFile) {
    final String filename = dlFile.getFilename();
    logger.msg("File downloading finished: " + filename, MyLogger.SEV_INFO);

    // notify application that download has finished
    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            mainApp.fileDownloadFinished(filename);
            mainApp.setProgBarToDecoding(filename, dlFile.getSegCount());
          }
        });

    // create result vector
    Vector<byte[]> articleData = new Vector<byte[]>();
    Vector<RspHandler> rspHandlers = dlFileRspHandlerMap.get(dlFile);
    for (int i = 0; i < rspHandlers.size(); i++) {
      byte[] tmpArray = removeFirstLine(rspHandlers.get(i).getData(true));
      articleData.add(tmpArray);
      rspHandlers.set(i, null); // free some memory
    }

    // call garbage collector
    rspHandlers = null;
    dlFileRspHandlerMap.remove(dlFile);
    Runtime.getRuntime().gc();

    logger.msg(
        "First line(s) dump:\n" + HelloNzbToolkit.firstLineFromByteData(articleData.get(0), 2),
        MyLogger.SEV_DEBUG);

    // determine data encoding (yenc or UU)
    String encoding = null;
    boolean bHasData = false;
    for (int i = 0; i < articleData.size(); i++) {
      byte[] abyteHelp = articleData.get(i);
      if (abyteHelp.length > 0) {
        bHasData = true;
        if (bytesEqualsString(abyteHelp, "=ybegin")) {
          encoding = "yenc";
          break;
        } else if (bytesEqualsString(abyteHelp, "begin ")) {
          encoding = "uu";
          break;
        }
      }
    }
    if (encoding == null) {
      if (bHasData) {
        encoding = "yenc";
        logger.msg(
            "No suitable decoder (no data) found for downloaded file: "
                + dlFile.getFilename()
                + " -- Assuming yenc.",
            MyLogger.SEV_WARNING);
      } else {
        // too bad, no decoder found for this file :(
        logger.msg(
            "No suitable decoder found for downloaded file (no data): " + dlFile.getFilename(),
            MyLogger.SEV_ERROR);

        // update main application window
        SwingUtilities.invokeLater(
            new Runnable() {
              public void run() {
                mainApp.fileDecodingFinished(dlFile.getFilename());
              }
            });

        return;
      }
    }

    /*
     * // determine data encoding String encoding = null;
     * if(bytesEqualsString(articleData.get(0), "=ybegin")) encoding =
     * "yenc"; else if(bytesEqualsString(articleData.get(0), "begin "))
     * encoding = "uu"; else { // too bad, no decoder found for this file :(
     * logger.msg("No suitable decoder found for downloaded file: " +
     * dlFile.getFilename(), MyLogger.SEV_ERROR);
     *
     * // update main application window SwingUtilities.invokeLater(new
     * Runnable() { public void run() {
     * mainApp.fileDecodingFinished(dlFile.getFilename()); } } );
     *
     * return; }
     */

    // start data decoding background thread
    FileDecoder fileDecoder = new FileDecoder(mainApp, dlDir, dlFile, articleData, encoding);
    Thread t = new Thread(fileDecoder);
    t.start();
  }
  /** This method starts the thread and begins to download the file. */
  public void run() {
    int maxThreads = Integer.parseInt(mainApp.getPrefValue("ServerSettingsThreadCount"));
    int runningThreads = 0;
    HashMap<String, Integer> downloadedBytes = new HashMap<String, Integer>();
    HashMap<String, Integer> lastProgBarUpdate = new HashMap<String, Integer>();

    // loop at all segments of the download file
    while (!shutdown && (segQueue.hasMoreSegments() || runningThreads > 0)) {
      // more segments to go?
      while (segQueue.hasMoreSegments()
          && runningThreads < maxThreads
          && !pause
          && nioClient.hasFreeSlot()) {
        // get next download segment of the download file
        DownloadFileSegment seg = segQueue.nextSegment();
        if (seg == null) break;
        String filename = seg.getDlFile().getFilename();
        logger.msg("Downloading next segment of file: " + filename, MyLogger.SEV_DEBUG);

        // create new response handler
        RspHandler newHandler = new RspHandler(seg);
        activeRspHandlers.add(newHandler);

        // map the new response handler to the download file
        Vector<RspHandler> tmpVector = dlFileRspHandlerMap.get(seg.getDlFile());
        if (tmpVector == null) tmpVector = new Vector<RspHandler>();
        tmpVector.add(newHandler);
        dlFileRspHandlerMap.put(seg.getDlFile(), tmpVector);

        // start data download
        nioClient.fetchArticleData(seg.getGroups().firstElement(), seg.getArticleId(), newHandler);

        // increase thread counter
        runningThreads++;
      }

      // check if the next element of the result set is already finished
      Vector<RspHandler> toRemoveVector = new Vector<RspHandler>();
      for (int i = 0; i < activeRspHandlers.size(); i++) {
        RspHandler handler = activeRspHandlers.get(i);

        // handle error response from NNTP server
        if (handler.getError() == RspHandler.ERR_NONE) {
          // no error, do nothing
        } else if (handler.getError() == RspHandler.ERR_AUTH) {
          // do nothing for this error (?)
        } else if (handler.getError() == RspHandler.ERR_FETCH) {
          // TODO: handle "430 no such article" error (?)
          String msg =
              "no such article found: <"
                  + handler.dlFileSeg().getArticleId()
                  + "> ("
                  + handler.getErrorMsg()
                  + ")";
          logger.msg(msg, MyLogger.SEV_WARNING);
        } else {
          // all other errors
          shutdown = true;
        }

        // update downloaded byte counter ...
        DownloadFile dlFile = handler.dlFileSeg().getDlFile();
        String filename = dlFile.getFilename();
        int bytes = 0;
        Integer bytesInt = downloadedBytes.get(filename);
        if (bytesInt != null) bytes = bytesInt;
        bytes += handler.newByteCount();
        downloadedBytes.put(filename, bytes);

        // ... and progres bar in main window
        int last = 0;
        Integer lastInt = lastProgBarUpdate.get(filename);
        if (lastInt != null) last = lastInt;
        last = updateProgressBar(bytes, last, dlFile);
        lastProgBarUpdate.put(filename, last);

        // all data downloaded?
        if (handler.isFinished()) {
          toRemoveVector.add(handler);
          runningThreads--;
          decrSegCount(filename); // decrease main window segment
          // counter

          // segment done, so check if whole download file is finished
          // now
          dlFile.removeSegment(handler.dlFileSeg().getIndex());
          if (!dlFile.hasMoreSegments()) {
            try {
              handleFinishedDlFile(dlFile);
            } catch (Exception e) {
              logger.printStackTrace(e);
            }
          }
        }
      }
      activeRspHandlers.removeAll(toRemoveVector);
      toRemoveVector.removeAllElements();

      // all tasks done?
      if (!segQueue.hasMoreSegments() && runningThreads == 0) {
        break;
      }

      try {
        // let the thread sleep a bit
        Thread.sleep(10);
      } catch (InterruptedException e) {
        // shutdown if interrupted
        shutdown = true;
      }
    } // end of main loop

    logger.msg("FileDownloader has finished downloading all files", MyLogger.SEV_DEBUG);
  }
  public void displaySongInfo(final MusicDirectory.Entry song) {
    Integer bitrate = null;
    String format = null;
    long size = 0;
    try {
      DownloadFile downloadFile = new DownloadFile(SubsonicTabActivity.this, song, false);
      File file = downloadFile.getCompleteFile();
      if (file.exists()) {
        MediaMetadataRetriever metadata = new MediaMetadataRetriever();
        metadata.setDataSource(file.getAbsolutePath());
        String tmp = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
        bitrate = Integer.parseInt((tmp != null) ? tmp : "0") / 1000;
        format = FileUtil.getExtension(file.getName());
        size = file.length();

        if (Util.isOffline(SubsonicTabActivity.this)) {
          song.setGenre(metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE));
          String year = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
          song.setYear(Integer.parseInt((year != null) ? year : "0"));
        }
      }
    } catch (Exception e) {
      Log.i(TAG, "Device doesn't properly support MediaMetadataRetreiver");
    }

    String msg = "";
    if (!song.isVideo()) {
      msg += "Artist: " + song.getArtist() + "\nAlbum: " + song.getAlbum();
    }
    if (song.getTrack() != null && song.getTrack() != 0) {
      msg += "\nTrack: " + song.getTrack();
    }
    if (song.getGenre() != null && !"".equals(song.getGenre())) {
      msg += "\nGenre: " + song.getGenre();
    }
    if (song.getYear() != null && song.getYear() != 0) {
      msg += "\nYear: " + song.getYear();
    }
    if (!Util.isOffline(SubsonicTabActivity.this)) {
      msg += "\nServer Format: " + song.getSuffix();
      if (song.getBitRate() != null && song.getBitRate() != 0) {
        msg += "\nServer Bitrate: " + song.getBitRate() + " kpbs";
      }
    }
    if (format != null && !"".equals(format)) {
      msg += "\nCached Format: " + format;
    }
    if (bitrate != null && bitrate != 0) {
      msg += "\nCached Bitrate: " + bitrate + " kpbs";
    }
    if (size != 0) {
      msg += "\nSize: " + Util.formatBytes(size);
    }
    if (song.getDuration() != null && song.getDuration() != 0) {
      msg += "\nLength: " + Util.formatDuration(song.getDuration());
    }

    new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(song.getTitle())
        .setMessage(msg)
        .show();
  }
 private void actionPause() {
   selectedDownload.pause();
   updateButtons();
 }
 private void actionResume() {
   selectedDownload.resume();
   updateButtons();
 }
 private void actionCancel() {
   selectedDownload.cancel();
   updateButtons();
 }
  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);
    }
  }
  public void update(Observable o, Object arg) {

    if (selectedDownload != null && selectedDownload.equals(o)) updateButtons();
  }