2013-02-08 10:09:55 +01:00
|
|
|
/*
|
|
|
|
This file is part of Subsonic.
|
|
|
|
|
|
|
|
Subsonic is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Subsonic is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Copyright 2009 (C) Sindre Mehus
|
|
|
|
*/
|
2013-04-06 21:47:24 +02:00
|
|
|
package com.thejoshwa.ultrasonic.androidapp.service;
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
import android.annotation.SuppressLint;
|
2013-02-08 10:09:55 +01:00
|
|
|
import android.app.Notification;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.app.Service;
|
|
|
|
import android.content.ComponentName;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.media.AudioManager;
|
|
|
|
import android.media.MediaMetadataRetriever;
|
|
|
|
import android.media.MediaPlayer;
|
|
|
|
import android.media.RemoteControlClient;
|
2013-05-16 09:59:55 +02:00
|
|
|
import android.media.audiofx.AudioEffect;
|
|
|
|
import android.os.Build;
|
2013-02-08 10:09:55 +01:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.IBinder;
|
2013-05-16 09:59:55 +02:00
|
|
|
import android.os.Looper;
|
2013-02-08 10:09:55 +01:00
|
|
|
import android.os.PowerManager;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.widget.RemoteViews;
|
2013-02-10 09:13:20 +01:00
|
|
|
import android.widget.SeekBar;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.R;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.activity.DownloadActivity;
|
2013-04-24 18:31:42 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.activity.SubsonicTabActivity;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.audiofx.EqualizerController;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.audiofx.VisualizerController;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory;
|
2013-05-26 07:19:28 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.PlayerState;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.RepeatMode;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x1;
|
2013-05-26 07:19:28 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x2;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x3;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x4;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.receiver.MediaButtonIntentReceiver;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.CancellableTask;
|
2013-05-16 09:59:55 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.Constants;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.FileUtil;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.LRUCache;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.ShufflePlayBuffer;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.SimpleServiceBinder;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.StreamProxy;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.Util;
|
2013-02-08 10:09:55 +01:00
|
|
|
import java.io.File;
|
2013-05-16 09:59:55 +02:00
|
|
|
import java.net.URLEncoder;
|
2013-02-08 10:09:55 +01:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
2013-04-06 21:47:24 +02:00
|
|
|
import static com.thejoshwa.ultrasonic.androidapp.domain.PlayerState.*;
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @author Sindre Mehus, Joshua Bahnsen
|
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
public class DownloadServiceImpl extends Service implements DownloadService {
|
|
|
|
|
|
|
|
private static final String TAG = DownloadServiceImpl.class.getSimpleName();
|
|
|
|
|
2013-04-06 21:47:24 +02:00
|
|
|
public static final String CMD_PLAY = "com.thejoshwa.ultrasonic.androidapp.CMD_PLAY";
|
|
|
|
public static final String CMD_TOGGLEPAUSE = "com.thejoshwa.ultrasonic.androidapp.CMD_TOGGLEPAUSE";
|
|
|
|
public static final String CMD_PAUSE = "com.thejoshwa.ultrasonic.androidapp.CMD_PAUSE";
|
|
|
|
public static final String CMD_STOP = "com.thejoshwa.ultrasonic.androidapp.CMD_STOP";
|
|
|
|
public static final String CMD_PREVIOUS = "com.thejoshwa.ultrasonic.androidapp.CMD_PREVIOUS";
|
|
|
|
public static final String CMD_NEXT = "com.thejoshwa.ultrasonic.androidapp.CMD_NEXT";
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
private final IBinder binder = new SimpleServiceBinder<DownloadService>(this);
|
2013-05-16 09:59:55 +02:00
|
|
|
private Looper mediaPlayerLooper;
|
2013-02-08 10:09:55 +01:00
|
|
|
private MediaPlayer mediaPlayer;
|
2013-05-16 09:59:55 +02:00
|
|
|
private MediaPlayer nextMediaPlayer;
|
2013-06-07 04:27:45 +02:00
|
|
|
private boolean nextSetup = false;
|
2013-02-08 10:09:55 +01:00
|
|
|
private final List<DownloadFile> downloadList = new ArrayList<DownloadFile>();
|
2013-05-16 09:59:55 +02:00
|
|
|
private final List<DownloadFile> backgroundDownloadList = new ArrayList<DownloadFile>();
|
2013-02-08 10:09:55 +01:00
|
|
|
private final Handler handler = new Handler();
|
2013-05-16 09:59:55 +02:00
|
|
|
private Handler mediaPlayerHandler;
|
2013-02-08 10:09:55 +01:00
|
|
|
private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this);
|
|
|
|
private final ShufflePlayBuffer shufflePlayBuffer = new ShufflePlayBuffer(this);
|
|
|
|
|
|
|
|
private final LRUCache<MusicDirectory.Entry, DownloadFile> downloadFileCache = new LRUCache<MusicDirectory.Entry, DownloadFile>(100);
|
|
|
|
private final List<DownloadFile> cleanupCandidates = new ArrayList<DownloadFile>();
|
|
|
|
private final Scrobbler scrobbler = new Scrobbler();
|
|
|
|
private final JukeboxService jukeboxService = new JukeboxService(this);
|
2013-04-24 18:31:42 +02:00
|
|
|
private Notification notification = new Notification(R.drawable.ic_stat_ultrasonic, null, System.currentTimeMillis());
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
private DownloadFile currentPlaying;
|
2013-05-16 09:59:55 +02:00
|
|
|
private DownloadFile nextPlaying;
|
2013-02-08 10:09:55 +01:00
|
|
|
private DownloadFile currentDownloading;
|
|
|
|
private CancellableTask bufferTask;
|
2013-05-16 09:59:55 +02:00
|
|
|
private CancellableTask nextPlayingTask;
|
2013-02-08 10:09:55 +01:00
|
|
|
private PlayerState playerState = IDLE;
|
2013-05-16 09:59:55 +02:00
|
|
|
private PlayerState nextPlayerState = IDLE;
|
2013-02-08 10:09:55 +01:00
|
|
|
private boolean shufflePlay;
|
|
|
|
private long revision;
|
|
|
|
private static DownloadService instance;
|
|
|
|
private String suggestedPlaylistName;
|
|
|
|
private PowerManager.WakeLock wakeLock;
|
|
|
|
private boolean keepScreenOn = false;
|
2013-05-16 09:59:55 +02:00
|
|
|
private int cachedPosition = 0;
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
private static boolean equalizerAvailable;
|
|
|
|
private static boolean visualizerAvailable;
|
|
|
|
private EqualizerController equalizerController;
|
|
|
|
private VisualizerController visualizerController;
|
|
|
|
private boolean showVisualization;
|
|
|
|
private boolean jukeboxEnabled;
|
2013-05-16 09:59:55 +02:00
|
|
|
private PositionCache positionCache;
|
2013-02-10 09:13:20 +01:00
|
|
|
private StreamProxy proxy;
|
2013-02-08 10:09:55 +01:00
|
|
|
private static MusicDirectory.Entry currentSong;
|
2013-02-23 02:23:41 +01:00
|
|
|
private RemoteControlClient remoteControlClient;
|
2013-04-09 08:01:45 +02:00
|
|
|
private AudioManager audioManager;
|
2013-02-23 05:45:10 +01:00
|
|
|
private int secondaryProgress = -1;
|
2013-05-16 09:59:55 +02:00
|
|
|
private boolean autoPlayStart = false;
|
2013-06-07 04:27:45 +02:00
|
|
|
private final static int lockScreenBitmapSize = 500;
|
|
|
|
|
|
|
|
static {
|
2013-05-17 09:00:20 +02:00
|
|
|
try {
|
|
|
|
EqualizerController.checkAvailable();
|
|
|
|
equalizerAvailable = true;
|
|
|
|
} catch (Throwable t) {
|
|
|
|
equalizerAvailable = false;
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-05-17 09:00:20 +02:00
|
|
|
static {
|
|
|
|
try {
|
|
|
|
VisualizerController.checkAvailable();
|
|
|
|
visualizerAvailable = true;
|
|
|
|
} catch (Throwable t) {
|
|
|
|
visualizerAvailable = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
@SuppressLint("NewApi")
|
|
|
|
@Override
|
2013-02-08 10:09:55 +01:00
|
|
|
public void onCreate() {
|
|
|
|
super.onCreate();
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
new Thread(new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
Looper.prepare();
|
|
|
|
|
2013-06-07 10:47:57 +02:00
|
|
|
if (mediaPlayer != null) {
|
2013-06-07 04:27:45 +02:00
|
|
|
mediaPlayer.release();
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
mediaPlayer = new MediaPlayer();
|
|
|
|
mediaPlayer.setWakeMode(DownloadServiceImpl.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;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
try {
|
|
|
|
Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
|
|
|
|
i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mediaPlayer.getAudioSessionId());
|
|
|
|
i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
|
|
|
|
sendBroadcast(i);
|
|
|
|
} catch(Throwable e) {
|
|
|
|
// Froyo or lower
|
|
|
|
}
|
|
|
|
|
|
|
|
mediaPlayerLooper = Looper.myLooper();
|
|
|
|
mediaPlayerHandler = new Handler(mediaPlayerLooper);
|
|
|
|
Looper.loop();
|
|
|
|
}
|
|
|
|
}).start();
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-04-09 08:01:45 +02:00
|
|
|
audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
|
2013-06-07 04:27:45 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
|
|
|
|
notification.contentView = new RemoteViews(this.getPackageName(), R.layout.notification);
|
2013-05-20 08:33:40 +02:00
|
|
|
Util.linkButtons(this, notification.contentView, false);
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN){
|
|
|
|
notification.bigContentView = new RemoteViews(this.getPackageName(), R.layout.notification_large);
|
|
|
|
Util.linkButtons(this, notification.bigContentView, false);
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
Intent notificationIntent = new Intent(this, DownloadActivity.class);
|
|
|
|
notification.contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
|
2013-05-20 08:33:40 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
if (equalizerAvailable) {
|
|
|
|
equalizerController = new EqualizerController(this, mediaPlayer);
|
|
|
|
if (!equalizerController.isAvailable()) {
|
|
|
|
equalizerController = null;
|
|
|
|
} else {
|
|
|
|
equalizerController.loadSettings();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (visualizerAvailable) {
|
2013-06-07 04:27:45 +02:00
|
|
|
visualizerController = new VisualizerController(mediaPlayer);
|
2013-02-08 10:09:55 +01:00
|
|
|
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();
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
@Override
|
2013-05-16 09:59:55 +02:00
|
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
|
|
super.onStartCommand(intent, flags, startId);
|
2013-02-08 10:09:55 +01:00
|
|
|
lifecycleSupport.onStart(intent);
|
2013-05-16 09:59:55 +02:00
|
|
|
return START_NOT_STICKY;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
super.onDestroy();
|
2013-07-17 10:59:58 +02:00
|
|
|
|
|
|
|
instance = null;
|
2013-02-08 10:09:55 +01:00
|
|
|
lifecycleSupport.onDestroy();
|
|
|
|
mediaPlayer.release();
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
if(nextMediaPlayer != null) {
|
|
|
|
nextMediaPlayer.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
mediaPlayerLooper.quit();
|
2013-02-08 10:09:55 +01:00
|
|
|
shufflePlayBuffer.shutdown();
|
2013-05-16 09:59:55 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
if (equalizerController != null) {
|
|
|
|
equalizerController.release();
|
|
|
|
}
|
|
|
|
if (visualizerController != null) {
|
|
|
|
visualizerController.release();
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
try {
|
|
|
|
Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
|
|
|
|
i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mediaPlayer.getAudioSessionId());
|
|
|
|
i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
|
|
|
|
sendBroadcast(i);
|
|
|
|
} catch(Throwable e) {
|
|
|
|
// Froyo or lower
|
|
|
|
}
|
|
|
|
|
|
|
|
if(bufferTask != null) {
|
|
|
|
bufferTask.cancel();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(nextPlayingTask != null) {
|
|
|
|
nextPlayingTask.cancel();
|
|
|
|
}
|
|
|
|
|
2013-02-23 02:23:41 +01:00
|
|
|
audioManager.unregisterRemoteControlClient(remoteControlClient);
|
2013-02-08 10:09:55 +01:00
|
|
|
notification = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static DownloadService getInstance() {
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public IBinder onBind(Intent intent) {
|
|
|
|
return binder;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-05-29 10:21:09 +02:00
|
|
|
public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, boolean newPlaylist) {
|
2013-02-08 10:09:55 +01:00
|
|
|
shufflePlay = false;
|
|
|
|
int offset = 1;
|
|
|
|
|
|
|
|
if (songs.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
|
|
|
if (newPlaylist) {
|
|
|
|
downloadList.clear();
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
if (playNext) {
|
|
|
|
if (autoplay && getCurrentPlayingIndex() >= 0) {
|
|
|
|
offset = 0;
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
for (MusicDirectory.Entry song : songs) {
|
|
|
|
DownloadFile downloadFile = new DownloadFile(this, song, save);
|
|
|
|
downloadList.add(getCurrentPlayingIndex() + offset, downloadFile);
|
|
|
|
offset++;
|
2013-07-17 10:59:58 +02:00
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
revision++;
|
|
|
|
} else {
|
2013-07-17 10:59:58 +02:00
|
|
|
int size = size();
|
|
|
|
int index = getCurrentPlayingIndex();
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
for (MusicDirectory.Entry song : songs) {
|
|
|
|
DownloadFile downloadFile = new DownloadFile(this, song, save);
|
|
|
|
downloadList.add(downloadFile);
|
|
|
|
}
|
2013-07-17 10:59:58 +02:00
|
|
|
|
|
|
|
if(!autoplay && (size - 1) == index) {
|
|
|
|
setNextPlaying();
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
revision++;
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
updateJukeboxPlaylist();
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
if(shuffle)
|
|
|
|
shuffle();
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
if (autoplay) {
|
|
|
|
play(0);
|
|
|
|
} else {
|
|
|
|
if (currentPlaying == null) {
|
|
|
|
currentPlaying = downloadList.get(0);
|
2013-05-16 09:59:55 +02:00
|
|
|
currentPlaying.setPlaying(true);
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
2013-06-07 10:47:57 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
checkDownloads();
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
public synchronized void downloadBackground(List<MusicDirectory.Entry> songs, boolean save) {
|
|
|
|
for (MusicDirectory.Entry song : songs) {
|
|
|
|
DownloadFile downloadFile = new DownloadFile(this, song, save);
|
|
|
|
backgroundDownloadList.add(downloadFile);
|
|
|
|
}
|
2013-07-17 10:59:58 +02:00
|
|
|
|
|
|
|
revision++;
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
checkDownloads();
|
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
private void updateJukeboxPlaylist() {
|
|
|
|
if (jukeboxEnabled) {
|
|
|
|
jukeboxService.updatePlaylist();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-29 10:21:09 +02:00
|
|
|
@Override
|
|
|
|
public void restore(List<MusicDirectory.Entry> songs, int currentPlayingIndex, int currentPlayingPosition, boolean autoPlay, boolean newPlaylist) {
|
|
|
|
download(songs, false, false, false, false, newPlaylist);
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
if (currentPlayingIndex != -1) {
|
2013-07-17 10:59:58 +02:00
|
|
|
while (mediaPlayer == null) {
|
|
|
|
Util.sleepQuietly(50L);
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
play(currentPlayingIndex, autoPlayStart);
|
2013-05-29 10:21:09 +02:00
|
|
|
|
|
|
|
if (currentPlaying != null) {
|
|
|
|
if (autoPlay && jukeboxEnabled) {
|
|
|
|
jukeboxService.skip(getCurrentPlayingIndex(), currentPlayingPosition / 1000);
|
|
|
|
} else {
|
|
|
|
if (currentPlaying.isCompleteFileAvailable()) {
|
|
|
|
doPlay(currentPlaying, currentPlayingPosition, autoPlay);
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
autoPlayStart = false;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
}
|
2013-05-20 08:33:40 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
@Override
|
|
|
|
public synchronized void setShufflePlayEnabled(boolean enabled) {
|
|
|
|
shufflePlay = enabled;
|
|
|
|
if (shufflePlay) {
|
|
|
|
clear();
|
|
|
|
checkDownloads();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-05-16 09:59:55 +02:00
|
|
|
public boolean isShufflePlayEnabled() {
|
2013-02-08 10:09:55 +01:00
|
|
|
return shufflePlay;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void shuffle() {
|
|
|
|
Collections.shuffle(downloadList);
|
|
|
|
if (currentPlaying != null) {
|
|
|
|
downloadList.remove(getCurrentPlayingIndex());
|
|
|
|
downloadList.add(0, currentPlaying);
|
|
|
|
}
|
|
|
|
revision++;
|
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
updateJukeboxPlaylist();
|
2013-05-16 09:59:55 +02:00
|
|
|
setNextPlaying();
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public RepeatMode getRepeatMode() {
|
|
|
|
return Util.getRepeatMode(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setRepeatMode(RepeatMode repeatMode) {
|
|
|
|
Util.setRepeatMode(this, repeatMode);
|
2013-05-16 09:59:55 +02:00
|
|
|
setNextPlaying();
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean getKeepScreenOn() {
|
|
|
|
return keepScreenOn;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setKeepScreenOn(boolean keepScreenOn) {
|
|
|
|
this.keepScreenOn = keepScreenOn;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean getShowVisualization() {
|
|
|
|
return showVisualization;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setShowVisualization(boolean showVisualization) {
|
|
|
|
this.showVisualization = showVisualization;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized DownloadFile forSong(MusicDirectory.Entry song) {
|
|
|
|
for (DownloadFile downloadFile : downloadList) {
|
2013-05-16 09:59:55 +02:00
|
|
|
if (downloadFile.getSong().equals(song) &&
|
|
|
|
((downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && downloadFile.getPartialFile().exists())
|
|
|
|
|| downloadFile.isWorkDone())) {
|
|
|
|
return downloadFile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (DownloadFile downloadFile : backgroundDownloadList) {
|
2013-02-08 10:09:55 +01:00
|
|
|
if (downloadFile.getSong().equals(song)) {
|
|
|
|
return downloadFile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DownloadFile downloadFile = downloadFileCache.get(song);
|
|
|
|
if (downloadFile == null) {
|
|
|
|
downloadFile = new DownloadFile(this, song, false);
|
|
|
|
downloadFileCache.put(song, downloadFile);
|
|
|
|
}
|
|
|
|
return downloadFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void clear() {
|
|
|
|
clear(true);
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void clearBackground() {
|
|
|
|
if(currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) {
|
|
|
|
currentDownloading.cancelDownload();
|
|
|
|
currentDownloading = null;
|
|
|
|
}
|
|
|
|
backgroundDownloadList.clear();
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
@Override
|
|
|
|
public synchronized void clearIncomplete() {
|
|
|
|
reset();
|
|
|
|
Iterator<DownloadFile> iterator = downloadList.iterator();
|
2013-06-07 10:47:57 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
while (iterator.hasNext()) {
|
|
|
|
DownloadFile downloadFile = iterator.next();
|
|
|
|
if (!downloadFile.isCompleteFileAvailable()) {
|
|
|
|
iterator.remove();
|
|
|
|
}
|
|
|
|
}
|
2013-06-07 10:47:57 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
updateJukeboxPlaylist();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized int size() {
|
|
|
|
return downloadList.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized void clear(boolean serialize) {
|
|
|
|
reset();
|
|
|
|
downloadList.clear();
|
|
|
|
revision++;
|
|
|
|
if (currentDownloading != null) {
|
|
|
|
currentDownloading.cancelDownload();
|
|
|
|
currentDownloading = null;
|
|
|
|
}
|
2013-06-07 04:27:45 +02:00
|
|
|
setCurrentPlaying(null);
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
if (serialize) {
|
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
}
|
|
|
|
updateJukeboxPlaylist();
|
2013-05-16 09:59:55 +02:00
|
|
|
setNextPlaying();
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void remove(int which) {
|
|
|
|
downloadList.remove(which);
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
@Override
|
|
|
|
public synchronized void remove(DownloadFile downloadFile) {
|
|
|
|
if (downloadFile == currentDownloading) {
|
|
|
|
currentDownloading.cancelDownload();
|
|
|
|
currentDownloading = null;
|
|
|
|
}
|
|
|
|
if (downloadFile == currentPlaying) {
|
|
|
|
reset();
|
2013-06-07 04:27:45 +02:00
|
|
|
setCurrentPlaying(null);
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
downloadList.remove(downloadFile);
|
2013-05-16 09:59:55 +02:00
|
|
|
backgroundDownloadList.remove(downloadFile);
|
2013-02-08 10:09:55 +01:00
|
|
|
revision++;
|
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
updateJukeboxPlaylist();
|
2013-05-16 09:59:55 +02:00
|
|
|
if(downloadFile == nextPlaying) {
|
|
|
|
setNextPlaying();
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void delete(List<MusicDirectory.Entry> songs) {
|
|
|
|
for (MusicDirectory.Entry song : songs) {
|
|
|
|
forSong(song).delete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void unpin(List<MusicDirectory.Entry> songs) {
|
|
|
|
for (MusicDirectory.Entry song : songs) {
|
|
|
|
forSong(song).unpin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-07 04:27:45 +02:00
|
|
|
synchronized void setCurrentPlaying(int currentPlayingIndex) {
|
2013-02-08 10:09:55 +01:00
|
|
|
try {
|
2013-06-07 04:27:45 +02:00
|
|
|
setCurrentPlaying(downloadList.get(currentPlayingIndex));
|
2013-02-08 10:09:55 +01:00
|
|
|
} catch (IndexOutOfBoundsException x) {
|
|
|
|
// Ignored
|
|
|
|
}
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
2013-06-07 04:27:45 +02:00
|
|
|
synchronized void setCurrentPlaying(DownloadFile currentPlaying) {
|
2013-02-08 10:09:55 +01:00
|
|
|
this.currentPlaying = currentPlaying;
|
|
|
|
|
|
|
|
if (currentPlaying != null) {
|
|
|
|
Util.broadcastNewTrackInfo(this, currentPlaying.getSong());
|
2013-04-18 08:18:59 +02:00
|
|
|
Util.broadcastA2dpMetaDataChange(this, getInstance());
|
2013-02-08 10:09:55 +01:00
|
|
|
} else {
|
|
|
|
Util.broadcastNewTrackInfo(this, null);
|
2013-04-24 18:31:42 +02:00
|
|
|
Util.broadcastA2dpMetaDataChange(this, null);
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
2013-04-18 08:18:59 +02:00
|
|
|
updateRemoteControl();
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-02-23 03:59:40 +01:00
|
|
|
// Update widget
|
2013-05-26 07:19:28 +02:00
|
|
|
UltraSonicAppWidgetProvider4x1.getInstance().notifyChange(this, this, playerState == PlayerState.STARTED, false);
|
|
|
|
UltraSonicAppWidgetProvider4x2.getInstance().notifyChange(this, this, playerState == PlayerState.STARTED, true);
|
|
|
|
UltraSonicAppWidgetProvider4x3.getInstance().notifyChange(this, this, playerState == PlayerState.STARTED, false);
|
|
|
|
UltraSonicAppWidgetProvider4x4.getInstance().notifyChange(this, this, playerState == PlayerState.STARTED, false);
|
2013-05-20 08:33:40 +02:00
|
|
|
SubsonicTabActivity tabInstance = SubsonicTabActivity.getInstance();
|
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
if (currentPlaying != null) {
|
|
|
|
if (tabInstance != null) {
|
2013-05-20 08:33:40 +02:00
|
|
|
int size = Util.getNotificationImageSize(this);
|
|
|
|
|
|
|
|
tabInstance.nowPlayingImage = FileUtil.getAlbumArtBitmap(this, currentPlaying.getSong(), size, true);
|
|
|
|
tabInstance.showNotification(handler, currentPlaying.getSong(), this, this.notification, this.playerState);
|
2013-05-16 09:59:55 +02:00
|
|
|
tabInstance.showNowPlaying();
|
2013-04-24 18:31:42 +02:00
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
} else {
|
2013-04-24 18:31:42 +02:00
|
|
|
if (tabInstance != null) {
|
2013-05-20 08:33:40 +02:00
|
|
|
tabInstance.nowPlayingImage = null;
|
|
|
|
tabInstance.hidePlayingNotification(handler, this);
|
2013-04-24 18:31:42 +02:00
|
|
|
tabInstance.hideNowPlaying();
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
synchronized void setNextPlaying() {
|
2013-07-17 10:59:58 +02:00
|
|
|
boolean gaplessPlayback = Util.getGaplessPlaybackPreference(DownloadServiceImpl.this);
|
|
|
|
|
|
|
|
if (!gaplessPlayback) {
|
|
|
|
nextPlaying = null;
|
|
|
|
nextPlayerState = IDLE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
int index = getCurrentPlayingIndex();
|
|
|
|
if (index != -1) {
|
|
|
|
switch (getRepeatMode()) {
|
|
|
|
case OFF:
|
|
|
|
index = index + 1;
|
|
|
|
break;
|
|
|
|
case ALL:
|
|
|
|
index = (index + 1) % size();
|
|
|
|
break;
|
|
|
|
case SINGLE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nextSetup = false;
|
|
|
|
if(nextPlayingTask != null) {
|
|
|
|
nextPlayingTask.cancel();
|
|
|
|
nextPlayingTask = null;
|
|
|
|
}
|
|
|
|
if(index < size() && index != -1) {
|
|
|
|
nextPlaying = downloadList.get(index);
|
|
|
|
nextPlayingTask = new CheckCompletionTask(nextPlaying);
|
|
|
|
nextPlayingTask.start();
|
|
|
|
} else {
|
|
|
|
nextPlaying = null;
|
|
|
|
setNextPlayerState(IDLE);
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized int getCurrentPlayingIndex() {
|
|
|
|
return downloadList.indexOf(currentPlaying);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public DownloadFile getCurrentPlaying() {
|
|
|
|
return currentPlaying;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public DownloadFile getCurrentDownloading() {
|
|
|
|
return currentDownloading;
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<DownloadFile> getSongs() {
|
|
|
|
return downloadList;
|
|
|
|
}
|
2013-05-26 07:19:28 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public long getDownloadListDuration() {
|
|
|
|
long totalDuration = 0;
|
|
|
|
|
|
|
|
for (DownloadFile downloadFile : downloadList) {
|
|
|
|
Entry entry = downloadFile.getSong();
|
|
|
|
|
|
|
|
if (!entry.isDirectory()) {
|
|
|
|
if (entry.getArtist() != null) {
|
|
|
|
Integer duration = entry.getDuration();
|
|
|
|
|
|
|
|
if (duration != null) {
|
|
|
|
totalDuration += duration;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return totalDuration;
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized List<DownloadFile> getDownloads() {
|
2013-05-16 09:59:55 +02:00
|
|
|
List<DownloadFile> temp = new ArrayList<DownloadFile>();
|
|
|
|
temp.addAll(downloadList);
|
|
|
|
temp.addAll(backgroundDownloadList);
|
|
|
|
return temp;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<DownloadFile> getBackgroundDownloads() {
|
|
|
|
return backgroundDownloadList;
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
/** Plays either the current song (resume) or the first/next one in queue. */
|
|
|
|
public synchronized void play()
|
|
|
|
{
|
|
|
|
int current = getCurrentPlayingIndex();
|
|
|
|
if (current == -1) {
|
|
|
|
play(0);
|
|
|
|
} else {
|
|
|
|
play(current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void play(int index) {
|
|
|
|
play(index, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private synchronized void play(int index, boolean start) {
|
|
|
|
if (index < 0 || index >= size()) {
|
2013-05-29 10:21:09 +02:00
|
|
|
resetPlayback();
|
2013-02-08 10:09:55 +01:00
|
|
|
} else {
|
2013-05-26 07:19:28 +02:00
|
|
|
if (nextPlayingTask != null) {
|
2013-05-16 09:59:55 +02:00
|
|
|
nextPlayingTask.cancel();
|
|
|
|
nextPlayingTask = null;
|
|
|
|
}
|
2013-05-26 07:19:28 +02:00
|
|
|
|
2013-06-07 04:27:45 +02:00
|
|
|
setCurrentPlaying(index);
|
2013-05-26 07:19:28 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
if (start) {
|
|
|
|
if (jukeboxEnabled) {
|
|
|
|
jukeboxService.skip(getCurrentPlayingIndex(), 0);
|
|
|
|
setPlayerState(STARTED);
|
|
|
|
} else {
|
|
|
|
bufferAndPlay();
|
|
|
|
}
|
|
|
|
}
|
2013-05-26 07:19:28 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
checkDownloads();
|
|
|
|
setNextPlaying();
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
2013-05-29 10:21:09 +02:00
|
|
|
private synchronized void resetPlayback() {
|
|
|
|
reset();
|
2013-06-07 04:27:45 +02:00
|
|
|
setCurrentPlaying(null);
|
2013-05-29 10:21:09 +02:00
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
private synchronized void playNext(boolean start) {
|
|
|
|
// Swap the media players since nextMediaPlayer is ready to play
|
2013-05-29 10:21:09 +02:00
|
|
|
if (start) {
|
2013-05-16 09:59:55 +02:00
|
|
|
nextMediaPlayer.start();
|
|
|
|
} else {
|
|
|
|
Log.i(TAG, "nextMediaPlayer already playing");
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
MediaPlayer tmp = mediaPlayer;
|
|
|
|
mediaPlayer = nextMediaPlayer;
|
|
|
|
nextMediaPlayer = tmp;
|
2013-06-07 04:27:45 +02:00
|
|
|
setCurrentPlaying(nextPlaying);
|
2013-05-16 09:59:55 +02:00
|
|
|
setPlayerState(PlayerState.STARTED);
|
|
|
|
setupHandlers(currentPlaying, false);
|
|
|
|
setNextPlaying();
|
|
|
|
|
|
|
|
// Proxy should not be being used here since the next player was already setup to play
|
2013-05-29 10:21:09 +02:00
|
|
|
if (proxy != null) {
|
2013-05-16 09:59:55 +02:00
|
|
|
proxy.stop();
|
|
|
|
proxy = null;
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
/** Plays or resumes the playback, depending on the current player state. */
|
2013-05-16 09:59:55 +02:00
|
|
|
public synchronized void togglePlayPause() {
|
|
|
|
if (playerState == PAUSED || playerState == COMPLETED || playerState == STOPPED) {
|
2013-02-08 10:09:55 +01:00
|
|
|
start();
|
2013-06-07 04:27:45 +02:00
|
|
|
} else if (playerState == IDLE) {
|
2013-05-16 09:59:55 +02:00
|
|
|
autoPlayStart = true;
|
2013-02-08 10:09:55 +01:00
|
|
|
play();
|
|
|
|
} else if (playerState == STARTED) {
|
|
|
|
pause();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void seekTo(int position) {
|
|
|
|
try {
|
|
|
|
if (jukeboxEnabled) {
|
|
|
|
jukeboxService.skip(getCurrentPlayingIndex(), position / 1000);
|
|
|
|
} else {
|
2013-05-16 09:59:55 +02:00
|
|
|
mediaPlayer.seekTo(position);
|
|
|
|
cachedPosition = position;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void previous() {
|
|
|
|
int index = getCurrentPlayingIndex();
|
|
|
|
if (index == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restart song if played more than five seconds.
|
|
|
|
if (getPlayerPosition() > 5000 || index == 0) {
|
|
|
|
play(index);
|
|
|
|
} else {
|
|
|
|
play(index - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void next() {
|
|
|
|
int index = getCurrentPlayingIndex();
|
|
|
|
if (index != -1) {
|
|
|
|
play(index + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onSongCompleted() {
|
|
|
|
int index = getCurrentPlayingIndex();
|
2013-05-29 10:21:09 +02:00
|
|
|
|
|
|
|
if (currentPlaying != null) {
|
|
|
|
final Entry song = currentPlaying.getSong();
|
|
|
|
|
|
|
|
if (song != null && song.getBookmarkPosition() > 0 && Util.getShouldClearBookmark(this)) {
|
|
|
|
MusicService musicService = MusicServiceFactory.getMusicService(DownloadServiceImpl.this);
|
|
|
|
try {
|
|
|
|
musicService.deleteBookmark(song.getId(), DownloadServiceImpl.this, null);
|
2013-06-07 04:27:45 +02:00
|
|
|
} catch (Exception ignored) {
|
2013-05-29 10:21:09 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
if (index != -1) {
|
|
|
|
switch (getRepeatMode()) {
|
|
|
|
case OFF:
|
2013-05-29 10:21:09 +02:00
|
|
|
if (index + 1 < 0 || index + 1 >= size()) {
|
|
|
|
if (Util.getShouldClearPlaylist(this)) {
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
resetPlayback();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
play(index + 1);
|
|
|
|
break;
|
|
|
|
case ALL:
|
|
|
|
play((index + 1) % size());
|
|
|
|
break;
|
|
|
|
case SINGLE:
|
|
|
|
play(index);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void pause() {
|
|
|
|
try {
|
|
|
|
if (playerState == STARTED) {
|
|
|
|
if (jukeboxEnabled) {
|
|
|
|
jukeboxService.stop();
|
|
|
|
} else {
|
|
|
|
mediaPlayer.pause();
|
|
|
|
}
|
|
|
|
setPlayerState(PAUSED);
|
|
|
|
}
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
@Override
|
|
|
|
public synchronized void stop() {
|
|
|
|
try {
|
|
|
|
if (playerState == STARTED) {
|
2013-02-08 10:09:55 +01:00
|
|
|
if (jukeboxEnabled) {
|
|
|
|
jukeboxService.stop();
|
|
|
|
} else {
|
|
|
|
mediaPlayer.pause();
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
setPlayerState(STOPPED);
|
|
|
|
} else if(playerState == PAUSED) {
|
|
|
|
setPlayerState(STOPPED);
|
|
|
|
}
|
|
|
|
} catch(Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void start() {
|
|
|
|
try {
|
|
|
|
if (jukeboxEnabled) {
|
|
|
|
jukeboxService.start();
|
|
|
|
} else {
|
|
|
|
mediaPlayer.start();
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
setPlayerState(STARTED);
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void reset() {
|
|
|
|
if (bufferTask != null) {
|
|
|
|
bufferTask.cancel();
|
|
|
|
}
|
|
|
|
try {
|
2013-05-16 09:59:55 +02:00
|
|
|
setPlayerState(IDLE);
|
|
|
|
mediaPlayer.setOnErrorListener(null);
|
|
|
|
mediaPlayer.setOnCompletionListener(null);
|
2013-02-08 10:09:55 +01:00
|
|
|
mediaPlayer.reset();
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized int getPlayerPosition() {
|
|
|
|
try {
|
|
|
|
if (playerState == IDLE || playerState == DOWNLOADING || playerState == PREPARING) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (jukeboxEnabled) {
|
2013-05-16 09:59:55 +02:00
|
|
|
return jukeboxService.getPositionSeconds() * 1000;
|
2013-02-08 10:09:55 +01:00
|
|
|
} else {
|
2013-05-16 09:59:55 +02:00
|
|
|
return cachedPosition;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized int getPlayerDuration() {
|
|
|
|
if (currentPlaying != null) {
|
|
|
|
Integer duration = currentPlaying.getSong().getDuration();
|
|
|
|
if (duration != null) {
|
|
|
|
return duration * 1000;
|
|
|
|
}
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
if (playerState != IDLE && playerState != DOWNLOADING && playerState != PlayerState.PREPARING) {
|
2013-02-08 10:09:55 +01:00
|
|
|
try {
|
|
|
|
return mediaPlayer.getDuration();
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public PlayerState getPlayerState() {
|
|
|
|
return playerState;
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
synchronized void setPlayerState(PlayerState playerState) {
|
|
|
|
Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")");
|
|
|
|
|
2013-04-18 08:18:59 +02:00
|
|
|
this.playerState = playerState;
|
|
|
|
|
|
|
|
if (this.playerState == PAUSED) {
|
2013-02-08 10:09:55 +01:00
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
}
|
2013-02-23 02:23:41 +01:00
|
|
|
|
2013-04-09 08:01:45 +02:00
|
|
|
if (this.playerState == PlayerState.STARTED) {
|
2013-05-20 08:33:40 +02:00
|
|
|
Util.requestAudioFocus(this);
|
2013-04-09 08:01:45 +02:00
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-04-18 08:18:59 +02:00
|
|
|
boolean showWhenPaused = (this.playerState == PlayerState.PAUSED && Util.isNotificationAlwaysEnabled(this));
|
|
|
|
boolean show = this.playerState == PlayerState.STARTED || showWhenPaused;
|
2013-06-07 04:27:45 +02:00
|
|
|
|
2013-04-18 08:18:59 +02:00
|
|
|
Util.broadcastPlaybackStatusChange(this, this.playerState);
|
|
|
|
Util.broadcastA2dpPlayStatusChange(this, this.playerState, getInstance());
|
|
|
|
|
|
|
|
// Set remote control
|
2013-04-19 01:21:24 +02:00
|
|
|
updateRemoteControl();
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-02-23 03:59:40 +01:00
|
|
|
// Update widget
|
2013-05-26 07:19:28 +02:00
|
|
|
UltraSonicAppWidgetProvider4x1.getInstance().notifyChange(this, this, this.playerState == PlayerState.STARTED, false);
|
|
|
|
UltraSonicAppWidgetProvider4x2.getInstance().notifyChange(this, this, this.playerState == PlayerState.STARTED, true);
|
|
|
|
UltraSonicAppWidgetProvider4x3.getInstance().notifyChange(this, this, this.playerState == PlayerState.STARTED, false);
|
|
|
|
UltraSonicAppWidgetProvider4x4.getInstance().notifyChange(this, this, this.playerState == PlayerState.STARTED, false);
|
2013-05-20 08:33:40 +02:00
|
|
|
SubsonicTabActivity tabInstance = SubsonicTabActivity.getInstance();
|
2013-07-18 09:58:59 +02:00
|
|
|
|
|
|
|
Entry song = null;
|
|
|
|
|
|
|
|
if (currentPlaying != null) {
|
|
|
|
song = currentPlaying.getSong();
|
|
|
|
}
|
|
|
|
|
2013-02-23 03:59:40 +01:00
|
|
|
if (show) {
|
2013-05-20 08:33:40 +02:00
|
|
|
int size = Util.getNotificationImageSize(this);
|
2013-07-18 09:58:59 +02:00
|
|
|
Bitmap bitmap = FileUtil.getAlbumArtBitmap(this, song , size, true);
|
2013-06-07 04:27:45 +02:00
|
|
|
|
|
|
|
if (tabInstance != null) {
|
|
|
|
tabInstance.nowPlayingImage = bitmap;
|
2013-07-18 09:58:59 +02:00
|
|
|
tabInstance.showNotification(handler, song, this, this.notification, this.playerState);
|
2013-06-07 04:27:45 +02:00
|
|
|
tabInstance.showNowPlaying();
|
|
|
|
}
|
|
|
|
} else {
|
2013-04-24 18:31:42 +02:00
|
|
|
if (tabInstance != null) {
|
2013-05-20 08:33:40 +02:00
|
|
|
tabInstance.nowPlayingImage = null;
|
|
|
|
tabInstance.hidePlayingNotification(handler, this);
|
2013-04-24 18:31:42 +02:00
|
|
|
tabInstance.hideNowPlaying();
|
|
|
|
}
|
2013-02-23 03:59:40 +01:00
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-04-18 08:18:59 +02:00
|
|
|
if (this.playerState == STARTED) {
|
2013-02-08 10:09:55 +01:00
|
|
|
scrobbler.scrobble(this, currentPlaying, false);
|
2013-04-18 08:18:59 +02:00
|
|
|
} else if (this.playerState == COMPLETED) {
|
2013-02-08 10:09:55 +01:00
|
|
|
scrobbler.scrobble(this, currentPlaying, true);
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
if (playerState == STARTED && positionCache == null) {
|
|
|
|
positionCache = new PositionCache();
|
|
|
|
Thread thread = new Thread(positionCache);
|
|
|
|
thread.start();
|
|
|
|
} else if (playerState != STARTED && positionCache != null) {
|
|
|
|
positionCache.stop();
|
|
|
|
positionCache = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setPlayerStateCompleted() {
|
|
|
|
Log.i(TAG, this.playerState.name() + " -> " + PlayerState.COMPLETED + " (" + currentPlaying + ")");
|
|
|
|
this.playerState = PlayerState.COMPLETED;
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
if(positionCache != null) {
|
|
|
|
positionCache.stop();
|
|
|
|
positionCache = null;
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
scrobbler.scrobble(this, currentPlaying, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private synchronized void setNextPlayerState(PlayerState playerState) {
|
|
|
|
Log.i(TAG, "Next: " + this.nextPlayerState.name() + " -> " + playerState.name() + " (" + nextPlaying + ")");
|
|
|
|
this.nextPlayerState = playerState;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setSuggestedPlaylistName(String name) {
|
|
|
|
this.suggestedPlaylistName = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getSuggestedPlaylistName() {
|
|
|
|
return suggestedPlaylistName;
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
@Override
|
|
|
|
public boolean getEqualizerAvailable() {
|
|
|
|
return equalizerAvailable;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean getVisualizerAvailable() {
|
|
|
|
return visualizerAvailable;
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
@Override
|
|
|
|
public EqualizerController getEqualizerController() {
|
2013-05-16 09:59:55 +02:00
|
|
|
if (equalizerAvailable && equalizerController == null) {
|
|
|
|
equalizerController = new EqualizerController(this, mediaPlayer);
|
|
|
|
if (!equalizerController.isAvailable()) {
|
|
|
|
equalizerController = null;
|
|
|
|
} else {
|
|
|
|
equalizerController.loadSettings();
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
return equalizerController;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public VisualizerController getVisualizerController() {
|
2013-05-16 09:59:55 +02:00
|
|
|
if (visualizerAvailable && visualizerController == null) {
|
2013-06-07 04:27:45 +02:00
|
|
|
visualizerController = new VisualizerController(mediaPlayer);
|
2013-05-16 09:59:55 +02:00
|
|
|
if (!visualizerController.isAvailable()) {
|
|
|
|
visualizerController = null;
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
return visualizerController;
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
@Override
|
|
|
|
public boolean isJukeboxEnabled() {
|
|
|
|
return jukeboxEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
@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 void adjustJukeboxVolume(boolean up) {
|
|
|
|
jukeboxService.adjustVolume(up);
|
|
|
|
}
|
|
|
|
|
2013-04-18 08:18:59 +02:00
|
|
|
private void updateRemoteControl() {
|
2013-02-08 10:09:55 +01:00
|
|
|
if (Util.isLockScreenEnabled(this)) {
|
2013-02-23 02:23:41 +01:00
|
|
|
if (remoteControlClient == null) {
|
2013-02-08 10:09:55 +01:00
|
|
|
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
|
|
|
intent.setComponent(new ComponentName(this.getPackageName(), MediaButtonIntentReceiver.class.getName()));
|
2013-02-23 02:23:41 +01:00
|
|
|
remoteControlClient = new RemoteControlClient(PendingIntent.getBroadcast(this, 0, intent, 0));
|
2013-05-20 08:33:40 +02:00
|
|
|
|
|
|
|
remoteControlClient.setTransportControlFlags(
|
|
|
|
RemoteControlClient.FLAG_KEY_MEDIA_PLAY |
|
|
|
|
RemoteControlClient.FLAG_KEY_MEDIA_PAUSE |
|
|
|
|
RemoteControlClient.FLAG_KEY_MEDIA_NEXT |
|
|
|
|
RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS |
|
|
|
|
RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE |
|
|
|
|
RemoteControlClient.FLAG_KEY_MEDIA_STOP);
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
2013-03-10 18:38:47 +01:00
|
|
|
|
|
|
|
audioManager.registerRemoteControlClient(remoteControlClient);
|
|
|
|
|
2013-02-21 03:24:16 +01:00
|
|
|
switch (playerState)
|
|
|
|
{
|
|
|
|
case STARTED:
|
2013-02-23 02:23:41 +01:00
|
|
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
|
2013-02-21 03:24:16 +01:00
|
|
|
break;
|
|
|
|
case PAUSED:
|
2013-02-23 02:23:41 +01:00
|
|
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
|
2013-02-21 03:24:16 +01:00
|
|
|
break;
|
|
|
|
case IDLE:
|
2013-05-20 08:33:40 +02:00
|
|
|
case COMPLETED:
|
2013-02-21 03:24:16 +01:00
|
|
|
case STOPPED:
|
2013-02-23 02:23:41 +01:00
|
|
|
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
|
2013-02-21 03:24:16 +01:00
|
|
|
break;
|
2013-05-20 08:33:40 +02:00
|
|
|
case DOWNLOADING:
|
|
|
|
case PREPARED:
|
|
|
|
case PREPARING:
|
|
|
|
default:
|
|
|
|
break;
|
2013-02-21 03:24:16 +01:00
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
try {
|
2013-03-10 18:38:47 +01:00
|
|
|
if (currentPlaying != null) {
|
2013-06-07 10:47:57 +02:00
|
|
|
Bitmap lockScreenBitmap;
|
|
|
|
if (currentSong != currentPlaying.getSong()) {
|
2013-03-10 18:38:47 +01:00
|
|
|
currentSong = currentPlaying.getSong();
|
2013-06-07 04:27:45 +02:00
|
|
|
lockScreenBitmap = FileUtil.getAlbumArtBitmap(this, currentSong, lockScreenBitmapSize, true);
|
2013-05-20 08:33:40 +02:00
|
|
|
} else {
|
|
|
|
return;
|
2013-03-10 18:38:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
String artist = currentSong.getArtist();
|
|
|
|
String album = currentSong.getAlbum();
|
|
|
|
String title = artist + " - " + currentSong.getTitle();
|
|
|
|
Integer duration = currentSong.getDuration();
|
|
|
|
|
|
|
|
// Update the remote controls
|
|
|
|
remoteControlClient
|
|
|
|
.editMetadata(true)
|
|
|
|
.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, title)
|
|
|
|
.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, artist)
|
|
|
|
.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, album)
|
|
|
|
.apply();
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-04-28 20:37:40 +02:00
|
|
|
if (duration != null) {
|
|
|
|
remoteControlClient
|
|
|
|
.editMetadata(false)
|
|
|
|
.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration)
|
|
|
|
.apply();
|
|
|
|
}
|
2013-06-07 04:27:45 +02:00
|
|
|
|
|
|
|
remoteControlClient
|
|
|
|
.editMetadata(false)
|
|
|
|
.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, lockScreenBitmap)
|
|
|
|
.apply();
|
2013-03-10 18:38:47 +01:00
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
catch (Exception e) {
|
|
|
|
Log.e(TAG, "Exception in setRemoteControl", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private synchronized void bufferAndPlay() {
|
2013-05-16 09:59:55 +02:00
|
|
|
if(playerState != PREPARED) {
|
|
|
|
reset();
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
bufferTask = new BufferTask(currentPlaying, 0);
|
|
|
|
bufferTask.start();
|
|
|
|
} else {
|
|
|
|
doPlay(currentPlaying, 0, true);
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
2013-02-11 05:30:46 +01:00
|
|
|
private synchronized void doPlay(final DownloadFile downloadFile, final int position, final boolean start) {
|
2013-02-08 10:09:55 +01:00
|
|
|
try {
|
2013-06-07 10:47:57 +02:00
|
|
|
downloadFile.setPlaying(false);
|
|
|
|
//downloadFile.setPlaying(true);
|
2013-02-08 10:09:55 +01:00
|
|
|
final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
|
2013-06-07 04:27:45 +02:00
|
|
|
boolean partial = file.equals(downloadFile.getPartialFile());
|
2013-02-08 10:09:55 +01:00
|
|
|
downloadFile.updateModificationDate();
|
2013-03-10 18:38:47 +01:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
mediaPlayer.setOnCompletionListener(null);
|
|
|
|
secondaryProgress = -1; // Ensure seeking in non StreamProxy playback works
|
|
|
|
mediaPlayer.reset();
|
|
|
|
setPlayerState(IDLE);
|
|
|
|
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
|
|
|
String dataSource = file.getPath();
|
|
|
|
|
2013-06-07 10:47:57 +02:00
|
|
|
if (partial) {
|
2013-05-16 09:59:55 +02:00
|
|
|
if (proxy == null) {
|
|
|
|
proxy = new StreamProxy(this);
|
|
|
|
proxy.start();
|
|
|
|
}
|
2013-06-07 10:47:57 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
dataSource = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), URLEncoder.encode(dataSource, Constants.UTF_8));
|
|
|
|
Log.i(TAG, "Data Source: " + dataSource);
|
2013-06-07 10:47:57 +02:00
|
|
|
} else if (proxy != null) {
|
2013-05-16 09:59:55 +02:00
|
|
|
proxy.stop();
|
|
|
|
proxy = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.i(TAG, "Preparing media player");
|
|
|
|
mediaPlayer.setDataSource(dataSource);
|
|
|
|
setPlayerState(PREPARING);
|
2013-02-23 05:45:10 +01:00
|
|
|
|
2013-02-10 18:44:57 +01:00
|
|
|
mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
|
|
|
|
@Override
|
|
|
|
public void onBufferingUpdate(MediaPlayer mp, int percent) {
|
2013-05-16 09:59:55 +02:00
|
|
|
if (percent == 100) {
|
|
|
|
mediaPlayer.setOnBufferingUpdateListener(null);
|
|
|
|
}
|
|
|
|
|
2013-02-10 18:44:57 +01:00
|
|
|
SeekBar progressBar = DownloadActivity.getProgressBar();
|
2013-02-11 05:30:46 +01:00
|
|
|
MusicDirectory.Entry song = downloadFile.getSong();
|
|
|
|
|
2013-06-07 04:27:45 +02:00
|
|
|
if (progressBar != null && song.getTranscodedContentType() == null && Util.getMaxBitRate(getApplicationContext()) == 0) {
|
2013-02-23 05:45:10 +01:00
|
|
|
secondaryProgress = (int) (((double)percent / (double)100) * progressBar.getMax());
|
2013-04-06 21:47:24 +02:00
|
|
|
progressBar.setSecondaryProgress(secondaryProgress);
|
2013-02-10 18:44:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-02-11 05:30:46 +01:00
|
|
|
|
2013-04-06 21:47:24 +02:00
|
|
|
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
|
|
|
|
@Override
|
|
|
|
public void onPrepared(MediaPlayer mp) {
|
2013-04-19 01:21:24 +02:00
|
|
|
Log.i(TAG, "Media player prepared");
|
|
|
|
|
2013-04-06 21:47:24 +02:00
|
|
|
setPlayerState(PREPARED);
|
|
|
|
|
|
|
|
SeekBar progressBar = DownloadActivity.getProgressBar();
|
|
|
|
|
2013-05-29 10:21:09 +02:00
|
|
|
if (progressBar != null && downloadFile.isWorkDone()) {
|
2013-04-06 21:47:24 +02:00
|
|
|
// Populate seek bar secondary progress if we have a complete file for consistency
|
|
|
|
DownloadActivity.getProgressBar().setSecondaryProgress(100 * progressBar.getMax());
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
synchronized (DownloadServiceImpl.this) {
|
|
|
|
if (position != 0) {
|
|
|
|
Log.i(TAG, "Restarting player from position " + position);
|
|
|
|
mediaPlayer.seekTo(position);
|
|
|
|
}
|
|
|
|
cachedPosition = position;
|
|
|
|
|
|
|
|
if (start) {
|
|
|
|
mediaPlayer.start();
|
|
|
|
setPlayerState(STARTED);
|
|
|
|
} else {
|
|
|
|
setPlayerState(PAUSED);
|
|
|
|
}
|
|
|
|
}
|
2013-04-06 21:47:24 +02:00
|
|
|
|
|
|
|
lifecycleSupport.serializeDownloadQueue();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-06-07 04:27:45 +02:00
|
|
|
setupHandlers(downloadFile, partial);
|
2013-04-19 01:21:24 +02:00
|
|
|
|
2013-04-06 21:47:24 +02:00
|
|
|
mediaPlayer.prepareAsync();
|
2013-02-08 10:09:55 +01:00
|
|
|
} catch (Exception x) {
|
|
|
|
handleError(x);
|
|
|
|
}
|
|
|
|
}
|
2013-04-06 21:47:24 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
private synchronized void setupNext(final DownloadFile downloadFile) {
|
|
|
|
try {
|
|
|
|
final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-06-07 10:47:57 +02:00
|
|
|
if (nextMediaPlayer != null) {
|
2013-05-16 09:59:55 +02:00
|
|
|
nextMediaPlayer.setOnCompletionListener(null);
|
|
|
|
nextMediaPlayer.release();
|
|
|
|
nextMediaPlayer = null;
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
nextMediaPlayer = new MediaPlayer();
|
|
|
|
nextMediaPlayer.setWakeMode(DownloadServiceImpl.this, PowerManager.PARTIAL_WAKE_LOCK);
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
try {
|
|
|
|
nextMediaPlayer.setAudioSessionId(mediaPlayer.getAudioSessionId());
|
|
|
|
} catch(Throwable e) {
|
|
|
|
nextMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
nextMediaPlayer.setDataSource(file.getPath());
|
|
|
|
setNextPlayerState(PREPARING);
|
|
|
|
|
|
|
|
nextMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
|
|
|
|
@SuppressLint("NewApi")
|
|
|
|
public void onPrepared(MediaPlayer mp) {
|
|
|
|
try {
|
|
|
|
setNextPlayerState(PREPARED);
|
|
|
|
|
2013-07-17 10:59:58 +02:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED)) {
|
2013-05-16 09:59:55 +02:00
|
|
|
mediaPlayer.setNextMediaPlayer(nextMediaPlayer);
|
|
|
|
nextSetup = true;
|
|
|
|
}
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleErrorNext(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
nextMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
|
|
|
public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
|
|
|
|
Log.w(TAG, "Error on playing next " + "(" + what + ", " + extra + "): " + downloadFile);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
nextMediaPlayer.prepareAsync();
|
|
|
|
} catch (Exception x) {
|
|
|
|
handleErrorNext(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setupHandlers(final DownloadFile downloadFile, final boolean isPartial) {
|
|
|
|
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
|
|
|
|
public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
|
|
|
|
Log.w(TAG, "Error on playing file " + "(" + what + ", " + extra + "): " + downloadFile);
|
|
|
|
int pos = cachedPosition;
|
|
|
|
reset();
|
|
|
|
downloadFile.setPlaying(false);
|
|
|
|
doPlay(downloadFile, pos, true);
|
|
|
|
downloadFile.setPlaying(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
final int duration = downloadFile.getSong().getDuration() == null ? 0 : downloadFile.getSong().getDuration() * 1000;
|
|
|
|
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);
|
|
|
|
|
|
|
|
setPlayerStateCompleted();
|
|
|
|
|
|
|
|
int pos = cachedPosition;
|
|
|
|
Log.i(TAG, "Ending position " + pos + " of " + duration);
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) {
|
2013-06-07 10:47:57 +02:00
|
|
|
if (nextPlaying != null && nextPlayerState == PlayerState.PREPARED) {
|
|
|
|
if (!nextSetup) {
|
2013-05-16 09:59:55 +02:00
|
|
|
playNext(true);
|
|
|
|
} else {
|
|
|
|
nextSetup = false;
|
|
|
|
playNext(false);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
onSongCompleted();
|
|
|
|
}
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronized (DownloadServiceImpl.this) {
|
2013-06-07 04:27:45 +02:00
|
|
|
if (downloadFile.isWorkDone()) {
|
2013-05-16 09:59:55 +02:00
|
|
|
// Complete was called early even though file is fully buffered
|
|
|
|
Log.i(TAG, "Requesting restart from " + pos + " of " + duration);
|
|
|
|
reset();
|
|
|
|
downloadFile.setPlaying(false);
|
|
|
|
doPlay(downloadFile, pos, true);
|
|
|
|
downloadFile.setPlaying(true);
|
|
|
|
} else {
|
|
|
|
Log.i(TAG, "Requesting restart from " + pos + " of " + duration);
|
|
|
|
reset();
|
|
|
|
bufferTask = new BufferTask(downloadFile, pos);
|
|
|
|
bufferTask.start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setVolume(float volume) {
|
|
|
|
if(mediaPlayer != null) {
|
|
|
|
mediaPlayer.setVolume(volume, volume);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public synchronized void swap(boolean mainList, int from, int to) {
|
|
|
|
List<DownloadFile> list = mainList ? downloadList : backgroundDownloadList;
|
|
|
|
int max = list.size();
|
|
|
|
if(to >= max) {
|
|
|
|
to = max - 1;
|
|
|
|
}
|
|
|
|
else if(to < 0) {
|
|
|
|
to = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int currentPlayingIndex = getCurrentPlayingIndex();
|
|
|
|
DownloadFile movedSong = list.remove(from);
|
|
|
|
list.add(to, movedSong);
|
|
|
|
if(jukeboxEnabled && mainList) {
|
|
|
|
updateJukeboxPlaylist();
|
|
|
|
} else if(mainList && (movedSong == nextPlaying || (currentPlayingIndex + 1) == to)) {
|
|
|
|
// Moving next playing or moving a song to be next playing
|
|
|
|
setNextPlaying();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
private void handleError(Exception x) {
|
|
|
|
Log.w(TAG, "Media player error: " + x, x);
|
|
|
|
mediaPlayer.reset();
|
|
|
|
setPlayerState(IDLE);
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
private void handleErrorNext(Exception x) {
|
|
|
|
Log.w(TAG, "Next Media player error: " + x, x);
|
|
|
|
nextMediaPlayer.reset();
|
|
|
|
setNextPlayerState(IDLE);
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
protected synchronized void checkDownloads() {
|
|
|
|
if (!Util.isExternalStoragePresent() || !lifecycleSupport.isExternalStorageAvailable()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shufflePlay) {
|
|
|
|
checkShufflePlay();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jukeboxEnabled || !Util.isNetworkConnected(this)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
if (downloadList.isEmpty() && backgroundDownloadList.isEmpty()) {
|
2013-02-08 10:09:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2013-06-07 10:47:57 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
// Need to download current playing?
|
2013-05-16 09:59:55 +02:00
|
|
|
if (currentPlaying != null && currentPlaying != currentDownloading && !currentPlaying.isWorkDone()) {
|
2013-02-08 10:09:55 +01:00
|
|
|
// Cancel current download, if necessary.
|
|
|
|
if (currentDownloading != null) {
|
|
|
|
currentDownloading.cancelDownload();
|
|
|
|
}
|
|
|
|
|
|
|
|
currentDownloading = currentPlaying;
|
|
|
|
currentDownloading.download();
|
|
|
|
cleanupCandidates.add(currentDownloading);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find a suitable target for download.
|
2013-05-16 09:59:55 +02:00
|
|
|
else if (currentDownloading == null || currentDownloading.isWorkDone() || currentDownloading.isFailed() && (!downloadList.isEmpty() || !backgroundDownloadList.isEmpty())) {
|
2013-06-07 10:47:57 +02:00
|
|
|
currentDownloading = null;
|
2013-02-08 10:09:55 +01:00
|
|
|
int n = size();
|
|
|
|
|
|
|
|
int preloaded = 0;
|
|
|
|
|
2013-06-07 10:47:57 +02:00
|
|
|
if(n != 0) {
|
|
|
|
int start = currentPlaying == null ? 0 : getCurrentPlayingIndex();
|
|
|
|
if (start == -1) {
|
|
|
|
start = 0;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
if(i == (start + 1)) {
|
|
|
|
setNextPlayerState(DOWNLOADING);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (currentPlaying != downloadFile) {
|
|
|
|
preloaded++;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = (i + 1) % n;
|
|
|
|
} while (i != start);
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
2013-06-07 10:47:57 +02:00
|
|
|
if ((preloaded + 1 == n || preloaded >= Util.getPreloadCount(this) || downloadList.isEmpty()) && !backgroundDownloadList.isEmpty()) {
|
|
|
|
for (int i = 0; i < backgroundDownloadList.size(); i++) {
|
|
|
|
DownloadFile downloadFile = backgroundDownloadList.get(i);
|
|
|
|
if (downloadFile.isWorkDone() && (!downloadFile.shouldSave() || downloadFile.isSaved())) {
|
|
|
|
// Don't need to keep list like active song list
|
2013-07-17 10:59:58 +02:00
|
|
|
backgroundDownloadList.remove(i);
|
|
|
|
revision++;
|
2013-06-07 10:47:57 +02:00
|
|
|
i--;
|
|
|
|
} else {
|
|
|
|
currentDownloading = downloadFile;
|
|
|
|
currentDownloading.download();
|
|
|
|
cleanupCandidates.add(currentDownloading);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete obsolete .partial and .complete files.
|
|
|
|
cleanup();
|
|
|
|
}
|
2013-06-07 10:47:57 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
private synchronized void checkShufflePlay() {
|
2013-05-16 09:59:55 +02:00
|
|
|
// Get users desired random playlist size
|
|
|
|
int listSize = Util.getMaxSongs(this);
|
|
|
|
boolean wasEmpty = downloadList.isEmpty();
|
|
|
|
|
|
|
|
long revisionBefore = revision;
|
|
|
|
|
|
|
|
// First, ensure that list is at least 20 songs long.
|
|
|
|
int size = size();
|
|
|
|
if (size < listSize) {
|
|
|
|
for (MusicDirectory.Entry song : shufflePlayBuffer.get(listSize - size)) {
|
|
|
|
DownloadFile downloadFile = new DownloadFile(this, song, false);
|
|
|
|
downloadList.add(downloadFile);
|
|
|
|
revision++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int currIndex = currentPlaying == null ? 0 : getCurrentPlayingIndex();
|
|
|
|
|
|
|
|
// Only shift playlist if playing song #5 or later.
|
|
|
|
if (currIndex > 4) {
|
|
|
|
int songsToShift = currIndex - 2;
|
|
|
|
for (MusicDirectory.Entry song : shufflePlayBuffer.get(songsToShift)) {
|
|
|
|
downloadList.add(new DownloadFile(this, song, false));
|
|
|
|
downloadList.get(0).cancelDownload();
|
|
|
|
downloadList.remove(0);
|
|
|
|
revision++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (revisionBefore != revision) {
|
|
|
|
updateJukeboxPlaylist();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wasEmpty && !downloadList.isEmpty()) {
|
|
|
|
play(0);
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
|
|
|
|
public long getDownloadListUpdateRevision() {
|
|
|
|
return revision;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class BufferTask extends CancellableTask {
|
|
|
|
private final DownloadFile downloadFile;
|
|
|
|
private final int position;
|
|
|
|
private final long expectedFileSize;
|
|
|
|
private final File partialFile;
|
|
|
|
|
|
|
|
public BufferTask(DownloadFile downloadFile, int position) {
|
2013-04-20 23:58:59 +02:00
|
|
|
this.downloadFile = downloadFile;
|
|
|
|
this.position = position;
|
|
|
|
partialFile = downloadFile.getPartialFile();
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
long bufferLength = Util.getBufferLength(DownloadServiceImpl.this);
|
2013-04-20 23:58:59 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
if(bufferLength == 0) {
|
|
|
|
// Set to seconds in a day, basically infinity
|
|
|
|
bufferLength = 86400L;
|
2013-04-20 23:58:59 +02:00
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
// Calculate roughly how many bytes BUFFER_LENGTH_SECONDS corresponds to.
|
|
|
|
int bitRate = downloadFile.getBitRate();
|
|
|
|
long byteCount = Math.max(100000, bitRate * 1024L / 8L * bufferLength);
|
|
|
|
|
|
|
|
// Find out how large the file should grow before resuming playback.
|
|
|
|
Log.i(TAG, "Buffering from position " + position + " and bitrate " + bitRate);
|
|
|
|
expectedFileSize = (position * bitRate / 8) + byteCount;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-05-16 09:59:55 +02:00
|
|
|
public void execute() {
|
|
|
|
setPlayerState(DOWNLOADING);
|
2013-04-20 23:58:59 +02:00
|
|
|
|
2013-05-24 10:16:48 +02:00
|
|
|
while (!bufferComplete() && !Util.isOffline(DownloadServiceImpl.this)) {
|
2013-05-16 09:59:55 +02:00
|
|
|
Util.sleepQuietly(1000L);
|
|
|
|
if (isCancelled()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-02-08 10:09:55 +01:00
|
|
|
doPlay(downloadFile, position, true);
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
private boolean bufferComplete() {
|
|
|
|
boolean completeFileAvailable = downloadFile.isWorkDone();
|
|
|
|
long size = partialFile.length();
|
|
|
|
|
|
|
|
Log.i(TAG, "Buffering " + partialFile + " (" + size + "/" + expectedFileSize + ", " + completeFileAvailable + ")");
|
|
|
|
return completeFileAvailable || size >= expectedFileSize;
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return "BufferTask (" + downloadFile + ")";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class PositionCache implements Runnable {
|
|
|
|
boolean isRunning = true;
|
|
|
|
|
|
|
|
public void stop() {
|
|
|
|
isRunning = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
// Stop checking position before the song reaches completion
|
|
|
|
while(isRunning) {
|
|
|
|
try {
|
|
|
|
if(mediaPlayer != null && playerState == STARTED) {
|
|
|
|
cachedPosition = mediaPlayer.getCurrentPosition();
|
|
|
|
}
|
|
|
|
Thread.sleep(200L);
|
|
|
|
}
|
|
|
|
catch(Exception e) {
|
|
|
|
Log.w(TAG, "Crashed getting current position", e);
|
|
|
|
isRunning = false;
|
|
|
|
positionCache = null;
|
2013-04-09 08:01:45 +02:00
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class CheckCompletionTask extends CancellableTask {
|
|
|
|
private final DownloadFile downloadFile;
|
|
|
|
private final File partialFile;
|
2013-04-09 08:01:45 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
public CheckCompletionTask(DownloadFile downloadFile) {
|
|
|
|
setNextPlayerState(PlayerState.IDLE);
|
|
|
|
this.downloadFile = downloadFile;
|
|
|
|
if(downloadFile != null) {
|
|
|
|
partialFile = downloadFile.getPartialFile();
|
2013-04-09 08:01:45 +02:00
|
|
|
} else {
|
2013-05-16 09:59:55 +02:00
|
|
|
partialFile = null;
|
2013-04-09 08:01:45 +02:00
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void execute() {
|
|
|
|
if(downloadFile == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do an initial sleep so this prepare can't compete with main prepare
|
|
|
|
Util.sleepQuietly(5000L);
|
|
|
|
while (!bufferComplete()) {
|
|
|
|
Util.sleepQuietly(5000L);
|
|
|
|
if (isCancelled()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the setup of the next media player
|
|
|
|
mediaPlayerHandler.post(new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
setupNext(downloadFile);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean bufferComplete() {
|
|
|
|
boolean completeFileAvailable = downloadFile.isWorkDone();
|
|
|
|
Log.i(TAG, "Buffering next " + partialFile + " (" + partialFile.length() + ")");
|
|
|
|
return completeFileAvailable && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED);
|
|
|
|
}
|
2013-04-09 08:01:45 +02:00
|
|
|
|
2013-02-08 10:09:55 +01:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
2013-05-16 09:59:55 +02:00
|
|
|
return "CheckCompletionTask (" + downloadFile + ")";
|
2013-02-08 10:09:55 +01:00
|
|
|
}
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|