From 0f974c59d5c7253dee643513bb4c363554feec5c Mon Sep 17 00:00:00 2001
From: Andrew Rabert
Date: Sun, 12 Mar 2017 14:40:13 -0400
Subject: [PATCH] Stream media with OkHttp
---
.../audinaut/service/CachedMusicService.java | 4 +-
.../audinaut/service/DownloadFile.java | 23 +-
.../audinaut/service/DownloadService.java | 3736 ++++++++---------
.../audinaut/service/MusicService.java | 4 +-
.../audinaut/service/OfflineMusicService.java | 4 +-
.../audinaut/service/RESTMusicService.java | 280 +-
.../service/ssl/SSLSocketFactory.java | 553 ---
.../service/ssl/TrustManagerDecorator.java | 65 -
.../service/ssl/TrustSelfSignedStrategy.java | 44 -
.../audinaut/service/ssl/TrustStrategy.java | 57 -
.../java/net/nullsum/audinaut/util/Util.java | 2 -
11 files changed, 1903 insertions(+), 2869 deletions(-)
delete mode 100644 app/src/main/java/net/nullsum/audinaut/service/ssl/SSLSocketFactory.java
delete mode 100644 app/src/main/java/net/nullsum/audinaut/service/ssl/TrustManagerDecorator.java
delete mode 100644 app/src/main/java/net/nullsum/audinaut/service/ssl/TrustSelfSignedStrategy.java
delete mode 100644 app/src/main/java/net/nullsum/audinaut/service/ssl/TrustStrategy.java
diff --git a/app/src/main/java/net/nullsum/audinaut/service/CachedMusicService.java b/app/src/main/java/net/nullsum/audinaut/service/CachedMusicService.java
index 578e7b4..74b0ebc 100644
--- a/app/src/main/java/net/nullsum/audinaut/service/CachedMusicService.java
+++ b/app/src/main/java/net/nullsum/audinaut/service/CachedMusicService.java
@@ -27,7 +27,7 @@ import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.TimeUnit;
-import org.apache.http.HttpResponse;
+import okhttp3.Response;
import android.content.Context;
import android.graphics.Bitmap;
@@ -607,7 +607,7 @@ public class CachedMusicService implements MusicService {
}
@Override
- public HttpResponse getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
+ public Response getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task);
}
diff --git a/app/src/main/java/net/nullsum/audinaut/service/DownloadFile.java b/app/src/main/java/net/nullsum/audinaut/service/DownloadFile.java
index 204c04a..679d2a6 100644
--- a/app/src/main/java/net/nullsum/audinaut/service/DownloadFile.java
+++ b/app/src/main/java/net/nullsum/audinaut/service/DownloadFile.java
@@ -38,10 +38,7 @@ import net.nullsum.audinaut.util.FileUtil;
import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.Util;
-import org.apache.http.Header;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
+import okhttp3.Response;
/**
* @author Sindre Mehus
@@ -454,21 +451,19 @@ public class DownloadFile implements BufferFile {
}
if(compare) {
// Attempt partial HTTP GET, appending to the file if it exists.
- HttpResponse response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this);
- Header contentLengthHeader = response.getFirstHeader("Content-Length");
- if(contentLengthHeader != null) {
- String contentLengthString = contentLengthHeader.getValue();
- if(contentLengthString != null) {
- Log.i(TAG, "Content Length: " + contentLengthString);
- contentLength = Long.parseLong(contentLengthString);
- }
+ Response response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this);
+ if(response.header("Content-Length") != null) {
+ Log.i(TAG, "Content Length: " + contentLength);
+ contentLength = contentLength;
}
- in = response.getEntity().getContent();
- boolean partial = response.getStatusLine().getStatusCode() == HttpStatus.SC_PARTIAL_CONTENT;
+
+ boolean partial = response.code() == 206;
if (partial) {
Log.i(TAG, "Executed partial HTTP GET, skipping " + partialFile.length() + " bytes");
}
+ in = response.body().byteStream();
+
out = new FileOutputStream(partialFile, partial);
long n = copy(in, out);
Log.i(TAG, "Downloaded " + n + " bytes to " + partialFile);
diff --git a/app/src/main/java/net/nullsum/audinaut/service/DownloadService.java b/app/src/main/java/net/nullsum/audinaut/service/DownloadService.java
index 6ff07ba..a664054 100644
--- a/app/src/main/java/net/nullsum/audinaut/service/DownloadService.java
+++ b/app/src/main/java/net/nullsum/audinaut/service/DownloadService.java
@@ -83,680 +83,680 @@ import android.view.KeyEvent;
* @version $Id$
*/
public class DownloadService extends Service {
- private static final String TAG = DownloadService.class.getSimpleName();
+ private static final String TAG = DownloadService.class.getSimpleName();
- public static final String CMD_PLAY = "net.nullsum.audinaut.CMD_PLAY";
- public static final String CMD_TOGGLEPAUSE = "net.nullsum.audinaut.CMD_TOGGLEPAUSE";
- public static final String CMD_PAUSE = "net.nullsum.audinaut.CMD_PAUSE";
- public static final String CMD_STOP = "net.nullsum.audinaut.CMD_STOP";
- public static final String CMD_PREVIOUS = "net.nullsum.audinaut.CMD_PREVIOUS";
- public static final String CMD_NEXT = "net.nullsum.audinaut.CMD_NEXT";
- public static final String CANCEL_DOWNLOADS = "net.nullsum.audinaut.CANCEL_DOWNLOADS";
- public static final String START_PLAY = "net.nullsum.audinaut.START_PLAYING";
- public static final int FAST_FORWARD = 30000;
- public static final int REWIND = 10000;
- private static final long DEFAULT_DELAY_UPDATE_PROGRESS = 1000L;
- private static final double DELETE_CUTOFF = 0.84;
- private static final int REQUIRED_ALBUM_MATCHES = 4;
- private static final int REMOTE_PLAYLIST_TOTAL = 3;
- private static final int SHUFFLE_MODE_NONE = 0;
- private static final int SHUFFLE_MODE_ALL = 1;
- private static final int SHUFFLE_MODE_ARTIST = 2;
+ public static final String CMD_PLAY = "net.nullsum.audinaut.CMD_PLAY";
+ public static final String CMD_TOGGLEPAUSE = "net.nullsum.audinaut.CMD_TOGGLEPAUSE";
+ public static final String CMD_PAUSE = "net.nullsum.audinaut.CMD_PAUSE";
+ public static final String CMD_STOP = "net.nullsum.audinaut.CMD_STOP";
+ public static final String CMD_PREVIOUS = "net.nullsum.audinaut.CMD_PREVIOUS";
+ public static final String CMD_NEXT = "net.nullsum.audinaut.CMD_NEXT";
+ public static final String CANCEL_DOWNLOADS = "net.nullsum.audinaut.CANCEL_DOWNLOADS";
+ public static final String START_PLAY = "net.nullsum.audinaut.START_PLAYING";
+ public static final int FAST_FORWARD = 30000;
+ public static final int REWIND = 10000;
+ private static final long DEFAULT_DELAY_UPDATE_PROGRESS = 1000L;
+ private static final double DELETE_CUTOFF = 0.84;
+ private static final int REQUIRED_ALBUM_MATCHES = 4;
+ private static final int REMOTE_PLAYLIST_TOTAL = 3;
+ private static final int SHUFFLE_MODE_NONE = 0;
+ private static final int SHUFFLE_MODE_ALL = 1;
+ private static final int SHUFFLE_MODE_ARTIST = 2;
- public static final int METADATA_UPDATED_ALL = 0;
- public static final int METADATA_UPDATED_STAR = 1;
- public static final int METADATA_UPDATED_COVER_ART = 8;
+ public static final int METADATA_UPDATED_ALL = 0;
+ public static final int METADATA_UPDATED_STAR = 1;
+ public static final int METADATA_UPDATED_COVER_ART = 8;
- private final IBinder binder = new SimpleServiceBinder<>(this);
- private Looper mediaPlayerLooper;
- private MediaPlayer mediaPlayer;
- private MediaPlayer nextMediaPlayer;
- private int audioSessionId;
- private boolean nextSetup = false;
- private final List downloadList = new ArrayList();
- private final List backgroundDownloadList = new ArrayList();
- private final List toDelete = new ArrayList();
- private final Handler handler = new Handler();
- private Handler mediaPlayerHandler;
- private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this);
- private ShufflePlayBuffer shufflePlayBuffer;
+ private final IBinder binder = new SimpleServiceBinder<>(this);
+ private Looper mediaPlayerLooper;
+ private MediaPlayer mediaPlayer;
+ private MediaPlayer nextMediaPlayer;
+ private int audioSessionId;
+ private boolean nextSetup = false;
+ private final List downloadList = new ArrayList();
+ private final List backgroundDownloadList = new ArrayList();
+ private final List toDelete = new ArrayList();
+ private final Handler handler = new Handler();
+ private Handler mediaPlayerHandler;
+ private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this);
+ private ShufflePlayBuffer shufflePlayBuffer;
- private final LruCache downloadFileCache = new LruCache(100);
- private final List cleanupCandidates = new ArrayList();
- private DownloadFile currentPlaying;
- private int currentPlayingIndex = -1;
- private DownloadFile nextPlaying;
- private DownloadFile currentDownloading;
- private SilentBackgroundTask bufferTask;
- private SilentBackgroundTask nextPlayingTask;
- private PlayerState playerState = IDLE;
- private PlayerState nextPlayerState = IDLE;
- private boolean removePlayed;
- private boolean shufflePlay;
- private final List onSongChangedListeners = new ArrayList<>();
- private long revision;
- private static DownloadService instance;
- private String suggestedPlaylistName;
- private String suggestedPlaylistId;
- private PowerManager.WakeLock wakeLock;
- private WifiManager.WifiLock wifiLock;
- private boolean keepScreenOn;
- private int cachedPosition = 0;
- private boolean downloadOngoing = false;
- private float volume = 1.0f;
- private long delayUpdateProgress = DEFAULT_DELAY_UPDATE_PROGRESS;
+ private final LruCache downloadFileCache = new LruCache(100);
+ private final List cleanupCandidates = new ArrayList();
+ private DownloadFile currentPlaying;
+ private int currentPlayingIndex = -1;
+ private DownloadFile nextPlaying;
+ private DownloadFile currentDownloading;
+ private SilentBackgroundTask bufferTask;
+ private SilentBackgroundTask nextPlayingTask;
+ private PlayerState playerState = IDLE;
+ private PlayerState nextPlayerState = IDLE;
+ private boolean removePlayed;
+ private boolean shufflePlay;
+ private final List onSongChangedListeners = new ArrayList<>();
+ private long revision;
+ private static DownloadService instance;
+ private String suggestedPlaylistName;
+ private String suggestedPlaylistId;
+ private PowerManager.WakeLock wakeLock;
+ private WifiManager.WifiLock wifiLock;
+ private boolean keepScreenOn;
+ private int cachedPosition = 0;
+ private boolean downloadOngoing = false;
+ private float volume = 1.0f;
+ private long delayUpdateProgress = DEFAULT_DELAY_UPDATE_PROGRESS;
- private AudioEffectsController effectsController;
- private PositionCache positionCache;
- private BufferProxy proxy;
+ private AudioEffectsController effectsController;
+ private PositionCache positionCache;
+ private BufferProxy proxy;
- private boolean autoPlayStart = false;
- private boolean runListenersOnInit = false;
+ private boolean autoPlayStart = false;
+ private boolean runListenersOnInit = false;
- // Variables to manage getCurrentPosition sometimes starting from an arbitrary non-zero number
- private long subtractNextPosition = 0;
- private int subtractPosition = 0;
+ // Variables to manage getCurrentPosition sometimes starting from an arbitrary non-zero number
+ private long subtractNextPosition = 0;
+ private int subtractPosition = 0;
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- final SharedPreferences prefs = Util.getPreferences(this);
- new Thread(new Runnable() {
- public void run() {
- Looper.prepare();
+ final SharedPreferences prefs = Util.getPreferences(this);
+ new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
- mediaPlayer = new MediaPlayer();
- mediaPlayer.setWakeMode(DownloadService.this, PowerManager.PARTIAL_WAKE_LOCK);
+ mediaPlayer = new MediaPlayer();
+ mediaPlayer.setWakeMode(DownloadService.this, PowerManager.PARTIAL_WAKE_LOCK);
- audioSessionId = -1;
- Integer id = prefs.getInt(Constants.CACHE_AUDIO_SESSION_ID, -1);
- if(id != -1) {
- try {
- audioSessionId = id;
- mediaPlayer.setAudioSessionId(audioSessionId);
- } catch (Throwable e) {
- audioSessionId = -1;
- }
- }
+ audioSessionId = -1;
+ Integer id = prefs.getInt(Constants.CACHE_AUDIO_SESSION_ID, -1);
+ if(id != -1) {
+ try {
+ audioSessionId = id;
+ mediaPlayer.setAudioSessionId(audioSessionId);
+ } catch (Throwable e) {
+ audioSessionId = -1;
+ }
+ }
- if(audioSessionId == -1) {
- mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- try {
- audioSessionId = mediaPlayer.getAudioSessionId();
- prefs.edit().putInt(Constants.CACHE_AUDIO_SESSION_ID, audioSessionId).commit();
- } catch (Throwable t) {
- // Froyo or lower
- }
- }
+ if(audioSessionId == -1) {
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ try {
+ audioSessionId = mediaPlayer.getAudioSessionId();
+ prefs.edit().putInt(Constants.CACHE_AUDIO_SESSION_ID, audioSessionId).commit();
+ } catch (Throwable t) {
+ // Froyo or lower
+ }
+ }
- mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
- @Override
- public boolean onError(MediaPlayer mediaPlayer, int what, int more) {
- handleError(new Exception("MediaPlayer error: " + what + " (" + more + ")"));
- return false;
- }
- });
+ 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, audioSessionId);
- i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
- sendBroadcast(i);
- } catch(Throwable e) {
- // Froyo or lower
- }*/
+ /*try {
+ Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
+ i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId);
+ i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
+ sendBroadcast(i);
+ } catch(Throwable e) {
+ // Froyo or lower
+ }*/
- effectsController = new AudioEffectsController(DownloadService.this, audioSessionId);
- if(prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false)) {
- getEqualizerController();
- }
+ effectsController = new AudioEffectsController(DownloadService.this, audioSessionId);
+ if(prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false)) {
+ getEqualizerController();
+ }
- mediaPlayerLooper = Looper.myLooper();
- mediaPlayerHandler = new Handler(mediaPlayerLooper);
+ mediaPlayerLooper = Looper.myLooper();
+ mediaPlayerHandler = new Handler(mediaPlayerLooper);
- if(runListenersOnInit) {
- onSongsChanged();
- onSongProgress();
- onStateUpdate();
- }
+ if(runListenersOnInit) {
+ onSongsChanged();
+ onSongProgress();
+ onStateUpdate();
+ }
- Looper.loop();
- }
- }, "DownloadService").start();
+ Looper.loop();
+ }
+ }, "DownloadService").start();
- Util.registerMediaButtonEventReceiver(this);
+ Util.registerMediaButtonEventReceiver(this);
- PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
- wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
- wakeLock.setReferenceCounted(false);
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+ wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
+ wakeLock.setReferenceCounted(false);
- WifiManager wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
- wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "downloadServiceLock");
+ WifiManager wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
+ wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "downloadServiceLock");
- keepScreenOn = prefs.getBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, false);
+ keepScreenOn = prefs.getBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, false);
- instance = this;
- shufflePlayBuffer = new ShufflePlayBuffer(this);
- lifecycleSupport.onCreate();
- }
+ instance = this;
+ shufflePlayBuffer = new ShufflePlayBuffer(this);
+ lifecycleSupport.onCreate();
+ }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- super.onStartCommand(intent, flags, startId);
- lifecycleSupport.onStart(intent);
- return START_NOT_STICKY;
- }
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ super.onStartCommand(intent, flags, startId);
+ lifecycleSupport.onStart(intent);
+ return START_NOT_STICKY;
+ }
- @Override
- public void onTrimMemory(int level) {
- ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(this);
- if(imageLoader != null) {
- Log.i(TAG, "Memory Trim Level: " + level);
- if (level < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
- if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
- imageLoader.onLowMemory(0.75f);
- } else if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
- imageLoader.onLowMemory(0.50f);
- } else if(level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE) {
- imageLoader.onLowMemory(0.25f);
- }
- } else if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
- imageLoader.onLowMemory(0.25f);
- } else if(level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
- imageLoader.onLowMemory(0.75f);
- }
- }
- }
+ @Override
+ public void onTrimMemory(int level) {
+ ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(this);
+ if(imageLoader != null) {
+ Log.i(TAG, "Memory Trim Level: " + level);
+ if (level < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
+ imageLoader.onLowMemory(0.75f);
+ } else if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
+ imageLoader.onLowMemory(0.50f);
+ } else if(level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE) {
+ imageLoader.onLowMemory(0.25f);
+ }
+ } else if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
+ imageLoader.onLowMemory(0.25f);
+ } else if(level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
+ imageLoader.onLowMemory(0.75f);
+ }
+ }
+ }
- @Override
- public void onDestroy() {
- super.onDestroy();
- instance = null;
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ instance = null;
- if(currentPlaying != null) currentPlaying.setPlaying(false);
- lifecycleSupport.onDestroy();
+ if(currentPlaying != null) currentPlaying.setPlaying(false);
+ lifecycleSupport.onDestroy();
- try {
- Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
- i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId);
- i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
- sendBroadcast(i);
- } catch(Throwable e) {
- // Froyo or lower
- }
+ try {
+ Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
+ i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId);
+ i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
+ sendBroadcast(i);
+ } catch(Throwable e) {
+ // Froyo or lower
+ }
- mediaPlayer.release();
- if(nextMediaPlayer != null) {
- nextMediaPlayer.release();
- }
- mediaPlayerLooper.quit();
- shufflePlayBuffer.shutdown();
- effectsController.release();
+ mediaPlayer.release();
+ if(nextMediaPlayer != null) {
+ nextMediaPlayer.release();
+ }
+ mediaPlayerLooper.quit();
+ shufflePlayBuffer.shutdown();
+ effectsController.release();
- if(bufferTask != null) {
- bufferTask.cancel();
- bufferTask = null;
- }
- if(nextPlayingTask != null) {
- nextPlayingTask.cancel();
- nextPlayingTask = null;
- }
- if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
- Notifications.hidePlayingNotification(this, this, handler);
- Notifications.hideDownloadingNotification(this, this, handler);
- }
+ if(bufferTask != null) {
+ bufferTask.cancel();
+ bufferTask = null;
+ }
+ if(nextPlayingTask != null) {
+ nextPlayingTask.cancel();
+ nextPlayingTask = null;
+ }
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+ Notifications.hidePlayingNotification(this, this, handler);
+ Notifications.hideDownloadingNotification(this, this, handler);
+ }
- public static DownloadService getInstance() {
- return instance;
- }
+ public static DownloadService getInstance() {
+ return instance;
+ }
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
-
- public void post(Runnable r) {
- handler.post(r);
- }
- public void postDelayed(Runnable r, long millis) {
- handler.postDelayed(r, millis);
- }
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
- public synchronized void download(List songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
- download(songs, save, autoplay, playNext, shuffle, 0, 0);
- }
- public synchronized void download(List songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, int start, int position) {
- setShufflePlayEnabled(false);
- int offset = 1;
- boolean noNetwork = !Util.isOffline(this) && !Util.isNetworkConnected(this);
- boolean warnNetwork = false;
+ public void post(Runnable r) {
+ handler.post(r);
+ }
+ public void postDelayed(Runnable r, long millis) {
+ handler.postDelayed(r, millis);
+ }
- if (songs.isEmpty()) {
- return;
- }
+ public synchronized void download(List songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
+ download(songs, save, autoplay, playNext, shuffle, 0, 0);
+ }
+ public synchronized void download(List songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, int start, int position) {
+ setShufflePlayEnabled(false);
+ int offset = 1;
+ boolean noNetwork = !Util.isOffline(this) && !Util.isNetworkConnected(this);
+ boolean warnNetwork = false;
- if (playNext) {
- if (autoplay && getCurrentPlayingIndex() >= 0) {
- offset = 0;
- }
- for (MusicDirectory.Entry song : songs) {
- if(song != null) {
- DownloadFile downloadFile = new DownloadFile(this, song, save);
- addToDownloadList(downloadFile, getCurrentPlayingIndex() + offset);
- if(noNetwork && !warnNetwork) {
- if(!downloadFile.isCompleteFileAvailable()) {
- warnNetwork = true;
- }
- }
- offset++;
- }
- }
+ if (songs.isEmpty()) {
+ return;
+ }
+
+ if (playNext) {
+ if (autoplay && getCurrentPlayingIndex() >= 0) {
+ offset = 0;
+ }
+ for (MusicDirectory.Entry song : songs) {
+ if(song != null) {
+ DownloadFile downloadFile = new DownloadFile(this, song, save);
+ addToDownloadList(downloadFile, getCurrentPlayingIndex() + offset);
+ if(noNetwork && !warnNetwork) {
+ if(!downloadFile.isCompleteFileAvailable()) {
+ warnNetwork = true;
+ }
+ }
+ offset++;
+ }
+ }
setNextPlaying();
- } else {
- int size = size();
- int index = getCurrentPlayingIndex();
- for (MusicDirectory.Entry song : songs) {
- if(song == null) {
- continue;
- }
+ } else {
+ int size = size();
+ int index = getCurrentPlayingIndex();
+ for (MusicDirectory.Entry song : songs) {
+ if(song == null) {
+ continue;
+ }
- DownloadFile downloadFile = new DownloadFile(this, song, save);
- addToDownloadList(downloadFile, -1);
- if(noNetwork && !warnNetwork) {
- if(!downloadFile.isCompleteFileAvailable()) {
- warnNetwork = true;
- }
- }
- }
- if(!autoplay && (size - 1) == index) {
- setNextPlaying();
- }
- }
- revision++;
- onSongsChanged();
- updateRemotePlaylist();
+ DownloadFile downloadFile = new DownloadFile(this, song, save);
+ addToDownloadList(downloadFile, -1);
+ if(noNetwork && !warnNetwork) {
+ if(!downloadFile.isCompleteFileAvailable()) {
+ warnNetwork = true;
+ }
+ }
+ }
+ if(!autoplay && (size - 1) == index) {
+ setNextPlaying();
+ }
+ }
+ revision++;
+ onSongsChanged();
+ updateRemotePlaylist();
- if(shuffle) {
- shuffle();
- }
- if(warnNetwork) {
- Util.toast(this, R.string.select_album_no_network);
- }
+ if(shuffle) {
+ shuffle();
+ }
+ if(warnNetwork) {
+ Util.toast(this, R.string.select_album_no_network);
+ }
- if (autoplay) {
- play(start, true, position);
- } else if(start != 0 || position != 0) {
- play(start, false, position);
- } else {
- if (currentPlaying == null) {
- currentPlaying = downloadList.get(0);
- currentPlayingIndex = 0;
- currentPlaying.setPlaying(true);
- } else {
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- }
- checkDownloads();
- }
- lifecycleSupport.serializeDownloadQueue();
- }
- private void addToDownloadList(DownloadFile file, int offset) {
- if(offset == -1) {
- downloadList.add(file);
- } else {
- downloadList.add(offset, file);
- }
- }
- public synchronized void downloadBackground(List songs, boolean save) {
- for (MusicDirectory.Entry song : songs) {
- DownloadFile downloadFile = new DownloadFile(this, song, save);
- if(!downloadFile.isWorkDone() || (downloadFile.shouldSave() && !downloadFile.isSaved())) {
- // Only add to list if there is work to be done
- backgroundDownloadList.add(downloadFile);
- } else if(downloadFile.isSaved() && !save) {
- // Quickly unpin song instead of adding it to work to be done
- downloadFile.unpin();
- }
- }
- revision++;
+ if (autoplay) {
+ play(start, true, position);
+ } else if(start != 0 || position != 0) {
+ play(start, false, position);
+ } else {
+ if (currentPlaying == null) {
+ currentPlaying = downloadList.get(0);
+ currentPlayingIndex = 0;
+ currentPlaying.setPlaying(true);
+ } else {
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ }
+ checkDownloads();
+ }
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ private void addToDownloadList(DownloadFile file, int offset) {
+ if(offset == -1) {
+ downloadList.add(file);
+ } else {
+ downloadList.add(offset, file);
+ }
+ }
+ public synchronized void downloadBackground(List songs, boolean save) {
+ for (MusicDirectory.Entry song : songs) {
+ DownloadFile downloadFile = new DownloadFile(this, song, save);
+ if(!downloadFile.isWorkDone() || (downloadFile.shouldSave() && !downloadFile.isSaved())) {
+ // Only add to list if there is work to be done
+ backgroundDownloadList.add(downloadFile);
+ } else if(downloadFile.isSaved() && !save) {
+ // Quickly unpin song instead of adding it to work to be done
+ downloadFile.unpin();
+ }
+ }
+ revision++;
- if(!Util.isOffline(this) && !Util.isNetworkConnected(this)) {
- Util.toast(this, R.string.select_album_no_network);
- }
+ if(!Util.isOffline(this) && !Util.isNetworkConnected(this)) {
+ Util.toast(this, R.string.select_album_no_network);
+ }
- checkDownloads();
- lifecycleSupport.serializeDownloadQueue();
- }
+ checkDownloads();
+ lifecycleSupport.serializeDownloadQueue();
+ }
- private synchronized void updateRemotePlaylist() {
- List playlist = new ArrayList<>();
- if(currentPlaying != null) {
- int index = downloadList.indexOf(currentPlaying);
- if(index == -1) {
- index = 0;
- }
+ private synchronized void updateRemotePlaylist() {
+ List playlist = new ArrayList<>();
+ if(currentPlaying != null) {
+ int index = downloadList.indexOf(currentPlaying);
+ if(index == -1) {
+ index = 0;
+ }
- int size = size();
- int end = index + REMOTE_PLAYLIST_TOTAL;
- for(int i = index; i < size && i < end; i++) {
- playlist.add(downloadList.get(i));
- }
- }
- }
+ int size = size();
+ int end = index + REMOTE_PLAYLIST_TOTAL;
+ for(int i = index; i < size && i < end; i++) {
+ playlist.add(downloadList.get(i));
+ }
+ }
+ }
- public synchronized void restore(List songs, List toDelete, int currentPlayingIndex, int currentPlayingPosition) {
- SharedPreferences prefs = Util.getPreferences(this);
- if(prefs.getBoolean(Constants.PREFERENCES_KEY_REMOVE_PLAYED, false)) {
- removePlayed = true;
- }
- int startShufflePlay = prefs.getInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, SHUFFLE_MODE_NONE);
- download(songs, false, false, false, false);
- if(startShufflePlay != SHUFFLE_MODE_NONE) {
- if(startShufflePlay == SHUFFLE_MODE_ALL) {
- shufflePlay = true;
- }
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, startShufflePlay);
- editor.commit();
- }
- if (currentPlayingIndex != -1) {
- while(mediaPlayer == null) {
- Util.sleepQuietly(50L);
- }
+ public synchronized void restore(List songs, List toDelete, int currentPlayingIndex, int currentPlayingPosition) {
+ SharedPreferences prefs = Util.getPreferences(this);
+ if(prefs.getBoolean(Constants.PREFERENCES_KEY_REMOVE_PLAYED, false)) {
+ removePlayed = true;
+ }
+ int startShufflePlay = prefs.getInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, SHUFFLE_MODE_NONE);
+ download(songs, false, false, false, false);
+ if(startShufflePlay != SHUFFLE_MODE_NONE) {
+ if(startShufflePlay == SHUFFLE_MODE_ALL) {
+ shufflePlay = true;
+ }
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, startShufflePlay);
+ editor.commit();
+ }
+ if (currentPlayingIndex != -1) {
+ while(mediaPlayer == null) {
+ Util.sleepQuietly(50L);
+ }
- play(currentPlayingIndex, autoPlayStart, currentPlayingPosition);
- autoPlayStart = false;
- }
+ play(currentPlayingIndex, autoPlayStart, currentPlayingPosition);
+ autoPlayStart = false;
+ }
- if(toDelete != null) {
- for(MusicDirectory.Entry entry: toDelete) {
- this.toDelete.add(forSong(entry));
- }
- }
-
- suggestedPlaylistName = prefs.getString(Constants.PREFERENCES_KEY_PLAYLIST_NAME, null);
- suggestedPlaylistId = prefs.getString(Constants.PREFERENCES_KEY_PLAYLIST_ID, null);
- }
+ if(toDelete != null) {
+ for(MusicDirectory.Entry entry: toDelete) {
+ this.toDelete.add(forSong(entry));
+ }
+ }
- public boolean isInitialized() {
- return lifecycleSupport != null && lifecycleSupport.isInitialized();
- }
+ suggestedPlaylistName = prefs.getString(Constants.PREFERENCES_KEY_PLAYLIST_NAME, null);
+ suggestedPlaylistId = prefs.getString(Constants.PREFERENCES_KEY_PLAYLIST_ID, null);
+ }
- public synchronized Date getLastStateChanged() {
- return lifecycleSupport.getLastChange();
- }
+ public boolean isInitialized() {
+ return lifecycleSupport != null && lifecycleSupport.isInitialized();
+ }
- public synchronized void setRemovePlayed(boolean enabled) {
- removePlayed = enabled;
- if(removePlayed) {
- checkDownloads();
- lifecycleSupport.serializeDownloadQueue();
- }
- SharedPreferences.Editor editor = Util.getPreferences(this).edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_REMOVE_PLAYED, enabled);
- editor.commit();
- }
- public boolean isRemovePlayed() {
- return removePlayed;
- }
+ public synchronized Date getLastStateChanged() {
+ return lifecycleSupport.getLastChange();
+ }
- public synchronized void setShufflePlayEnabled(boolean enabled) {
- shufflePlay = enabled;
- if (shufflePlay) {
- checkDownloads();
- }
- SharedPreferences.Editor editor = Util.getPreferences(this).edit();
- editor.putInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, enabled ? SHUFFLE_MODE_ALL : SHUFFLE_MODE_NONE);
- editor.commit();
- }
+ public synchronized void setRemovePlayed(boolean enabled) {
+ removePlayed = enabled;
+ if(removePlayed) {
+ checkDownloads();
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_REMOVE_PLAYED, enabled);
+ editor.commit();
+ }
+ public boolean isRemovePlayed() {
+ return removePlayed;
+ }
- public boolean isShufflePlayEnabled() {
- return shufflePlay;
- }
+ public synchronized void setShufflePlayEnabled(boolean enabled) {
+ shufflePlay = enabled;
+ if (shufflePlay) {
+ checkDownloads();
+ }
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_SHUFFLE_MODE, enabled ? SHUFFLE_MODE_ALL : SHUFFLE_MODE_NONE);
+ editor.commit();
+ }
- public synchronized void shuffle() {
- Collections.shuffle(downloadList);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- if (currentPlaying != null) {
- downloadList.remove(getCurrentPlayingIndex());
- downloadList.add(0, currentPlaying);
- currentPlayingIndex = 0;
- }
- revision++;
- onSongsChanged();
- lifecycleSupport.serializeDownloadQueue();
- updateRemotePlaylist();
- setNextPlaying();
- }
+ public boolean isShufflePlayEnabled() {
+ return shufflePlay;
+ }
- public RepeatMode getRepeatMode() {
- return Util.getRepeatMode(this);
- }
+ public synchronized void shuffle() {
+ Collections.shuffle(downloadList);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ if (currentPlaying != null) {
+ downloadList.remove(getCurrentPlayingIndex());
+ downloadList.add(0, currentPlaying);
+ currentPlayingIndex = 0;
+ }
+ revision++;
+ onSongsChanged();
+ lifecycleSupport.serializeDownloadQueue();
+ updateRemotePlaylist();
+ setNextPlaying();
+ }
- public void setRepeatMode(RepeatMode repeatMode) {
- Util.setRepeatMode(this, repeatMode);
- setNextPlaying();
- }
+ public RepeatMode getRepeatMode() {
+ return Util.getRepeatMode(this);
+ }
- public boolean getKeepScreenOn() {
- return keepScreenOn;
- }
+ public void setRepeatMode(RepeatMode repeatMode) {
+ Util.setRepeatMode(this, repeatMode);
+ setNextPlaying();
+ }
- public void setKeepScreenOn(boolean keepScreenOn) {
- this.keepScreenOn = keepScreenOn;
+ public boolean getKeepScreenOn() {
+ return keepScreenOn;
+ }
- SharedPreferences prefs = Util.getPreferences(this);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, keepScreenOn);
- editor.commit();
- }
+ public void setKeepScreenOn(boolean keepScreenOn) {
+ this.keepScreenOn = keepScreenOn;
- public synchronized DownloadFile forSong(MusicDirectory.Entry song) {
- DownloadFile returnFile = null;
- for (DownloadFile downloadFile : downloadList) {
- if (downloadFile.getSong().equals(song)) {
- if(((downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && downloadFile.getPartialFile().exists()) || downloadFile.isWorkDone())) {
- // If downloading, return immediately
- return downloadFile;
- } else {
- // Otherwise, check to make sure there isn't a background download going on first
- returnFile = downloadFile;
- }
- }
- }
- for (DownloadFile downloadFile : backgroundDownloadList) {
- if (downloadFile.getSong().equals(song)) {
- return downloadFile;
- }
- }
+ SharedPreferences prefs = Util.getPreferences(this);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, keepScreenOn);
+ editor.commit();
+ }
- if(returnFile != null) {
- return returnFile;
- }
+ public synchronized DownloadFile forSong(MusicDirectory.Entry song) {
+ DownloadFile returnFile = null;
+ for (DownloadFile downloadFile : downloadList) {
+ if (downloadFile.getSong().equals(song)) {
+ if(((downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && downloadFile.getPartialFile().exists()) || downloadFile.isWorkDone())) {
+ // If downloading, return immediately
+ return downloadFile;
+ } else {
+ // Otherwise, check to make sure there isn't a background download going on first
+ returnFile = downloadFile;
+ }
+ }
+ }
+ for (DownloadFile downloadFile : backgroundDownloadList) {
+ 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;
- }
+ if(returnFile != null) {
+ return returnFile;
+ }
- public synchronized void clearBackground() {
- if(currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) {
- currentDownloading.cancelDownload();
- currentDownloading = null;
- }
- backgroundDownloadList.clear();
- revision++;
- Notifications.hideDownloadingNotification(this, this, handler);
- }
+ DownloadFile downloadFile = downloadFileCache.get(song);
+ if (downloadFile == null) {
+ downloadFile = new DownloadFile(this, song, false);
+ downloadFileCache.put(song, downloadFile);
+ }
+ return downloadFile;
+ }
- public synchronized void clearIncomplete() {
- Iterator iterator = downloadList.iterator();
- while (iterator.hasNext()) {
- DownloadFile downloadFile = iterator.next();
- if (!downloadFile.isCompleteFileAvailable()) {
- iterator.remove();
-
- // Reset if the current playing song has been removed
- if(currentPlaying == downloadFile) {
- reset();
- }
+ public synchronized void clearBackground() {
+ if(currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+ backgroundDownloadList.clear();
+ revision++;
+ Notifications.hideDownloadingNotification(this, this, handler);
+ }
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- }
- }
- lifecycleSupport.serializeDownloadQueue();
- updateRemotePlaylist();
- onSongsChanged();
- }
+ public synchronized void clearIncomplete() {
+ Iterator iterator = downloadList.iterator();
+ while (iterator.hasNext()) {
+ DownloadFile downloadFile = iterator.next();
+ if (!downloadFile.isCompleteFileAvailable()) {
+ iterator.remove();
- public void setOnline(final boolean online) {
- if(shufflePlay) {
- setShufflePlayEnabled(false);
- }
+ // Reset if the current playing song has been removed
+ if(currentPlaying == downloadFile) {
+ reset();
+ }
- lifecycleSupport.post(new Runnable() {
- @Override
- public void run() {
- if (online) {
- checkDownloads();
- } else {
- clearIncomplete();
- }
- }
- });
- }
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ }
+ }
+ lifecycleSupport.serializeDownloadQueue();
+ updateRemotePlaylist();
+ onSongsChanged();
+ }
- public synchronized int size() {
- return downloadList.size();
- }
+ public void setOnline(final boolean online) {
+ if(shufflePlay) {
+ setShufflePlayEnabled(false);
+ }
- public synchronized void clear() {
- clear(true);
- }
- public synchronized void clear(boolean serialize) {
- // Delete podcast if fully listened to
- int position = getPlayerPosition();
- int duration = getPlayerDuration();
- boolean cutoff = isPastCutoff(position, duration, true);
- for(DownloadFile podcast: toDelete) {
- podcast.delete();
- }
- toDelete.clear();
-
- reset();
- downloadList.clear();
- onSongsChanged();
- if (currentDownloading != null && !backgroundDownloadList.contains(currentDownloading)) {
- currentDownloading.cancelDownload();
- currentDownloading = null;
- }
- setCurrentPlaying(null, false);
+ lifecycleSupport.post(new Runnable() {
+ @Override
+ public void run() {
+ if (online) {
+ checkDownloads();
+ } else {
+ clearIncomplete();
+ }
+ }
+ });
+ }
- if (serialize) {
- lifecycleSupport.serializeDownloadQueue();
- }
- updateRemotePlaylist();
- setNextPlaying();
- if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
+ public synchronized int size() {
+ return downloadList.size();
+ }
- suggestedPlaylistName = null;
- suggestedPlaylistId = null;
+ public synchronized void clear() {
+ clear(true);
+ }
+ public synchronized void clear(boolean serialize) {
+ // Delete podcast if fully listened to
+ int position = getPlayerPosition();
+ int duration = getPlayerDuration();
+ boolean cutoff = isPastCutoff(position, duration, true);
+ for(DownloadFile podcast: toDelete) {
+ podcast.delete();
+ }
+ toDelete.clear();
- setShufflePlayEnabled(false);
- checkDownloads();
- }
+ reset();
+ downloadList.clear();
+ onSongsChanged();
+ if (currentDownloading != null && !backgroundDownloadList.contains(currentDownloading)) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+ setCurrentPlaying(null, false);
- public synchronized void remove(int which) {
- downloadList.remove(which);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- }
+ if (serialize) {
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ updateRemotePlaylist();
+ setNextPlaying();
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
- public synchronized void remove(DownloadFile downloadFile) {
- if (downloadFile == currentDownloading) {
- currentDownloading.cancelDownload();
- currentDownloading = null;
- }
- if (downloadFile == currentPlaying) {
- reset();
- setCurrentPlaying(null, false);
- }
- downloadList.remove(downloadFile);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- backgroundDownloadList.remove(downloadFile);
- revision++;
- onSongsChanged();
- lifecycleSupport.serializeDownloadQueue();
- updateRemotePlaylist();
- if(downloadFile == nextPlaying) {
- setNextPlaying();
- }
+ suggestedPlaylistName = null;
+ suggestedPlaylistId = null;
- checkDownloads();
- }
- public synchronized void removeBackground(DownloadFile downloadFile) {
- if (downloadFile == currentDownloading && downloadFile != currentPlaying && downloadFile != nextPlaying) {
- currentDownloading.cancelDownload();
- currentDownloading = null;
- }
+ setShufflePlayEnabled(false);
+ checkDownloads();
+ }
- backgroundDownloadList.remove(downloadFile);
- revision++;
- checkDownloads();
- }
+ public synchronized void remove(int which) {
+ downloadList.remove(which);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ }
- public synchronized void delete(List songs) {
- for (MusicDirectory.Entry song : songs) {
- forSong(song).delete();
- }
- }
+ public synchronized void remove(DownloadFile downloadFile) {
+ if (downloadFile == currentDownloading) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+ if (downloadFile == currentPlaying) {
+ reset();
+ setCurrentPlaying(null, false);
+ }
+ downloadList.remove(downloadFile);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ backgroundDownloadList.remove(downloadFile);
+ revision++;
+ onSongsChanged();
+ lifecycleSupport.serializeDownloadQueue();
+ updateRemotePlaylist();
+ if(downloadFile == nextPlaying) {
+ setNextPlaying();
+ }
- public synchronized void unpin(List songs) {
- for (MusicDirectory.Entry song : songs) {
- forSong(song).unpin();
- }
- }
+ checkDownloads();
+ }
+ public synchronized void removeBackground(DownloadFile downloadFile) {
+ if (downloadFile == currentDownloading && downloadFile != currentPlaying && downloadFile != nextPlaying) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
- synchronized void setCurrentPlaying(int currentPlayingIndex, boolean showNotification) {
- try {
- setCurrentPlaying(downloadList.get(currentPlayingIndex), showNotification);
- } catch (IndexOutOfBoundsException x) {
- // Ignored
- }
- }
+ backgroundDownloadList.remove(downloadFile);
+ revision++;
+ checkDownloads();
+ }
- synchronized void setCurrentPlaying(DownloadFile currentPlaying, boolean showNotification) {
- if(this.currentPlaying != null) {
- this.currentPlaying.setPlaying(false);
- }
- this.currentPlaying = currentPlaying;
- if(currentPlaying == null) {
- currentPlayingIndex = -1;
- setPlayerState(IDLE);
- } else {
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- }
+ public synchronized void delete(List songs) {
+ for (MusicDirectory.Entry song : songs) {
+ forSong(song).delete();
+ }
+ }
- if (currentPlaying != null && currentPlaying.getSong() != null) {
- Util.broadcastNewTrackInfo(this, currentPlaying.getSong());
- } else {
- Util.broadcastNewTrackInfo(this, null);
- Notifications.hidePlayingNotification(this, this, handler);
- }
- onSongChanged();
- }
+ public synchronized void unpin(List songs) {
+ for (MusicDirectory.Entry song : songs) {
+ forSong(song).unpin();
+ }
+ }
- synchronized void setNextPlaying() {
- SharedPreferences prefs = Util.getPreferences(DownloadService.this);
+ synchronized void setCurrentPlaying(int currentPlayingIndex, boolean showNotification) {
+ try {
+ setCurrentPlaying(downloadList.get(currentPlayingIndex), showNotification);
+ } catch (IndexOutOfBoundsException x) {
+ // Ignored
+ }
+ }
+
+ synchronized void setCurrentPlaying(DownloadFile currentPlaying, boolean showNotification) {
+ if(this.currentPlaying != null) {
+ this.currentPlaying.setPlaying(false);
+ }
+ this.currentPlaying = currentPlaying;
+ if(currentPlaying == null) {
+ currentPlayingIndex = -1;
+ setPlayerState(IDLE);
+ } else {
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ }
+
+ if (currentPlaying != null && currentPlaying.getSong() != null) {
+ Util.broadcastNewTrackInfo(this, currentPlaying.getSong());
+ } else {
+ Util.broadcastNewTrackInfo(this, null);
+ Notifications.hidePlayingNotification(this, this, handler);
+ }
+ onSongChanged();
+ }
+
+ synchronized void setNextPlaying() {
+ SharedPreferences prefs = Util.getPreferences(DownloadService.this);
boolean gaplessPlayback = prefs.getBoolean(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK, true);
if (!gaplessPlayback) {
@@ -764,218 +764,218 @@ public class DownloadService extends Service {
nextPlayerState = IDLE;
return;
}
- setNextPlayerState(IDLE);
+ setNextPlayerState(IDLE);
- int index = getNextPlayingIndex();
+ int index = getNextPlayingIndex();
- if(nextPlayingTask != null) {
- nextPlayingTask.cancel();
- nextPlayingTask = null;
- }
- resetNext();
+ if(nextPlayingTask != null) {
+ nextPlayingTask.cancel();
+ nextPlayingTask = null;
+ }
+ resetNext();
- if(index < size() && index != -1 && index != currentPlayingIndex) {
- nextPlaying = downloadList.get(index);
+ if(index < size() && index != -1 && index != currentPlayingIndex) {
+ nextPlaying = downloadList.get(index);
nextPlayingTask = new CheckCompletionTask(nextPlaying);
nextPlayingTask.execute();
- } else {
- nextPlaying = null;
- }
- }
+ } else {
+ nextPlaying = null;
+ }
+ }
- public int getCurrentPlayingIndex() {
- return currentPlayingIndex;
- }
- private int getNextPlayingIndex() {
- int index = getCurrentPlayingIndex();
- if (index != -1) {
- RepeatMode repeatMode = getRepeatMode();
- switch (repeatMode) {
- case OFF:
- index = index + 1;
- break;
- case ALL:
- index = (index + 1) % size();
- break;
- case SINGLE:
- break;
- default:
- break;
- }
+ public int getCurrentPlayingIndex() {
+ return currentPlayingIndex;
+ }
+ private int getNextPlayingIndex() {
+ int index = getCurrentPlayingIndex();
+ if (index != -1) {
+ RepeatMode repeatMode = getRepeatMode();
+ switch (repeatMode) {
+ case OFF:
+ index = index + 1;
+ break;
+ case ALL:
+ index = (index + 1) % size();
+ break;
+ case SINGLE:
+ break;
+ default:
+ break;
+ }
- index = checkNextIndexValid(index, repeatMode);
- }
- return index;
- }
- private int checkNextIndexValid(int index, RepeatMode repeatMode) {
- int startIndex = index;
- int size = size();
- if(index < size && index != -1) {
- if(!Util.isAllowedToDownload(this)){
- DownloadFile next = downloadList.get(index);
- while(!next.isCompleteFileAvailable()) {
- index++;
+ index = checkNextIndexValid(index, repeatMode);
+ }
+ return index;
+ }
+ private int checkNextIndexValid(int index, RepeatMode repeatMode) {
+ int startIndex = index;
+ int size = size();
+ if(index < size && index != -1) {
+ if(!Util.isAllowedToDownload(this)){
+ DownloadFile next = downloadList.get(index);
+ while(!next.isCompleteFileAvailable()) {
+ index++;
- if (index >= size) {
- if(repeatMode == RepeatMode.ALL) {
- index = 0;
- } else {
- return -1;
- }
- } else if(index == startIndex) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- Util.toast(DownloadService.this, R.string.download_playerstate_mobile_disabled);
- }
- });
- return -1;
- }
+ if (index >= size) {
+ if(repeatMode == RepeatMode.ALL) {
+ index = 0;
+ } else {
+ return -1;
+ }
+ } else if(index == startIndex) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ Util.toast(DownloadService.this, R.string.download_playerstate_mobile_disabled);
+ }
+ });
+ return -1;
+ }
- next = downloadList.get(index);
- }
- }
- }
+ next = downloadList.get(index);
+ }
+ }
+ }
- return index;
- }
+ return index;
+ }
- public DownloadFile getCurrentPlaying() {
- return currentPlaying;
- }
+ public DownloadFile getCurrentPlaying() {
+ return currentPlaying;
+ }
- public DownloadFile getCurrentDownloading() {
- return currentDownloading;
- }
+ public DownloadFile getCurrentDownloading() {
+ return currentDownloading;
+ }
- public DownloadFile getNextPlaying() {
- return nextPlaying;
- }
+ public DownloadFile getNextPlaying() {
+ return nextPlaying;
+ }
- public List getSongs() {
- return downloadList;
- }
+ public List getSongs() {
+ return downloadList;
+ }
- public List getToDelete() { return toDelete; }
+ public List getToDelete() { return toDelete; }
- public synchronized List getDownloads() {
- List temp = new ArrayList();
- temp.addAll(downloadList);
- temp.addAll(backgroundDownloadList);
- return temp;
- }
+ public synchronized List getDownloads() {
+ List temp = new ArrayList();
+ temp.addAll(downloadList);
+ temp.addAll(backgroundDownloadList);
+ return temp;
+ }
- public List getBackgroundDownloads() {
- return backgroundDownloadList;
- }
+ public List getBackgroundDownloads() {
+ return backgroundDownloadList;
+ }
- /** 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);
- }
- }
+ /** 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);
+ }
+ }
- public synchronized void play(int index) {
- play(index, true);
- }
- public synchronized void play(DownloadFile downloadFile) {
- play(downloadList.indexOf(downloadFile));
- }
- private synchronized void play(int index, boolean start) {
- play(index, start, 0);
- }
- private synchronized void play(int index, boolean start, int position) {
- int size = this.size();
- cachedPosition = 0;
- if (index < 0 || index >= size) {
- reset();
- if(index >= size && size != 0) {
- setCurrentPlaying(0, false);
- Notifications.hidePlayingNotification(this, this, handler);
- } else {
- setCurrentPlaying(null, false);
- }
- lifecycleSupport.serializeDownloadQueue();
- } else {
- if(nextPlayingTask != null) {
- nextPlayingTask.cancel();
- nextPlayingTask = null;
- }
- setCurrentPlaying(index, start);
+ public synchronized void play(int index) {
+ play(index, true);
+ }
+ public synchronized void play(DownloadFile downloadFile) {
+ play(downloadList.indexOf(downloadFile));
+ }
+ private synchronized void play(int index, boolean start) {
+ play(index, start, 0);
+ }
+ private synchronized void play(int index, boolean start, int position) {
+ int size = this.size();
+ cachedPosition = 0;
+ if (index < 0 || index >= size) {
+ reset();
+ if(index >= size && size != 0) {
+ setCurrentPlaying(0, false);
+ Notifications.hidePlayingNotification(this, this, handler);
+ } else {
+ setCurrentPlaying(null, false);
+ }
+ lifecycleSupport.serializeDownloadQueue();
+ } else {
+ if(nextPlayingTask != null) {
+ nextPlayingTask.cancel();
+ nextPlayingTask = null;
+ }
+ setCurrentPlaying(index, start);
bufferAndPlay(position, start);
checkDownloads();
setNextPlaying();
- }
- }
- private synchronized void playNext() {
- if(nextPlaying != null && nextPlayerState == PlayerState.PREPARED) {
- if(!nextSetup) {
- playNext(true);
- } else {
- nextSetup = false;
- playNext(false);
- }
- } else {
- onSongCompleted();
- }
- }
- private synchronized void playNext(boolean start) {
- Util.broadcastPlaybackStatusChange(this, currentPlaying.getSong(), PlayerState.PREPARED);
-
- // Swap the media players since nextMediaPlayer is ready to play
- subtractPosition = 0;
- if(start) {
- nextMediaPlayer.start();
- } else if(!nextMediaPlayer.isPlaying()) {
- Log.w(TAG, "nextSetup lied about it's state!");
- nextMediaPlayer.start();
- } else {
- Log.i(TAG, "nextMediaPlayer already playing");
-
- // Next time the cachedPosition is updated, use that as position 0
- subtractNextPosition = System.currentTimeMillis();
- }
- MediaPlayer tmp = mediaPlayer;
- mediaPlayer = nextMediaPlayer;
- nextMediaPlayer = tmp;
- setCurrentPlaying(nextPlaying, true);
- setPlayerState(PlayerState.STARTED);
- setupHandlers(currentPlaying, false, start);
- setNextPlaying();
+ }
+ }
+ private synchronized void playNext() {
+ if(nextPlaying != null && nextPlayerState == PlayerState.PREPARED) {
+ if(!nextSetup) {
+ playNext(true);
+ } else {
+ nextSetup = false;
+ playNext(false);
+ }
+ } else {
+ onSongCompleted();
+ }
+ }
+ private synchronized void playNext(boolean start) {
+ Util.broadcastPlaybackStatusChange(this, currentPlaying.getSong(), PlayerState.PREPARED);
- // Proxy should not be being used here since the next player was already setup to play
- if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
- checkDownloads();
- updateRemotePlaylist();
- }
+ // Swap the media players since nextMediaPlayer is ready to play
+ subtractPosition = 0;
+ if(start) {
+ nextMediaPlayer.start();
+ } else if(!nextMediaPlayer.isPlaying()) {
+ Log.w(TAG, "nextSetup lied about it's state!");
+ nextMediaPlayer.start();
+ } else {
+ Log.i(TAG, "nextMediaPlayer already playing");
- /** Plays or resumes the playback, depending on the current player state. */
- public synchronized void togglePlayPause() {
- if (playerState == PAUSED || playerState == COMPLETED || playerState == STOPPED) {
- start();
- } else if (playerState == STOPPED || playerState == IDLE) {
- autoPlayStart = true;
- play();
- } else if (playerState == STARTED) {
- pause();
- }
- }
+ // Next time the cachedPosition is updated, use that as position 0
+ subtractNextPosition = System.currentTimeMillis();
+ }
+ MediaPlayer tmp = mediaPlayer;
+ mediaPlayer = nextMediaPlayer;
+ nextMediaPlayer = tmp;
+ setCurrentPlaying(nextPlaying, true);
+ setPlayerState(PlayerState.STARTED);
+ setupHandlers(currentPlaying, false, start);
+ setNextPlaying();
- public synchronized void seekTo(int position) {
- if(position < 0) {
- position = 0;
- }
+ // Proxy should not be being used here since the next player was already setup to play
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+ checkDownloads();
+ updateRemotePlaylist();
+ }
- try {
+ /** Plays or resumes the playback, depending on the current player state. */
+ public synchronized void togglePlayPause() {
+ if (playerState == PAUSED || playerState == COMPLETED || playerState == STOPPED) {
+ start();
+ } else if (playerState == STOPPED || playerState == IDLE) {
+ autoPlayStart = true;
+ play();
+ } else if (playerState == STARTED) {
+ pause();
+ }
+ }
+
+ public synchronized void seekTo(int position) {
+ if(position < 0) {
+ position = 0;
+ }
+
+ try {
if(proxy != null && currentPlaying.isCompleteFileAvailable()) {
doPlay(currentPlaying, position, playerState == STARTED);
return;
@@ -983,472 +983,472 @@ public class DownloadService extends Service {
mediaPlayer.seekTo(position);
subtractPosition = 0;
- cachedPosition = position;
+ cachedPosition = position;
- onSongProgress();
- if(playerState == PAUSED) {
- lifecycleSupport.serializeDownloadQueue();
- }
- } catch (Exception x) {
- handleError(x);
- }
- }
- public synchronized int rewind() {
- return seekToWrapper(-REWIND);
- }
- public synchronized int fastForward() {
- return seekToWrapper(FAST_FORWARD);
- }
- protected int seekToWrapper(int difference) {
- int msPlayed = Math.max(0, getPlayerPosition());
- Integer duration = getPlayerDuration();
- int msTotal = duration == null ? 0 : duration;
+ onSongProgress();
+ if(playerState == PAUSED) {
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+ public synchronized int rewind() {
+ return seekToWrapper(-REWIND);
+ }
+ public synchronized int fastForward() {
+ return seekToWrapper(FAST_FORWARD);
+ }
+ protected int seekToWrapper(int difference) {
+ int msPlayed = Math.max(0, getPlayerPosition());
+ Integer duration = getPlayerDuration();
+ int msTotal = duration == null ? 0 : duration;
- int seekTo;
- if(msPlayed + difference > msTotal) {
- seekTo = msTotal;
- } else {
- seekTo = msPlayed + difference;
- }
- seekTo(seekTo);
+ int seekTo;
+ if(msPlayed + difference > msTotal) {
+ seekTo = msTotal;
+ } else {
+ seekTo = msPlayed + difference;
+ }
+ seekTo(seekTo);
- return seekTo;
- }
+ return seekTo;
+ }
- public synchronized void previous() {
- int index = getCurrentPlayingIndex();
- if (index == -1) {
- return;
- }
+ public synchronized void previous() {
+ int index = getCurrentPlayingIndex();
+ if (index == -1) {
+ return;
+ }
- // If only one song, just skip within song
- if(size() == 1 || (currentPlaying != null && !currentPlaying.isSong())) {
- rewind();
- return;
- }
+ // If only one song, just skip within song
+ if(size() == 1 || (currentPlaying != null && !currentPlaying.isSong())) {
+ rewind();
+ return;
+ }
- // Restart song if played more than five seconds.
- if (getPlayerPosition() > 5000 || (index == 0 && getRepeatMode() != RepeatMode.ALL)) {
- seekTo(0);
- } else {
- if(index == 0) {
- index = size();
- }
+ // Restart song if played more than five seconds.
+ if (getPlayerPosition() > 5000 || (index == 0 && getRepeatMode() != RepeatMode.ALL)) {
+ seekTo(0);
+ } else {
+ if(index == 0) {
+ index = size();
+ }
- play(index - 1, playerState != PAUSED && playerState != STOPPED && playerState != IDLE);
- }
- }
+ play(index - 1, playerState != PAUSED && playerState != STOPPED && playerState != IDLE);
+ }
+ }
- public synchronized void next() {
- next(false);
- }
- public synchronized void next(boolean forceCutoff) {
- next(forceCutoff, false);
- }
- public synchronized void next(boolean forceCutoff, boolean forceStart) {
- // If only one song, just skip within song
- if(size() == 1 || (currentPlaying != null && !currentPlaying.isSong())) {
- fastForward();
- return;
- } else if(playerState == PREPARING || playerState == PREPARED) {
- return;
- }
+ public synchronized void next() {
+ next(false);
+ }
+ public synchronized void next(boolean forceCutoff) {
+ next(forceCutoff, false);
+ }
+ public synchronized void next(boolean forceCutoff, boolean forceStart) {
+ // If only one song, just skip within song
+ if(size() == 1 || (currentPlaying != null && !currentPlaying.isSong())) {
+ fastForward();
+ return;
+ } else if(playerState == PREPARING || playerState == PREPARED) {
+ return;
+ }
- // Delete podcast if fully listened to
- int position = getPlayerPosition();
- int duration = getPlayerDuration();
- boolean cutoff;
- if(forceCutoff) {
- cutoff = true;
- } else {
- cutoff = isPastCutoff(position, duration);
- }
+ // Delete podcast if fully listened to
+ int position = getPlayerPosition();
+ int duration = getPlayerDuration();
+ boolean cutoff;
+ if(forceCutoff) {
+ cutoff = true;
+ } else {
+ cutoff = isPastCutoff(position, duration);
+ }
- int index = getCurrentPlayingIndex();
- int nextPlayingIndex = getNextPlayingIndex();
- // Make sure to actually go to next when repeat song is on
- if(index == nextPlayingIndex) {
- nextPlayingIndex++;
- }
- if (index != -1 && nextPlayingIndex < size()) {
- play(nextPlayingIndex, playerState != PAUSED && playerState != STOPPED && playerState != IDLE || forceStart);
- }
- }
+ int index = getCurrentPlayingIndex();
+ int nextPlayingIndex = getNextPlayingIndex();
+ // Make sure to actually go to next when repeat song is on
+ if(index == nextPlayingIndex) {
+ nextPlayingIndex++;
+ }
+ if (index != -1 && nextPlayingIndex < size()) {
+ play(nextPlayingIndex, playerState != PAUSED && playerState != STOPPED && playerState != IDLE || forceStart);
+ }
+ }
- public void onSongCompleted() {
- setPlayerStateCompleted();
- postPlayCleanup();
- play(getNextPlayingIndex());
- }
- public void onNextStarted(DownloadFile nextPlaying) {
- setPlayerStateCompleted();
- postPlayCleanup();
- setCurrentPlaying(nextPlaying, true);
- setPlayerState(STARTED);
- setNextPlayerState(IDLE);
- }
+ public void onSongCompleted() {
+ setPlayerStateCompleted();
+ postPlayCleanup();
+ play(getNextPlayingIndex());
+ }
+ public void onNextStarted(DownloadFile nextPlaying) {
+ setPlayerStateCompleted();
+ postPlayCleanup();
+ setCurrentPlaying(nextPlaying, true);
+ setPlayerState(STARTED);
+ setNextPlayerState(IDLE);
+ }
- public synchronized void pause() {
- pause(false);
- }
- public synchronized void pause(boolean temp) {
- try {
- if (playerState == STARTED) {
+ public synchronized void pause() {
+ pause(false);
+ }
+ public synchronized void pause(boolean temp) {
+ try {
+ if (playerState == STARTED) {
mediaPlayer.pause();
- setPlayerState(temp ? PAUSED_TEMP : PAUSED);
- } else if(playerState == PAUSED_TEMP) {
- setPlayerState(temp ? PAUSED_TEMP : PAUSED);
- }
- } catch (Exception x) {
- handleError(x);
- }
- }
+ setPlayerState(temp ? PAUSED_TEMP : PAUSED);
+ } else if(playerState == PAUSED_TEMP) {
+ setPlayerState(temp ? PAUSED_TEMP : PAUSED);
+ }
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
- public synchronized void stop() {
- try {
- if (playerState == STARTED) {
+ public synchronized void stop() {
+ try {
+ if (playerState == STARTED) {
mediaPlayer.pause();
setPlayerState(STOPPED);
- } else if(playerState == PAUSED) {
- setPlayerState(STOPPED);
- }
- } catch(Exception x) {
- handleError(x);
- }
- }
+ } else if(playerState == PAUSED) {
+ setPlayerState(STOPPED);
+ }
+ } catch(Exception x) {
+ handleError(x);
+ }
+ }
- public synchronized void start() {
- try {
+ public synchronized void start() {
+ try {
// Only start if done preparing
if(playerState != PREPARING) {
mediaPlayer.start();
} else {
// Otherwise, we need to set it up to start when done preparing
autoPlayStart = true;
- }
- setPlayerState(STARTED);
- } catch (Exception x) {
- handleError(x);
- }
- }
+ }
+ setPlayerState(STARTED);
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
- public synchronized void reset() {
- if (bufferTask != null) {
- bufferTask.cancel();
- bufferTask = null;
- }
- try {
+ public synchronized void reset() {
+ if (bufferTask != null) {
+ bufferTask.cancel();
+ bufferTask = null;
+ }
+ try {
setPlayerState(IDLE);
- mediaPlayer.setOnErrorListener(null);
- mediaPlayer.setOnCompletionListener(null);
- if(nextSetup) {
- mediaPlayer.setNextMediaPlayer(null);
- nextSetup = false;
- }
- mediaPlayer.reset();
- subtractPosition = 0;
- } catch (Exception x) {
- handleError(x);
- }
- }
+ mediaPlayer.setOnErrorListener(null);
+ mediaPlayer.setOnCompletionListener(null);
+ if(nextSetup) {
+ mediaPlayer.setNextMediaPlayer(null);
+ nextSetup = false;
+ }
+ mediaPlayer.reset();
+ subtractPosition = 0;
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
- public synchronized void resetNext() {
- try {
- if (nextMediaPlayer != null) {
- if (nextSetup) {
- mediaPlayer.setNextMediaPlayer(null);
- }
- nextSetup = false;
+ public synchronized void resetNext() {
+ try {
+ if (nextMediaPlayer != null) {
+ if (nextSetup) {
+ mediaPlayer.setNextMediaPlayer(null);
+ }
+ nextSetup = false;
- nextMediaPlayer.setOnCompletionListener(null);
- nextMediaPlayer.setOnErrorListener(null);
- nextMediaPlayer.reset();
- nextMediaPlayer.release();
- nextMediaPlayer = null;
- } else if(nextSetup) {
- nextSetup = false;
- }
- } catch (Exception e) {
- Log.w(TAG, "Failed to reset next media player");
- }
- }
+ nextMediaPlayer.setOnCompletionListener(null);
+ nextMediaPlayer.setOnErrorListener(null);
+ nextMediaPlayer.reset();
+ nextMediaPlayer.release();
+ nextMediaPlayer = null;
+ } else if(nextSetup) {
+ nextSetup = false;
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to reset next media player");
+ }
+ }
- public int getPlayerPosition() {
- try {
- if (playerState == IDLE || playerState == DOWNLOADING || playerState == PREPARING) {
- return 0;
- }
+ public int getPlayerPosition() {
+ try {
+ if (playerState == IDLE || playerState == DOWNLOADING || playerState == PREPARING) {
+ return 0;
+ }
return Math.max(0, cachedPosition - subtractPosition);
- } catch (Exception x) {
- handleError(x);
- return 0;
- }
- }
+ } catch (Exception x) {
+ handleError(x);
+ return 0;
+ }
+ }
- public synchronized int getPlayerDuration() {
- if (playerState != IDLE && playerState != DOWNLOADING && playerState != PlayerState.PREPARING) {
- int duration = 0;
+ public synchronized int getPlayerDuration() {
+ if (playerState != IDLE && playerState != DOWNLOADING && playerState != PlayerState.PREPARING) {
+ int duration = 0;
try {
duration = mediaPlayer.getDuration();
} catch (Exception x) {
duration = 0;
}
- if(duration != 0) {
- return duration;
- }
- }
+ if(duration != 0) {
+ return duration;
+ }
+ }
- if (currentPlaying != null) {
- Integer duration = currentPlaying.getSong().getDuration();
- if (duration != null) {
- return duration * 1000;
- }
- }
+ if (currentPlaying != null) {
+ Integer duration = currentPlaying.getSong().getDuration();
+ if (duration != null) {
+ return duration * 1000;
+ }
+ }
- return 0;
- }
+ return 0;
+ }
- public PlayerState getPlayerState() {
- return playerState;
- }
+ public PlayerState getPlayerState() {
+ return playerState;
+ }
- public PlayerState getNextPlayerState() {
- return nextPlayerState;
- }
+ public PlayerState getNextPlayerState() {
+ return nextPlayerState;
+ }
- public synchronized void setPlayerState(final PlayerState playerState) {
- Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")");
+ public synchronized void setPlayerState(final PlayerState playerState) {
+ Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")");
- if (playerState == PAUSED) {
- lifecycleSupport.serializeDownloadQueue();
- }
+ if (playerState == PAUSED) {
+ lifecycleSupport.serializeDownloadQueue();
+ }
- boolean show = playerState == PlayerState.STARTED;
- boolean pause = playerState == PlayerState.PAUSED;
- boolean hide = playerState == PlayerState.STOPPED;
- Util.broadcastPlaybackStatusChange(this, (currentPlaying != null) ? currentPlaying.getSong() : null, playerState);
+ boolean show = playerState == PlayerState.STARTED;
+ boolean pause = playerState == PlayerState.PAUSED;
+ boolean hide = playerState == PlayerState.STOPPED;
+ Util.broadcastPlaybackStatusChange(this, (currentPlaying != null) ? currentPlaying.getSong() : null, playerState);
- this.playerState = playerState;
+ this.playerState = playerState;
- if(playerState == STARTED) {
- Util.requestAudioFocus(this);
- }
+ if(playerState == STARTED) {
+ Util.requestAudioFocus(this);
+ }
- if (show) {
- Notifications.showPlayingNotification(this, this, handler, currentPlaying.getSong());
- } else if (pause) {
- SharedPreferences prefs = Util.getPreferences(this);
- if(prefs.getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false)) {
- Notifications.showPlayingNotification(this, this, handler, currentPlaying.getSong());
- } else {
- Notifications.hidePlayingNotification(this, this, handler);
- }
- } else if(hide) {
- Notifications.hidePlayingNotification(this, this, handler);
- }
- if(playerState == STARTED && positionCache == null) {
+ if (show) {
+ Notifications.showPlayingNotification(this, this, handler, currentPlaying.getSong());
+ } else if (pause) {
+ SharedPreferences prefs = Util.getPreferences(this);
+ if(prefs.getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false)) {
+ Notifications.showPlayingNotification(this, this, handler, currentPlaying.getSong());
+ } else {
+ Notifications.hidePlayingNotification(this, this, handler);
+ }
+ } else if(hide) {
+ Notifications.hidePlayingNotification(this, this, handler);
+ }
+ if(playerState == STARTED && positionCache == null) {
positionCache = new LocalPositionCache();
- Thread thread = new Thread(positionCache, "PositionCache");
- thread.start();
- } else if(playerState != STARTED && positionCache != null) {
- positionCache.stop();
- positionCache = null;
- }
+ Thread thread = new Thread(positionCache, "PositionCache");
+ thread.start();
+ } else if(playerState != STARTED && positionCache != null) {
+ positionCache.stop();
+ positionCache = null;
+ }
- onStateUpdate();
- }
+ onStateUpdate();
+ }
- public void setPlayerStateCompleted() {
- // Acquire a temporary wakelock
- acquireWakelock();
+ public void setPlayerStateCompleted() {
+ // Acquire a temporary wakelock
+ acquireWakelock();
- Log.i(TAG, this.playerState.name() + " -> " + PlayerState.COMPLETED + " (" + currentPlaying + ")");
- this.playerState = PlayerState.COMPLETED;
- if(positionCache != null) {
- positionCache.stop();
- positionCache = null;
- }
+ Log.i(TAG, this.playerState.name() + " -> " + PlayerState.COMPLETED + " (" + currentPlaying + ")");
+ this.playerState = PlayerState.COMPLETED;
+ if(positionCache != null) {
+ positionCache.stop();
+ positionCache = null;
+ }
- onStateUpdate();
- }
+ onStateUpdate();
+ }
- private class PositionCache implements Runnable {
- boolean isRunning = true;
+ private class PositionCache implements Runnable {
+ boolean isRunning = true;
- public void stop() {
- isRunning = false;
- }
+ public void stop() {
+ isRunning = false;
+ }
- @Override
- public void run() {
- // Stop checking position before the song reaches completion
- while(isRunning) {
- try {
- onSongProgress();
- Thread.sleep(delayUpdateProgress);
- }
- catch(Exception e) {
- isRunning = false;
- positionCache = null;
- }
- }
- }
- }
- private class LocalPositionCache extends PositionCache {
- boolean isRunning = true;
+ @Override
+ public void run() {
+ // Stop checking position before the song reaches completion
+ while(isRunning) {
+ try {
+ onSongProgress();
+ Thread.sleep(delayUpdateProgress);
+ }
+ catch(Exception e) {
+ isRunning = false;
+ positionCache = null;
+ }
+ }
+ }
+ }
+ private class LocalPositionCache extends PositionCache {
+ boolean isRunning = true;
- public void stop() {
- isRunning = false;
- }
+ 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) {
- int newPosition = mediaPlayer.getCurrentPosition();
+ @Override
+ public void run() {
+ // Stop checking position before the song reaches completion
+ while(isRunning) {
+ try {
+ if(mediaPlayer != null && playerState == STARTED) {
+ int newPosition = mediaPlayer.getCurrentPosition();
- // If sudden jump in position, something is wrong
- if(subtractNextPosition == 0 && newPosition > (cachedPosition + 5000)) {
- // Only 1 second should have gone by, subtract the rest
- subtractPosition += (newPosition - cachedPosition) - 1000;
- }
+ // If sudden jump in position, something is wrong
+ if(subtractNextPosition == 0 && newPosition > (cachedPosition + 5000)) {
+ // Only 1 second should have gone by, subtract the rest
+ subtractPosition += (newPosition - cachedPosition) - 1000;
+ }
- cachedPosition = newPosition;
+ cachedPosition = newPosition;
- if(subtractNextPosition > 0) {
- // Subtraction amount is current position - how long ago onCompletionListener was called
- subtractPosition = cachedPosition - (int) (System.currentTimeMillis() - subtractNextPosition);
- if(subtractPosition < 0) {
- subtractPosition = 0;
- }
- subtractNextPosition = 0;
- }
- }
- onSongProgress(cachedPosition < 2000 ? true: false);
- Thread.sleep(delayUpdateProgress);
- }
- catch(Exception e) {
- Log.w(TAG, "Crashed getting current position", e);
- isRunning = false;
- positionCache = null;
- }
- }
- }
- }
+ if(subtractNextPosition > 0) {
+ // Subtraction amount is current position - how long ago onCompletionListener was called
+ subtractPosition = cachedPosition - (int) (System.currentTimeMillis() - subtractNextPosition);
+ if(subtractPosition < 0) {
+ subtractPosition = 0;
+ }
+ subtractNextPosition = 0;
+ }
+ }
+ onSongProgress(cachedPosition < 2000 ? true: false);
+ Thread.sleep(delayUpdateProgress);
+ }
+ catch(Exception e) {
+ Log.w(TAG, "Crashed getting current position", e);
+ isRunning = false;
+ positionCache = null;
+ }
+ }
+ }
+ }
- public synchronized void setNextPlayerState(PlayerState playerState) {
- Log.i(TAG, "Next: " + this.nextPlayerState.name() + " -> " + playerState.name() + " (" + nextPlaying + ")");
- this.nextPlayerState = playerState;
- }
+ public synchronized void setNextPlayerState(PlayerState playerState) {
+ Log.i(TAG, "Next: " + this.nextPlayerState.name() + " -> " + playerState.name() + " (" + nextPlaying + ")");
+ this.nextPlayerState = playerState;
+ }
- public void setSuggestedPlaylistName(String name, String id) {
- this.suggestedPlaylistName = name;
- this.suggestedPlaylistId = id;
+ public void setSuggestedPlaylistName(String name, String id) {
+ this.suggestedPlaylistName = name;
+ this.suggestedPlaylistId = id;
- SharedPreferences.Editor editor = Util.getPreferences(this).edit();
- editor.putString(Constants.PREFERENCES_KEY_PLAYLIST_NAME, name);
- editor.putString(Constants.PREFERENCES_KEY_PLAYLIST_ID, id);
- editor.commit();
- }
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putString(Constants.PREFERENCES_KEY_PLAYLIST_NAME, name);
+ editor.putString(Constants.PREFERENCES_KEY_PLAYLIST_ID, id);
+ editor.commit();
+ }
- public String getSuggestedPlaylistName() {
- return suggestedPlaylistName;
- }
+ public String getSuggestedPlaylistName() {
+ return suggestedPlaylistName;
+ }
- public String getSuggestedPlaylistId() {
- return suggestedPlaylistId;
- }
+ public String getSuggestedPlaylistId() {
+ return suggestedPlaylistId;
+ }
- public EqualizerController getEqualizerController() {
- EqualizerController controller = null;
- try {
- controller = effectsController.getEqualizerController();
- if(controller.getEqualizer() == null) {
- throw new Exception("Failed to get EQ");
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to start EQ, retrying with new mediaPlayer: " + e);
+ public EqualizerController getEqualizerController() {
+ EqualizerController controller = null;
+ try {
+ controller = effectsController.getEqualizerController();
+ if(controller.getEqualizer() == null) {
+ throw new Exception("Failed to get EQ");
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to start EQ, retrying with new mediaPlayer: " + e);
- // If we failed, we are going to try to reinitialize the MediaPlayer
- boolean playing = playerState == STARTED;
- int pos = getPlayerPosition();
- mediaPlayer.pause();
- Util.sleepQuietly(10L);
- reset();
+ // If we failed, we are going to try to reinitialize the MediaPlayer
+ boolean playing = playerState == STARTED;
+ int pos = getPlayerPosition();
+ mediaPlayer.pause();
+ Util.sleepQuietly(10L);
+ reset();
- try {
- // Resetup media player
- mediaPlayer.setAudioSessionId(audioSessionId);
- mediaPlayer.setDataSource(currentPlaying.getFile().getCanonicalPath());
+ try {
+ // Resetup media player
+ mediaPlayer.setAudioSessionId(audioSessionId);
+ mediaPlayer.setDataSource(currentPlaying.getFile().getCanonicalPath());
- controller = effectsController.getEqualizerController();
- if(controller.getEqualizer() == null) {
- throw new Exception("Failed to get EQ");
- }
- } catch(Exception e2) {
- Log.w(TAG, "Failed to setup EQ even after reinitialization");
- // Don't try again, just resetup media player and continue on
- controller = null;
- }
+ controller = effectsController.getEqualizerController();
+ if(controller.getEqualizer() == null) {
+ throw new Exception("Failed to get EQ");
+ }
+ } catch(Exception e2) {
+ Log.w(TAG, "Failed to setup EQ even after reinitialization");
+ // Don't try again, just resetup media player and continue on
+ controller = null;
+ }
- // Restart from same position and state we left off in
- play(getCurrentPlayingIndex(), false, pos);
- }
+ // Restart from same position and state we left off in
+ play(getCurrentPlayingIndex(), false, pos);
+ }
- return controller;
- }
+ return controller;
+ }
- public boolean isSeekable() {
+ public boolean isSeekable() {
return currentPlaying != null && currentPlaying.isWorkDone() && playerState != PREPARING;
- }
+ }
- public void updateRemoteVolume(boolean up) {
- AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
- audioManager.adjustVolume(up ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
- }
+ public void updateRemoteVolume(boolean up) {
+ AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
+ audioManager.adjustVolume(up ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
+ }
- private synchronized void bufferAndPlay() {
- bufferAndPlay(0);
- }
- private synchronized void bufferAndPlay(int position) {
- bufferAndPlay(position, true);
- }
- private synchronized void bufferAndPlay(int position, boolean start) {
- if(!currentPlaying.isCompleteFileAvailable()) {
- if(Util.isAllowedToDownload(this)) {
- reset();
+ private synchronized void bufferAndPlay() {
+ bufferAndPlay(0);
+ }
+ private synchronized void bufferAndPlay(int position) {
+ bufferAndPlay(position, true);
+ }
+ private synchronized void bufferAndPlay(int position, boolean start) {
+ if(!currentPlaying.isCompleteFileAvailable()) {
+ if(Util.isAllowedToDownload(this)) {
+ reset();
- bufferTask = new BufferTask(currentPlaying, position, start);
- bufferTask.execute();
- } else {
- next(false, start);
- }
- } else {
- doPlay(currentPlaying, position, start);
- }
- }
+ bufferTask = new BufferTask(currentPlaying, position, start);
+ bufferTask.execute();
+ } else {
+ next(false, start);
+ }
+ } else {
+ doPlay(currentPlaying, position, start);
+ }
+ }
- private synchronized void doPlay(final DownloadFile downloadFile, final int position, final boolean start) {
- try {
- subtractPosition = 0;
- mediaPlayer.setOnCompletionListener(null);
- mediaPlayer.setOnPreparedListener(null);
- mediaPlayer.setOnErrorListener(null);
- mediaPlayer.reset();
- setPlayerState(IDLE);
- try {
- mediaPlayer.setAudioSessionId(audioSessionId);
- } catch(Throwable e) {
- mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- }
+ private synchronized void doPlay(final DownloadFile downloadFile, final int position, final boolean start) {
+ try {
+ subtractPosition = 0;
+ mediaPlayer.setOnCompletionListener(null);
+ mediaPlayer.setOnPreparedListener(null);
+ mediaPlayer.setOnErrorListener(null);
+ mediaPlayer.reset();
+ setPlayerState(IDLE);
+ try {
+ mediaPlayer.setAudioSessionId(audioSessionId);
+ } catch(Throwable e) {
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ }
- String dataSource;
- boolean isPartial = false;
+ String dataSource;
+ boolean isPartial = false;
downloadFile.setPlaying(true);
final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
isPartial = file.equals(downloadFile.getPartialFile());
@@ -1468,797 +1468,797 @@ public class DownloadService extends Service {
proxy = null;
}
- mediaPlayer.setDataSource(dataSource);
- setPlayerState(PREPARING);
+ mediaPlayer.setDataSource(dataSource);
+ setPlayerState(PREPARING);
- mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
- Log.i(TAG, "Buffered " + percent + "%");
- if (percent == 100) {
- mediaPlayer.setOnBufferingUpdateListener(null);
- }
- }
- });
+ mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ Log.i(TAG, "Buffered " + percent + "%");
+ if (percent == 100) {
+ mediaPlayer.setOnBufferingUpdateListener(null);
+ }
+ }
+ });
- mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
- public void onPrepared(MediaPlayer mediaPlayer) {
- try {
- setPlayerState(PREPARED);
+ mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mediaPlayer) {
+ try {
+ setPlayerState(PREPARED);
- synchronized (DownloadService.this) {
- if (position != 0) {
- Log.i(TAG, "Restarting player from position " + position);
- mediaPlayer.seekTo(position);
- }
- cachedPosition = position;
+ synchronized (DownloadService.this) {
+ if (position != 0) {
+ Log.i(TAG, "Restarting player from position " + position);
+ mediaPlayer.seekTo(position);
+ }
+ cachedPosition = position;
- applyReplayGain(mediaPlayer, downloadFile);
+ applyReplayGain(mediaPlayer, downloadFile);
- if (start || autoPlayStart) {
- mediaPlayer.start();
- setPlayerState(STARTED);
+ if (start || autoPlayStart) {
+ mediaPlayer.start();
+ setPlayerState(STARTED);
- // Disable autoPlayStart after done
- autoPlayStart = false;
- } else {
- setPlayerState(PAUSED);
- onSongProgress();
- }
+ // Disable autoPlayStart after done
+ autoPlayStart = false;
+ } else {
+ setPlayerState(PAUSED);
+ onSongProgress();
+ }
- updateRemotePlaylist();
- }
+ updateRemotePlaylist();
+ }
- // Only call when starting, setPlayerState(PAUSED) already calls this
- if(start) {
- lifecycleSupport.serializeDownloadQueue();
- }
- } catch (Exception x) {
- handleError(x);
- }
- }
- });
+ // Only call when starting, setPlayerState(PAUSED) already calls this
+ if(start) {
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+ });
- setupHandlers(downloadFile, isPartial, start);
+ setupHandlers(downloadFile, isPartial, start);
- mediaPlayer.prepareAsync();
- } catch (Exception x) {
- handleError(x);
- }
- }
+ mediaPlayer.prepareAsync();
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
- private synchronized void setupNext(final DownloadFile downloadFile) {
- try {
- final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
- resetNext();
+ private synchronized void setupNext(final DownloadFile downloadFile) {
+ try {
+ final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
+ resetNext();
- nextMediaPlayer = new MediaPlayer();
- nextMediaPlayer.setWakeMode(DownloadService.this, PowerManager.PARTIAL_WAKE_LOCK);
- try {
- nextMediaPlayer.setAudioSessionId(audioSessionId);
- } catch(Throwable e) {
- nextMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- }
- nextMediaPlayer.setDataSource(file.getPath());
- setNextPlayerState(PREPARING);
+ nextMediaPlayer = new MediaPlayer();
+ nextMediaPlayer.setWakeMode(DownloadService.this, PowerManager.PARTIAL_WAKE_LOCK);
+ try {
+ nextMediaPlayer.setAudioSessionId(audioSessionId);
+ } catch(Throwable e) {
+ nextMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ }
+ nextMediaPlayer.setDataSource(file.getPath());
+ setNextPlayerState(PREPARING);
- nextMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
- public void onPrepared(MediaPlayer mp) {
- // Changed to different while preparing so ignore
- if(nextMediaPlayer != mp) {
- return;
- }
+ nextMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mp) {
+ // Changed to different while preparing so ignore
+ if(nextMediaPlayer != mp) {
+ return;
+ }
- try {
- setNextPlayerState(PREPARED);
+ try {
+ setNextPlayerState(PREPARED);
- if(playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED) {
- mediaPlayer.setNextMediaPlayer(nextMediaPlayer);
- nextSetup = true;
- }
+ if(playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED) {
+ mediaPlayer.setNextMediaPlayer(nextMediaPlayer);
+ nextSetup = true;
+ }
- applyReplayGain(nextMediaPlayer, downloadFile);
- } catch (Exception x) {
- handleErrorNext(x);
- }
- }
- });
+ applyReplayGain(nextMediaPlayer, downloadFile);
+ } 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.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);
- }
- }
+ nextMediaPlayer.prepareAsync();
+ } catch (Exception x) {
+ handleErrorNext(x);
+ }
+ }
- private void setupHandlers(final DownloadFile downloadFile, final boolean isPartial, final boolean isPlaying) {
- final int duration = downloadFile.getSong().getDuration() == null ? 0 : downloadFile.getSong().getDuration() * 1000;
- 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 = getPlayerPosition();
- reset();
- if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) {
- playNext();
- } else {
- downloadFile.setPlaying(false);
- doPlay(downloadFile, pos, isPlaying);
- downloadFile.setPlaying(true);
- }
- return true;
- }
- });
+ private void setupHandlers(final DownloadFile downloadFile, final boolean isPartial, final boolean isPlaying) {
+ final int duration = downloadFile.getSong().getDuration() == null ? 0 : downloadFile.getSong().getDuration() * 1000;
+ 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 = getPlayerPosition();
+ reset();
+ if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) {
+ playNext();
+ } else {
+ downloadFile.setPlaying(false);
+ doPlay(downloadFile, pos, isPlaying);
+ downloadFile.setPlaying(true);
+ }
+ return true;
+ }
+ });
- mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
- @Override
- public void onCompletion(MediaPlayer mediaPlayer) {
- setPlayerStateCompleted();
+ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ setPlayerStateCompleted();
- int pos = getPlayerPosition();
- Log.i(TAG, "Ending position " + pos + " of " + duration);
- if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000)) || nextSetup) {
- playNext();
- postPlayCleanup(downloadFile);
- } else {
- // If file is not completely downloaded, restart the playback from the current position.
- synchronized (DownloadService.this) {
- if (downloadFile.isWorkDone()) {
- // 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, true);
- bufferTask.execute();
- }
- }
- checkDownloads();
- }
- }
- });
- }
+ int pos = getPlayerPosition();
+ Log.i(TAG, "Ending position " + pos + " of " + duration);
+ if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000)) || nextSetup) {
+ playNext();
+ postPlayCleanup(downloadFile);
+ } else {
+ // If file is not completely downloaded, restart the playback from the current position.
+ synchronized (DownloadService.this) {
+ if (downloadFile.isWorkDone()) {
+ // 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, true);
+ bufferTask.execute();
+ }
+ }
+ checkDownloads();
+ }
+ }
+ });
+ }
- public void setVolume(float volume) {
- if(mediaPlayer != null && (playerState == STARTED || playerState == PAUSED || playerState == STOPPED)) {
- try {
- this.volume = volume;
- reapplyVolume();
- } catch(Exception e) {
- Log.w(TAG, "Failed to set volume");
- }
- }
- }
+ public void setVolume(float volume) {
+ if(mediaPlayer != null && (playerState == STARTED || playerState == PAUSED || playerState == STOPPED)) {
+ try {
+ this.volume = volume;
+ reapplyVolume();
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to set volume");
+ }
+ }
+ }
- public void reapplyVolume() {
- applyReplayGain(mediaPlayer, currentPlaying);
- }
+ public void reapplyVolume() {
+ applyReplayGain(mediaPlayer, currentPlaying);
+ }
- public synchronized void swap(boolean mainList, DownloadFile from, DownloadFile to) {
- List list = mainList ? downloadList : backgroundDownloadList;
- swap(mainList, list.indexOf(from), list.indexOf(to));
- }
- public synchronized void swap(boolean mainList, int from, int to) {
- List list = mainList ? downloadList : backgroundDownloadList;
- int max = list.size();
- if(to >= max) {
- to = max - 1;
- }
- else if(to < 0) {
- to = 0;
- }
+ public synchronized void swap(boolean mainList, DownloadFile from, DownloadFile to) {
+ List list = mainList ? downloadList : backgroundDownloadList;
+ swap(mainList, list.indexOf(from), list.indexOf(to));
+ }
+ public synchronized void swap(boolean mainList, int from, int to) {
+ List list = mainList ? downloadList : backgroundDownloadList;
+ int max = list.size();
+ if(to >= max) {
+ to = max - 1;
+ }
+ else if(to < 0) {
+ to = 0;
+ }
- DownloadFile movedSong = list.remove(from);
- list.add(to, movedSong);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- if(mainList) {
+ DownloadFile movedSong = list.remove(from);
+ list.add(to, movedSong);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ if(mainList) {
// Moving next playing, current playing, or moving a song to be next playing
if(movedSong == nextPlaying || movedSong == currentPlaying || (currentPlayingIndex + 1) == to) {
setNextPlaying();
}
- }
- }
+ }
+ }
- public synchronized void serializeQueue() {
- serializeQueue(true);
- }
- public synchronized void serializeQueue(boolean serializeRemote) {
- if(playerState == PlayerState.PAUSED) {
- lifecycleSupport.serializeDownloadQueue(serializeRemote);
- }
- }
+ public synchronized void serializeQueue() {
+ serializeQueue(true);
+ }
+ public synchronized void serializeQueue(boolean serializeRemote) {
+ if(playerState == PlayerState.PAUSED) {
+ lifecycleSupport.serializeDownloadQueue(serializeRemote);
+ }
+ }
- private void handleError(Exception x) {
- Log.w(TAG, "Media player error: " + x, x);
- if(mediaPlayer != null) {
- try {
- mediaPlayer.reset();
- } catch(Exception e) {
- Log.e(TAG, "Failed to reset player in error handler");
- }
- }
- setPlayerState(IDLE);
- }
- private void handleErrorNext(Exception x) {
- Log.w(TAG, "Next Media player error: " + x, x);
- try {
- nextMediaPlayer.reset();
- } catch(Exception e) {
- Log.e(TAG, "Failed to reset next media player", x);
- }
- setNextPlayerState(IDLE);
- }
+ private void handleError(Exception x) {
+ Log.w(TAG, "Media player error: " + x, x);
+ if(mediaPlayer != null) {
+ try {
+ mediaPlayer.reset();
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to reset player in error handler");
+ }
+ }
+ setPlayerState(IDLE);
+ }
+ private void handleErrorNext(Exception x) {
+ Log.w(TAG, "Next Media player error: " + x, x);
+ try {
+ nextMediaPlayer.reset();
+ } catch(Exception e) {
+ Log.e(TAG, "Failed to reset next media player", x);
+ }
+ setNextPlayerState(IDLE);
+ }
- public synchronized void checkDownloads() {
- if (!Util.isExternalStoragePresent() || !lifecycleSupport.isExternalStorageAvailable()) {
- return;
- }
+ public synchronized void checkDownloads() {
+ if (!Util.isExternalStoragePresent() || !lifecycleSupport.isExternalStorageAvailable()) {
+ return;
+ }
- if(removePlayed) {
- checkRemovePlayed();
- }
- if (shufflePlay) {
- checkShufflePlay();
- }
+ if(removePlayed) {
+ checkRemovePlayed();
+ }
+ if (shufflePlay) {
+ checkShufflePlay();
+ }
- if (!Util.isAllowedToDownload(this)) {
- return;
- }
+ if (!Util.isAllowedToDownload(this)) {
+ return;
+ }
- if (downloadList.isEmpty() && backgroundDownloadList.isEmpty()) {
- return;
- }
+ if (downloadList.isEmpty() && backgroundDownloadList.isEmpty()) {
+ return;
+ }
- // Need to download current playing?
- if (currentPlaying != null && currentPlaying != currentDownloading && !currentPlaying.isWorkDone()) {
- // Cancel current download, if necessary.
- if (currentDownloading != null) {
- currentDownloading.cancelDownload();
- }
+ // Need to download current playing?
+ if (currentPlaying != null && currentPlaying != currentDownloading && !currentPlaying.isWorkDone()) {
+ // Cancel current download, if necessary.
+ if (currentDownloading != null) {
+ currentDownloading.cancelDownload();
+ }
- currentDownloading = currentPlaying;
- currentDownloading.download();
- cleanupCandidates.add(currentDownloading);
- }
+ currentDownloading = currentPlaying;
+ currentDownloading.download();
+ cleanupCandidates.add(currentDownloading);
+ }
- // Find a suitable target for download.
- else if (currentDownloading == null || currentDownloading.isWorkDone() || currentDownloading.isFailed() && (!downloadList.isEmpty() || !backgroundDownloadList.isEmpty())) {
- currentDownloading = null;
- int n = size();
+ // Find a suitable target for download.
+ else if (currentDownloading == null || currentDownloading.isWorkDone() || currentDownloading.isFailed() && (!downloadList.isEmpty() || !backgroundDownloadList.isEmpty())) {
+ currentDownloading = null;
+ int n = size();
- int preloaded = 0;
+ int preloaded = 0;
- 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() && !downloadFile.isFailedMax()) {
- 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++;
- }
+ 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() && !downloadFile.isFailedMax()) {
+ 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);
- }
+ i = (i + 1) % n;
+ } while (i != start);
+ }
- 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()) || downloadFile.isFailedMax()) {
- // Don't need to keep list like active song list
- backgroundDownloadList.remove(i);
- revision++;
- i--;
- } else {
- currentDownloading = downloadFile;
- currentDownloading.download();
- cleanupCandidates.add(currentDownloading);
- break;
- }
- }
- }
- }
+ 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()) || downloadFile.isFailedMax()) {
+ // Don't need to keep list like active song list
+ backgroundDownloadList.remove(i);
+ revision++;
+ i--;
+ } else {
+ currentDownloading = downloadFile;
+ currentDownloading.download();
+ cleanupCandidates.add(currentDownloading);
+ break;
+ }
+ }
+ }
+ }
- if(!backgroundDownloadList.isEmpty()) {
- Notifications.showDownloadingNotification(this, this, handler, currentDownloading, backgroundDownloadList.size());
- downloadOngoing = true;
- } else if(backgroundDownloadList.isEmpty() && downloadOngoing) {
- Notifications.hideDownloadingNotification(this, this, handler);
- downloadOngoing = false;
- }
+ if(!backgroundDownloadList.isEmpty()) {
+ Notifications.showDownloadingNotification(this, this, handler, currentDownloading, backgroundDownloadList.size());
+ downloadOngoing = true;
+ } else if(backgroundDownloadList.isEmpty() && downloadOngoing) {
+ Notifications.hideDownloadingNotification(this, this, handler);
+ downloadOngoing = false;
+ }
- // Delete obsolete .partial and .complete files.
- cleanup();
- }
+ // Delete obsolete .partial and .complete files.
+ cleanup();
+ }
- private synchronized void checkRemovePlayed() {
- boolean changed = false;
- SharedPreferences prefs = Util.getPreferences(this);
- int keepCount = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_KEEP_PLAYED_CNT, "0"));
- while(currentPlayingIndex > keepCount) {
- downloadList.remove(0);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- changed = true;
- }
+ private synchronized void checkRemovePlayed() {
+ boolean changed = false;
+ SharedPreferences prefs = Util.getPreferences(this);
+ int keepCount = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_KEEP_PLAYED_CNT, "0"));
+ while(currentPlayingIndex > keepCount) {
+ downloadList.remove(0);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ changed = true;
+ }
- if(changed) {
- revision++;
- onSongsChanged();
- }
- }
+ if(changed) {
+ revision++;
+ onSongsChanged();
+ }
+ }
- private synchronized void checkShufflePlay() {
+ private synchronized void checkShufflePlay() {
- // Get users desired random playlist size
- SharedPreferences prefs = Util.getPreferences(this);
- int listSize = Math.max(1, Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20")));
- boolean wasEmpty = downloadList.isEmpty();
+ // Get users desired random playlist size
+ SharedPreferences prefs = Util.getPreferences(this);
+ int listSize = Math.max(1, Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20")));
+ boolean wasEmpty = downloadList.isEmpty();
- long revisionBefore = revision;
+ 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++;
- }
- }
+ // 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();
+ 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++;
- }
- }
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ // 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++;
+ }
+ }
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
- if (revisionBefore != revision) {
- onSongsChanged();
- updateRemotePlaylist();
- }
+ if (revisionBefore != revision) {
+ onSongsChanged();
+ updateRemotePlaylist();
+ }
- if (wasEmpty && !downloadList.isEmpty()) {
- play(0);
- }
- }
+ if (wasEmpty && !downloadList.isEmpty()) {
+ play(0);
+ }
+ }
- public long getDownloadListUpdateRevision() {
- return revision;
- }
+ public long getDownloadListUpdateRevision() {
+ return revision;
+ }
- private synchronized void cleanup() {
- Iterator iterator = cleanupCandidates.iterator();
- while (iterator.hasNext()) {
- DownloadFile downloadFile = iterator.next();
- if (downloadFile != currentPlaying && downloadFile != currentDownloading) {
- if (downloadFile.cleanup()) {
- iterator.remove();
- }
- }
- }
- }
+ private synchronized void cleanup() {
+ Iterator iterator = cleanupCandidates.iterator();
+ while (iterator.hasNext()) {
+ DownloadFile downloadFile = iterator.next();
+ if (downloadFile != currentPlaying && downloadFile != currentDownloading) {
+ if (downloadFile.cleanup()) {
+ iterator.remove();
+ }
+ }
+ }
+ }
- public void postPlayCleanup() {
- postPlayCleanup(currentPlaying);
- }
- public void postPlayCleanup(DownloadFile downloadFile) {
- if(downloadFile == null) {
- return;
- }
- }
+ public void postPlayCleanup() {
+ postPlayCleanup(currentPlaying);
+ }
+ public void postPlayCleanup(DownloadFile downloadFile) {
+ if(downloadFile == null) {
+ return;
+ }
+ }
- private boolean isPastCutoff() {
- return isPastCutoff(getPlayerPosition(), getPlayerDuration());
- }
- private boolean isPastCutoff(int position, int duration) {
- return isPastCutoff(position, duration, false);
- }
- private boolean isPastCutoff(int position, int duration, boolean allowSkipping) {
- if(currentPlaying == null) {
- return false;
- }
+ private boolean isPastCutoff() {
+ return isPastCutoff(getPlayerPosition(), getPlayerDuration());
+ }
+ private boolean isPastCutoff(int position, int duration) {
+ return isPastCutoff(position, duration, false);
+ }
+ private boolean isPastCutoff(int position, int duration, boolean allowSkipping) {
+ if(currentPlaying == null) {
+ return false;
+ }
- // Make cutoff a maximum of 10 minutes
- int cutoffPoint = Math.max((int) (duration * DELETE_CUTOFF), duration - 10 * 60 * 1000);
- boolean isPastCutoff = duration > 0 && position > cutoffPoint;
-
- return isPastCutoff;
- }
-
- private void applyReplayGain(MediaPlayer mediaPlayer, DownloadFile downloadFile) {
- if(currentPlaying == null) {
- return;
- }
+ // Make cutoff a maximum of 10 minutes
+ int cutoffPoint = Math.max((int) (duration * DELETE_CUTOFF), duration - 10 * 60 * 1000);
+ boolean isPastCutoff = duration > 0 && position > cutoffPoint;
- SharedPreferences prefs = Util.getPreferences(this);
- try {
- float adjust = 0f;
- if (prefs.getBoolean(Constants.PREFERENCES_KEY_REPLAY_GAIN, false)) {
- float[] rg = BastpUtil.getReplayGainValues(downloadFile.getFile().getCanonicalPath()); /* track, album */
- boolean singleAlbum = false;
-
- String replayGainType = prefs.getString(Constants.PREFERENCES_KEY_REPLAY_GAIN_TYPE, "1");
- // 1 => Smart replay gain
- if("1".equals(replayGainType)) {
- // Check if part of at least consequetive songs of the same album
-
- int index = downloadList.indexOf(downloadFile);
- if(index != -1) {
- String albumName = downloadFile.getSong().getAlbum();
- int matched = 0;
-
- // Check forwards
- for(int i = index + 1; i < downloadList.size() && matched < REQUIRED_ALBUM_MATCHES; i++) {
- if(albumName.equals(downloadList.get(i).getSong().getAlbum())) {
- matched++;
- } else {
- break;
- }
- }
-
- // Check backwards
- for(int i = index - 1; i >= 0 && matched < REQUIRED_ALBUM_MATCHES; i--) {
- if(albumName.equals(downloadList.get(i).getSong().getAlbum())) {
- matched++;
- } else {
- break;
- }
- }
-
- if(matched >= REQUIRED_ALBUM_MATCHES) {
- singleAlbum = true;
- }
- }
- }
- // 2 => Use album tags
- else if("2".equals(replayGainType)) {
- singleAlbum = true;
- }
- // 3 => Use track tags
- // Already false, no need to do anything here
-
-
- // If playing a single album or no track gain, use album gain
- if((singleAlbum || rg[0] == 0) && rg[1] != 0) {
- adjust = rg[1];
- } else {
- // Otherwise, give priority to track gain
- adjust = rg[0];
- }
-
- if (adjust == 0) {
- /* No RG value found: decrease volume for untagged song if requested by user */
- int untagged = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_REPLAY_GAIN_UNTAGGED, "0"));
- adjust = (untagged - 150) / 10f;
- } else {
- int bump = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_REPLAY_GAIN_BUMP, "150"));
- adjust += (bump - 150) / 10f;
- }
- }
-
- float rg_result = ((float) Math.pow(10, (adjust / 20))) * volume;
- if (rg_result > 1.0f) {
- rg_result = 1.0f; /* android would IGNORE the change if this is > 1 and we would end up with the wrong volume */
- } else if (rg_result < 0.0f) {
- rg_result = 0.0f;
- }
- mediaPlayer.setVolume(rg_result, rg_result);
- } catch(IOException e) {
- Log.w(TAG, "Failed to apply replay gain values", e);
- }
- }
+ return isPastCutoff;
+ }
- private synchronized boolean isNextPlayingSameAlbum() {
- return isNextPlayingSameAlbum(currentPlaying, nextPlaying);
- }
- private synchronized boolean isNextPlayingSameAlbum(DownloadFile currentPlaying, DownloadFile nextPlaying) {
- if(currentPlaying == null || nextPlaying == null) {
- return false;
- } else {
- return currentPlaying.getSong().getAlbum().equals(nextPlaying.getSong().getAlbum());
- }
- }
+ private void applyReplayGain(MediaPlayer mediaPlayer, DownloadFile downloadFile) {
+ if(currentPlaying == null) {
+ return;
+ }
- public void acquireWakelock() {
- acquireWakelock(30000);
- }
- public void acquireWakelock(int ms) {
- wakeLock.acquire(ms);
- }
+ SharedPreferences prefs = Util.getPreferences(this);
+ try {
+ float adjust = 0f;
+ if (prefs.getBoolean(Constants.PREFERENCES_KEY_REPLAY_GAIN, false)) {
+ float[] rg = BastpUtil.getReplayGainValues(downloadFile.getFile().getCanonicalPath()); /* track, album */
+ boolean singleAlbum = false;
- public void handleKeyEvent(KeyEvent keyEvent) {
- lifecycleSupport.handleKeyEvent(keyEvent);
- }
+ String replayGainType = prefs.getString(Constants.PREFERENCES_KEY_REPLAY_GAIN_TYPE, "1");
+ // 1 => Smart replay gain
+ if("1".equals(replayGainType)) {
+ // Check if part of at least consequetive songs of the same album
- public void addOnSongChangedListener(OnSongChangedListener listener) {
- addOnSongChangedListener(listener, false);
- }
- public void addOnSongChangedListener(OnSongChangedListener listener, boolean run) {
- synchronized(onSongChangedListeners) {
- int index = onSongChangedListeners.indexOf(listener);
- if (index == -1) {
- onSongChangedListeners.add(listener);
- }
- }
+ int index = downloadList.indexOf(downloadFile);
+ if(index != -1) {
+ String albumName = downloadFile.getSong().getAlbum();
+ int matched = 0;
- if(run) {
- if(mediaPlayerHandler != null) {
- mediaPlayerHandler.post(new Runnable() {
- @Override
- public void run() {
- onSongsChanged();
- onSongProgress();
- onStateUpdate();
- onMetadataUpdate(METADATA_UPDATED_ALL);
- }
- });
- } else {
- runListenersOnInit = true;
- }
- }
- }
- public void removeOnSongChangeListener(OnSongChangedListener listener) {
- synchronized(onSongChangedListeners) {
- int index = onSongChangedListeners.indexOf(listener);
- if (index != -1) {
- onSongChangedListeners.remove(index);
- }
- }
- }
+ // Check forwards
+ for(int i = index + 1; i < downloadList.size() && matched < REQUIRED_ALBUM_MATCHES; i++) {
+ if(albumName.equals(downloadList.get(i).getSong().getAlbum())) {
+ matched++;
+ } else {
+ break;
+ }
+ }
- private void onSongChanged() {
- final long atRevision = revision;
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onSongChanged(currentPlaying, currentPlayingIndex);
+ // Check backwards
+ for(int i = index - 1; i >= 0 && matched < REQUIRED_ALBUM_MATCHES; i--) {
+ if(albumName.equals(downloadList.get(i).getSong().getAlbum())) {
+ matched++;
+ } else {
+ break;
+ }
+ }
- MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
- listener.onMetadataUpdate(entry, METADATA_UPDATED_ALL);
- }
- }
- });
- }
+ if(matched >= REQUIRED_ALBUM_MATCHES) {
+ singleAlbum = true;
+ }
+ }
+ }
+ // 2 => Use album tags
+ else if("2".equals(replayGainType)) {
+ singleAlbum = true;
+ }
+ // 3 => Use track tags
+ // Already false, no need to do anything here
- if (mediaPlayerHandler != null && !onSongChangedListeners.isEmpty()) {
- mediaPlayerHandler.post(new Runnable() {
- @Override
- public void run() {
- onSongProgress();
- }
- });
- }
- }
- }
- private void onSongsChanged() {
- final long atRevision = revision;
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onSongsChanged(downloadList, currentPlaying, currentPlayingIndex);
- }
- }
- });
- }
- }
- }
- private void onSongProgress() {
- onSongProgress(true);
- }
- private synchronized void onSongProgress(boolean manual) {
- final long atRevision = revision;
- final Integer duration = getPlayerDuration();
- final boolean isSeekable = isSeekable();
- final int position = getPlayerPosition();
+ // If playing a single album or no track gain, use album gain
+ if((singleAlbum || rg[0] == 0) && rg[1] != 0) {
+ adjust = rg[1];
+ } else {
+ // Otherwise, give priority to track gain
+ adjust = rg[0];
+ }
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onSongProgress(currentPlaying, position, duration, isSeekable);
- }
- }
- });
- }
- }
+ if (adjust == 0) {
+ /* No RG value found: decrease volume for untagged song if requested by user */
+ int untagged = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_REPLAY_GAIN_UNTAGGED, "0"));
+ adjust = (untagged - 150) / 10f;
+ } else {
+ int bump = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_REPLAY_GAIN_BUMP, "150"));
+ adjust += (bump - 150) / 10f;
+ }
+ }
- if(manual) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- }
- });
- }
- }
- private void onStateUpdate() {
- final long atRevision = revision;
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (revision == atRevision && instance != null) {
- listener.onStateUpdate(currentPlaying, playerState);
- }
- }
- });
- }
- }
- }
- public void onMetadataUpdate() {
- onMetadataUpdate(METADATA_UPDATED_ALL);
- }
- public void onMetadataUpdate(final int updateType) {
- synchronized(onSongChangedListeners) {
- for (final OnSongChangedListener listener : onSongChangedListeners) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- if (instance != null) {
- MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
- listener.onMetadataUpdate(entry, updateType);
- }
- }
- });
- }
- }
+ float rg_result = ((float) Math.pow(10, (adjust / 20))) * volume;
+ if (rg_result > 1.0f) {
+ rg_result = 1.0f; /* android would IGNORE the change if this is > 1 and we would end up with the wrong volume */
+ } else if (rg_result < 0.0f) {
+ rg_result = 0.0f;
+ }
+ mediaPlayer.setVolume(rg_result, rg_result);
+ } catch(IOException e) {
+ Log.w(TAG, "Failed to apply replay gain values", e);
+ }
+ }
- handler.post(new Runnable() {
- @Override
- public void run() {
- }
- });
- }
+ private synchronized boolean isNextPlayingSameAlbum() {
+ return isNextPlayingSameAlbum(currentPlaying, nextPlaying);
+ }
+ private synchronized boolean isNextPlayingSameAlbum(DownloadFile currentPlaying, DownloadFile nextPlaying) {
+ if(currentPlaying == null || nextPlaying == null) {
+ return false;
+ } else {
+ return currentPlaying.getSong().getAlbum().equals(nextPlaying.getSong().getAlbum());
+ }
+ }
- private class BufferTask extends SilentBackgroundTask {
- private final DownloadFile downloadFile;
- private final int position;
- private final long expectedFileSize;
- private final File partialFile;
- private final boolean start;
+ public void acquireWakelock() {
+ acquireWakelock(30000);
+ }
+ public void acquireWakelock(int ms) {
+ wakeLock.acquire(ms);
+ }
- public BufferTask(DownloadFile downloadFile, int position, boolean start) {
- super(instance);
- this.downloadFile = downloadFile;
- this.position = position;
- partialFile = downloadFile.getPartialFile();
- this.start = start;
+ public void handleKeyEvent(KeyEvent keyEvent) {
+ lifecycleSupport.handleKeyEvent(keyEvent);
+ }
- // Calculate roughly how many bytes BUFFER_LENGTH_SECONDS corresponds to.
- int bitRate = downloadFile.getBitRate();
- long byteCount = Math.max(100000, bitRate * 1024L / 8L * 5L);
+ public void addOnSongChangedListener(OnSongChangedListener listener) {
+ addOnSongChangedListener(listener, false);
+ }
+ public void addOnSongChangedListener(OnSongChangedListener listener, boolean run) {
+ synchronized(onSongChangedListeners) {
+ int index = onSongChangedListeners.indexOf(listener);
+ if (index == -1) {
+ onSongChangedListeners.add(listener);
+ }
+ }
- // 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;
- }
+ if(run) {
+ if(mediaPlayerHandler != null) {
+ mediaPlayerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onSongsChanged();
+ onSongProgress();
+ onStateUpdate();
+ onMetadataUpdate(METADATA_UPDATED_ALL);
+ }
+ });
+ } else {
+ runListenersOnInit = true;
+ }
+ }
+ }
+ public void removeOnSongChangeListener(OnSongChangedListener listener) {
+ synchronized(onSongChangedListeners) {
+ int index = onSongChangedListeners.indexOf(listener);
+ if (index != -1) {
+ onSongChangedListeners.remove(index);
+ }
+ }
+ }
- @Override
- public Void doInBackground() throws InterruptedException {
- setPlayerState(DOWNLOADING);
+ private void onSongChanged() {
+ final long atRevision = revision;
+ synchronized(onSongChangedListeners) {
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onSongChanged(currentPlaying, currentPlayingIndex);
- while (!bufferComplete()) {
- Thread.sleep(1000L);
- if (isCancelled() || downloadFile.isFailedMax()) {
- return null;
- } else if(!downloadFile.isFailedMax() && !downloadFile.isDownloading()) {
- checkDownloads();
- }
- }
- doPlay(downloadFile, position, start);
+ MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
+ listener.onMetadataUpdate(entry, METADATA_UPDATED_ALL);
+ }
+ }
+ });
+ }
- return null;
- }
+ if (mediaPlayerHandler != null && !onSongChangedListeners.isEmpty()) {
+ mediaPlayerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onSongProgress();
+ }
+ });
+ }
+ }
+ }
+ private void onSongsChanged() {
+ final long atRevision = revision;
+ synchronized(onSongChangedListeners) {
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onSongsChanged(downloadList, currentPlaying, currentPlayingIndex);
+ }
+ }
+ });
+ }
+ }
+ }
- private boolean bufferComplete() {
- boolean completeFileAvailable = downloadFile.isWorkDone();
- long size = partialFile.length();
+ private void onSongProgress() {
+ onSongProgress(true);
+ }
+ private synchronized void onSongProgress(boolean manual) {
+ final long atRevision = revision;
+ final Integer duration = getPlayerDuration();
+ final boolean isSeekable = isSeekable();
+ final int position = getPlayerPosition();
- Log.i(TAG, "Buffering " + partialFile + " (" + size + "/" + expectedFileSize + ", " + completeFileAvailable + ")");
- return completeFileAvailable || size >= expectedFileSize;
- }
+ synchronized(onSongChangedListeners) {
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onSongProgress(currentPlaying, position, duration, isSeekable);
+ }
+ }
+ });
+ }
+ }
- @Override
- public String toString() {
- return "BufferTask (" + downloadFile + ")";
- }
- }
+ if(manual) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ }
+ });
+ }
+ }
+ private void onStateUpdate() {
+ final long atRevision = revision;
+ synchronized(onSongChangedListeners) {
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (revision == atRevision && instance != null) {
+ listener.onStateUpdate(currentPlaying, playerState);
+ }
+ }
+ });
+ }
+ }
+ }
+ public void onMetadataUpdate() {
+ onMetadataUpdate(METADATA_UPDATED_ALL);
+ }
+ public void onMetadataUpdate(final int updateType) {
+ synchronized(onSongChangedListeners) {
+ for (final OnSongChangedListener listener : onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (instance != null) {
+ MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null;
+ listener.onMetadataUpdate(entry, updateType);
+ }
+ }
+ });
+ }
+ }
- private class CheckCompletionTask extends SilentBackgroundTask {
- private final DownloadFile downloadFile;
- private final File partialFile;
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ }
+ });
+ }
- public CheckCompletionTask(DownloadFile downloadFile) {
- super(instance);
- this.downloadFile = downloadFile;
- if(downloadFile != null) {
- partialFile = downloadFile.getPartialFile();
- } else {
- partialFile = null;
- }
- }
+ private class BufferTask extends SilentBackgroundTask {
+ private final DownloadFile downloadFile;
+ private final int position;
+ private final long expectedFileSize;
+ private final File partialFile;
+ private final boolean start;
- @Override
- public Void doInBackground() throws InterruptedException {
- if(downloadFile == null) {
- return null;
- }
+ public BufferTask(DownloadFile downloadFile, int position, boolean start) {
+ super(instance);
+ this.downloadFile = downloadFile;
+ this.position = position;
+ partialFile = downloadFile.getPartialFile();
+ this.start = start;
- // Do an initial sleep so this prepare can't compete with main prepare
- Thread.sleep(5000L);
- while (!bufferComplete()) {
- Thread.sleep(5000L);
- if (isCancelled()) {
- return null;
- }
- }
+ // Calculate roughly how many bytes BUFFER_LENGTH_SECONDS corresponds to.
+ int bitRate = downloadFile.getBitRate();
+ long byteCount = Math.max(100000, bitRate * 1024L / 8L * 5L);
- // Start the setup of the next media player
- mediaPlayerHandler.post(new Runnable() {
- public void run() {
- if(!CheckCompletionTask.this.isCancelled()) {
- setupNext(downloadFile);
- }
- }
- });
- return null;
- }
+ // 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;
+ }
- private boolean bufferComplete() {
- boolean completeFileAvailable = downloadFile.isWorkDone();
- Log.i(TAG, "Buffering next " + partialFile + " (" + partialFile.length() + "): " + completeFileAvailable);
- return completeFileAvailable && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED);
- }
+ @Override
+ public Void doInBackground() throws InterruptedException {
+ setPlayerState(DOWNLOADING);
- @Override
- public String toString() {
- return "CheckCompletionTask (" + downloadFile + ")";
- }
- }
+ while (!bufferComplete()) {
+ Thread.sleep(1000L);
+ if (isCancelled() || downloadFile.isFailedMax()) {
+ return null;
+ } else if(!downloadFile.isFailedMax() && !downloadFile.isDownloading()) {
+ checkDownloads();
+ }
+ }
+ doPlay(downloadFile, position, start);
- public interface OnSongChangedListener {
- void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex);
- void onSongsChanged(List songs, DownloadFile currentPlaying, int currentPlayingIndex);
- void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable);
- void onStateUpdate(DownloadFile downloadFile, PlayerState playerState);
- void onMetadataUpdate(MusicDirectory.Entry entry, int fieldChange);
- }
+ return null;
+ }
+
+ private boolean bufferComplete() {
+ boolean completeFileAvailable = downloadFile.isWorkDone();
+ long size = partialFile.length();
+
+ Log.i(TAG, "Buffering " + partialFile + " (" + size + "/" + expectedFileSize + ", " + completeFileAvailable + ")");
+ return completeFileAvailable || size >= expectedFileSize;
+ }
+
+ @Override
+ public String toString() {
+ return "BufferTask (" + downloadFile + ")";
+ }
+ }
+
+ private class CheckCompletionTask extends SilentBackgroundTask {
+ private final DownloadFile downloadFile;
+ private final File partialFile;
+
+ public CheckCompletionTask(DownloadFile downloadFile) {
+ super(instance);
+ this.downloadFile = downloadFile;
+ if(downloadFile != null) {
+ partialFile = downloadFile.getPartialFile();
+ } else {
+ partialFile = null;
+ }
+ }
+
+ @Override
+ public Void doInBackground() throws InterruptedException {
+ if(downloadFile == null) {
+ return null;
+ }
+
+ // Do an initial sleep so this prepare can't compete with main prepare
+ Thread.sleep(5000L);
+ while (!bufferComplete()) {
+ Thread.sleep(5000L);
+ if (isCancelled()) {
+ return null;
+ }
+ }
+
+ // Start the setup of the next media player
+ mediaPlayerHandler.post(new Runnable() {
+ public void run() {
+ if(!CheckCompletionTask.this.isCancelled()) {
+ setupNext(downloadFile);
+ }
+ }
+ });
+ return null;
+ }
+
+ private boolean bufferComplete() {
+ boolean completeFileAvailable = downloadFile.isWorkDone();
+ Log.i(TAG, "Buffering next " + partialFile + " (" + partialFile.length() + "): " + completeFileAvailable);
+ return completeFileAvailable && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED);
+ }
+
+ @Override
+ public String toString() {
+ return "CheckCompletionTask (" + downloadFile + ")";
+ }
+ }
+
+ public interface OnSongChangedListener {
+ void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex);
+ void onSongsChanged(List songs, DownloadFile currentPlaying, int currentPlayingIndex);
+ void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable);
+ void onStateUpdate(DownloadFile downloadFile, PlayerState playerState);
+ void onMetadataUpdate(MusicDirectory.Entry entry, int fieldChange);
+ }
}
diff --git a/app/src/main/java/net/nullsum/audinaut/service/MusicService.java b/app/src/main/java/net/nullsum/audinaut/service/MusicService.java
index dcc3bdb..97ca82a 100644
--- a/app/src/main/java/net/nullsum/audinaut/service/MusicService.java
+++ b/app/src/main/java/net/nullsum/audinaut/service/MusicService.java
@@ -20,7 +20,7 @@ package net.nullsum.audinaut.service;
import java.util.List;
-import org.apache.http.HttpResponse;
+import okhttp3.Response;
import android.content.Context;
import android.graphics.Bitmap;
@@ -87,7 +87,7 @@ public interface MusicService {
Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception;
- HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception;
+ Response getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception;
String getMusicUrl(Context context, MusicDirectory.Entry song, int maxBitrate) throws Exception;
diff --git a/app/src/main/java/net/nullsum/audinaut/service/OfflineMusicService.java b/app/src/main/java/net/nullsum/audinaut/service/OfflineMusicService.java
index 2824303..961c743 100644
--- a/app/src/main/java/net/nullsum/audinaut/service/OfflineMusicService.java
+++ b/app/src/main/java/net/nullsum/audinaut/service/OfflineMusicService.java
@@ -34,7 +34,7 @@ import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.util.Log;
-import org.apache.http.HttpResponse;
+import okhttp3.Response;
import net.nullsum.audinaut.domain.Artist;
import net.nullsum.audinaut.domain.Genre;
@@ -206,7 +206,7 @@ public class OfflineMusicService implements MusicService {
}
@Override
- public HttpResponse getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
+ public Response getDownloadInputStream(Context context, Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
throw new OfflineException(ERRORMSG);
}
diff --git a/app/src/main/java/net/nullsum/audinaut/service/RESTMusicService.java b/app/src/main/java/net/nullsum/audinaut/service/RESTMusicService.java
index 0690236..5a54dba 100644
--- a/app/src/main/java/net/nullsum/audinaut/service/RESTMusicService.java
+++ b/app/src/main/java/net/nullsum/audinaut/service/RESTMusicService.java
@@ -34,37 +34,6 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.TimeUnit;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.params.ConnManagerParams;
-import org.apache.http.conn.params.ConnPerRouteBean;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.scheme.SocketFactory;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.protocol.ExecutionContext;
-import org.apache.http.protocol.HttpContext;
-
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@@ -97,8 +66,6 @@ import net.nullsum.audinaut.service.parser.PlaylistsParser;
import net.nullsum.audinaut.service.parser.RandomSongsParser;
import net.nullsum.audinaut.service.parser.SearchResult2Parser;
import net.nullsum.audinaut.service.parser.UserParser;
-import net.nullsum.audinaut.service.ssl.SSLSocketFactory;
-import net.nullsum.audinaut.service.ssl.TrustSelfSignedStrategy;
import net.nullsum.audinaut.util.BackgroundTask;
import net.nullsum.audinaut.util.Pair;
import net.nullsum.audinaut.util.SilentBackgroundTask;
@@ -130,46 +97,13 @@ public class RESTMusicService implements MusicService {
private static final int HTTP_REQUEST_MAX_ATTEMPTS = 5;
private static final long REDIRECTION_CHECK_INTERVAL_MILLIS = 60L * 60L * 1000L;
- private final DefaultHttpClient httpClient;
private long redirectionLastChecked;
private int redirectionNetworkType = -1;
private String redirectFrom;
private String redirectTo;
- private final ThreadSafeClientConnManager connManager;
private Integer instance;
public RESTMusicService() {
-
- // Create and initialize default HTTP parameters
- HttpParams params = new BasicHttpParams();
- ConnManagerParams.setMaxTotalConnections(params, 20);
- ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(20));
- HttpConnectionParams.setConnectionTimeout(params, SOCKET_CONNECT_TIMEOUT);
- HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_DEFAULT);
-
- // Turn off stale checking. Our connections break all the time anyway,
- // and it's not worth it to pay the penalty of checking every time.
- HttpConnectionParams.setStaleCheckingEnabled(params, false);
-
- // Create and initialize scheme registry
- SchemeRegistry schemeRegistry = new SchemeRegistry();
- schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schemeRegistry.register(new Scheme("https", createSSLSocketFactory(), 443));
-
- // Create an HttpClient with the ThreadSafeClientConnManager.
- // This connection manager must be used if more than one thread will
- // be using the HttpClient.
- connManager = new ThreadSafeClientConnManager(params, schemeRegistry);
- httpClient = new DefaultHttpClient(connManager, params);
- }
-
- private SocketFactory createSSLSocketFactory() {
- try {
- return new SSLSocketFactory(new TrustSelfSignedStrategy(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
- } catch (Throwable x) {
- Log.e(TAG, "Failed to create custom SSL socket factory, using default.", x);
- return org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
- }
}
@Override
@@ -744,48 +678,35 @@ public class RESTMusicService implements MusicService {
}
@Override
- public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
+ public Response getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
+
+ OkHttpClient eagerClient = client.newBuilder()
+ .readTimeout(30, TimeUnit.SECONDS)
+ .build();
String url = getRestUrl(context, "stream");
- // Set socket read timeout. Note: The timeout increases as the offset gets larger. This is
- // to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server.
- // In that case, the server uses a long time before sending any data, causing the client to time out.
- HttpParams params = new BasicHttpParams();
- int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE);
- HttpConnectionParams.setSoTimeout(params, timeout);
+ url += "&id=" + song.getId();
- // Add "Range" header if offset is given.
- List headers = new ArrayList();
+ Log.i(TAG, "Using music URL: " + url);
+
+ Builder builder = new FormBody.Builder();
+ builder.add("id", song.getId());
+ builder.add("maxBitRate", Integer.toString(maxBitrate));
+
+ RequestBody formBody = builder.build();
+
+ Request.Builder requestBuilder= new Request.Builder();
if (offset > 0) {
- headers.add(new BasicHeader("Range", "bytes=" + offset + "-"));
+ requestBuilder.header("Range", "bytes=" + offset + "-");
}
- List parameterNames = new ArrayList();
- parameterNames.add("id");
- parameterNames.add("maxBitRate");
+ requestBuilder.url(url);
+// requestBuilder.post(formBody);
- List
- *
- *
- *
- * Issue a certificate signing request (CSR)
- *
keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore
- *
- *
- *
- *
- * Send the certificate request to the trusted Certificate Authority for signature.
- * One may choose to act as her own CA and sign the certificate request using a PKI
- * tool, such as OpenSSL.
- *
- *
- *
- *
- * Import the trusted CA root certificate
- *
keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore
- *
- *
- *
- *
- * Import the PKCS#7 file containg the complete certificate chain
- *
keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore
- *
- *
- *
- *
- * Verify the content the resultant keystore file
- *
keytool -list -v -keystore my.keystore
- *
- *
- *
- *
- * @since 4.0
- */
-public class SSLSocketFactory implements LayeredSocketFactory {
- private static final String TAG = SSLSocketFactory.class.getSimpleName();
- public static final String TLS = "TLS";
-
- public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
- = new AllowAllHostnameVerifier();
-
- public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
- = new BrowserCompatHostnameVerifier();
-
- public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
- = new StrictHostnameVerifier();
-
- /**
- * The default factory using the default JVM settings for secure connections.
- */
- private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
-
- /**
- * Gets the default factory, which uses the default JVM settings for secure
- * connections.
- *
- * @return the default factory
- */
- public static SSLSocketFactory getSocketFactory() {
- return DEFAULT_FACTORY;
- }
-
- private final javax.net.ssl.SSLSocketFactory socketfactory;
- private final HostNameResolver nameResolver;
- // TODO: make final
- private volatile X509HostnameVerifier hostnameVerifier;
-
- private static SSLContext createSSLContext(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final TrustStrategy trustStrategy)
- throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException {
- if (algorithm == null) {
- algorithm = TLS;
- }
- KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
- KeyManagerFactory.getDefaultAlgorithm());
- kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray(): null);
- KeyManager[] keymanagers = kmfactory.getKeyManagers();
- TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
- TrustManagerFactory.getDefaultAlgorithm());
- tmfactory.init(keystore);
- TrustManager[] trustmanagers = tmfactory.getTrustManagers();
- if (trustmanagers != null && trustStrategy != null) {
- for (int i = 0; i < trustmanagers.length; i++) {
- TrustManager tm = trustmanagers[i];
- if (tm instanceof X509TrustManager) {
- trustmanagers[i] = new TrustManagerDecorator(
- (X509TrustManager) tm, trustStrategy);
- }
- }
- }
-
- SSLContext sslcontext = SSLContext.getInstance(algorithm);
- sslcontext.init(keymanagers, trustmanagers, random);
- return sslcontext;
- }
-
- /**
- * @deprecated Use {@link #SSLSocketFactory(String, KeyStore, String, KeyStore, SecureRandom, X509HostnameVerifier)}
- */
- @Deprecated
- public SSLSocketFactory(
- final String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final HostNameResolver nameResolver)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, null),
- nameResolver);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, null),
- hostnameVerifier);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final TrustStrategy trustStrategy,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, trustStrategy),
- hostnameVerifier);
- }
-
- public SSLSocketFactory(
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, keystore, keystorePassword, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(
- final KeyStore keystore,
- final String keystorePassword)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{
- this(TLS, keystore, keystorePassword, null, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(
- final KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final TrustStrategy trustStrategy,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, null, null, trustStrategy, hostnameVerifier);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final TrustStrategy trustStrategy)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, null, null, trustStrategy, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(final SSLContext sslContext) {
- this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- /**
- * @deprecated Use {@link #SSLSocketFactory(SSLContext)}
- */
- @Deprecated
- public SSLSocketFactory(
- final SSLContext sslContext, final HostNameResolver nameResolver) {
- super();
- this.socketfactory = sslContext.getSocketFactory();
- this.hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
- this.nameResolver = nameResolver;
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) {
- super();
- this.socketfactory = sslContext.getSocketFactory();
- this.hostnameVerifier = hostnameVerifier;
- this.nameResolver = null;
- }
-
- private SSLSocketFactory() {
- super();
- this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
- this.hostnameVerifier = null;
- this.nameResolver = null;
- }
-
- /**
- * @param params Optional parameters. Parameters passed to this method will have no effect.
- * This method will create a unconnected instance of {@link Socket} class
- * using {@link javax.net.ssl.SSLSocketFactory#createSocket()} method.
- * @since 4.1
- */
- @SuppressWarnings("cast")
- public Socket createSocket(final HttpParams params) throws IOException {
- // the cast makes sure that the factory is working as expected
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket();
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- return sslSocket;
- }
-
- @SuppressWarnings("cast")
- public Socket createSocket() throws IOException {
- // the cast makes sure that the factory is working as expected
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket();
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- return sslSocket;
- }
-
- /**
- * @since 4.1
- */
- public Socket connectSocket(
- final Socket sock,
- final InetSocketAddress remoteAddress,
- final InetSocketAddress localAddress,
- final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
- if (remoteAddress == null) {
- throw new IllegalArgumentException("Remote address may not be null");
- }
- if (params == null) {
- throw new IllegalArgumentException("HTTP parameters may not be null");
- }
- SSLSocket sslsock = (SSLSocket) (sock != null ? sock : createSocket());
- if (localAddress != null) {
-// sslsock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
- sslsock.bind(localAddress);
- }
-
- setHostName(sslsock, remoteAddress.getHostName());
- int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
- int soTimeout = HttpConnectionParams.getSoTimeout(params);
-
- try {
- sslsock.connect(remoteAddress, connTimeout);
- } catch (SocketTimeoutException ex) {
- throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
- + remoteAddress.getAddress() + " timed out");
- }
- sslsock.setSoTimeout(soTimeout);
- if (this.hostnameVerifier != null) {
- try {
- this.hostnameVerifier.verify(remoteAddress.getHostName(), sslsock);
- // verifyHostName() didn't blowup - good!
- } catch (IOException iox) {
- // close the socket before re-throwing the exception
- try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
- throw iox;
- }
- }
- return sslsock;
- }
-
-
- /**
- * Checks whether a socket connection is secure.
- * This factory creates TLS/SSL socket connections
- * which, by default, are considered secure.
- *
- * Derived classes may override this method to perform
- * runtime checks, for example based on the cypher suite.
- *
- * @param sock the connected socket
- *
- * @return true
- *
- * @throws IllegalArgumentException if the argument is invalid
- */
- public boolean isSecure(final Socket sock) throws IllegalArgumentException {
- if (sock == null) {
- throw new IllegalArgumentException("Socket may not be null");
- }
- // This instanceof check is in line with createSocket() above.
- if (!(sock instanceof SSLSocket)) {
- throw new IllegalArgumentException("Socket not created by this factory");
- }
- // This check is performed last since it calls the argument object.
- if (sock.isClosed()) {
- throw new IllegalArgumentException("Socket is closed");
- }
- return true;
- }
-
- /**
- * @since 4.1
- */
- public Socket createLayeredSocket(
- final Socket socket,
- final String host,
- final int port,
- final boolean autoClose) throws IOException, UnknownHostException {
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
- socket,
- host,
- port,
- autoClose
- );
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- if (this.hostnameVerifier != null) {
- this.hostnameVerifier.verify(host, sslSocket);
- }
- // verifyHostName() didn't blowup - good!
- return sslSocket;
- }
-
- @Deprecated
- public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
- if ( hostnameVerifier == null ) {
- throw new IllegalArgumentException("Hostname verifier may not be null");
- }
- this.hostnameVerifier = hostnameVerifier;
- }
-
- public X509HostnameVerifier getHostnameVerifier() {
- return this.hostnameVerifier;
- }
-
- /**
- * @deprecated Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)}
- */
- @Deprecated
- public Socket connectSocket(
- final Socket socket,
- final String host, int port,
- final InetAddress localAddress, int localPort,
- final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
- InetSocketAddress local = null;
- if (localAddress != null || localPort > 0) {
- // we need to bind explicitly
- if (localPort < 0) {
- localPort = 0; // indicates "any"
- }
- local = new InetSocketAddress(localAddress, localPort);
- }
- InetAddress remoteAddress;
- if (this.nameResolver != null) {
- remoteAddress = this.nameResolver.resolve(host);
- } else {
- remoteAddress = InetAddress.getByName(host);
- }
- InetSocketAddress remote = new InetSocketAddress(remoteAddress, port);
- return connectSocket(socket, remote, local, params);
- }
-
- /**
- * @deprecated Use {@link #createLayeredSocket(Socket, String, int, boolean)}
- */
- @Deprecated
- public Socket createSocket(
- final Socket socket,
- final String host, int port,
- boolean autoClose) throws IOException, UnknownHostException {
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(socket, host, port, autoClose);
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- setHostName(sslSocket, host);
- return sslSocket;
- }
-
- private void setHostName(SSLSocket sslsock, String hostname){
- try {
- java.lang.reflect.Method setHostnameMethod = sslsock.getClass().getMethod("setHostname", String.class);
- setHostnameMethod.invoke(sslsock, hostname);
- } catch (Exception e) {
- Log.w(TAG, "SNI not useable", e);
- }
- }
-
- private String[] getProtocols(SSLSocket sslSocket) {
- String[] protocols = sslSocket.getEnabledProtocols();
-
- // Remove SSLv3 if it is not the only option
- if(protocols.length > 1) {
- List protocolList = new ArrayList(Arrays.asList(protocols));
- protocolList.remove("SSLv3");
- protocols = protocolList.toArray(new String[protocolList.size()]);
- }
-
- return protocols;
- }
-
- private String[] getCiphers(SSLSocket sslSocket) {
- String[] ciphers = sslSocket.getEnabledCipherSuites();
-
- List enabledCiphers = new ArrayList(Arrays.asList(ciphers));
- // On Android 5.0 release, Jetty doesn't seem to play nice with these ciphers
- // Issue seems to have been fixed in M, and now won't work without them. Because Google
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- enabledCiphers.remove("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
- enabledCiphers.remove("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
- }
-
- ciphers = enabledCiphers.toArray(new String[enabledCiphers.size()]);
- return ciphers;
- }
-}
diff --git a/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustManagerDecorator.java b/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustManagerDecorator.java
deleted file mode 100644
index 563b35f..0000000
--- a/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustManagerDecorator.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * .
- *
- */
-package net.nullsum.audinaut.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.X509TrustManager;
-
-
-/**
- * @since 4.1
- */
-class TrustManagerDecorator implements X509TrustManager {
-
- private final X509TrustManager trustManager;
- private final TrustStrategy trustStrategy;
-
- TrustManagerDecorator(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
- super();
- this.trustManager = trustManager;
- this.trustStrategy = trustStrategy;
- }
-
- public void checkClientTrusted(
- final X509Certificate[] chain, final String authType) throws CertificateException {
- this.trustManager.checkClientTrusted(chain, authType);
- }
-
- public void checkServerTrusted(
- final X509Certificate[] chain, final String authType) throws CertificateException {
- if (!this.trustStrategy.isTrusted(chain, authType)) {
- this.trustManager.checkServerTrusted(chain, authType);
- }
- }
-
- public X509Certificate[] getAcceptedIssuers() {
- return this.trustManager.getAcceptedIssuers();
- }
-
-}
diff --git a/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustSelfSignedStrategy.java b/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustSelfSignedStrategy.java
deleted file mode 100644
index 07aefff..0000000
--- a/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustSelfSignedStrategy.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * .
- *
- */
-package net.nullsum.audinaut.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-/**
- * A trust strategy that accepts self-signed certificates as trusted. Verification of all other
- * certificates is done by the trust manager configured in the SSL context.
- *
- * @since 4.1
- */
-public class TrustSelfSignedStrategy implements TrustStrategy {
-
- public boolean isTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
- return true;
- }
-
-}
diff --git a/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustStrategy.java b/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustStrategy.java
deleted file mode 100644
index aa84047..0000000
--- a/app/src/main/java/net/nullsum/audinaut/service/ssl/TrustStrategy.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * .
- *
- */
-package net.nullsum.audinaut.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-/**
- * A strategy to establish trustworthiness of certificates without consulting the trust manager
- * configured in the actual SSL context. This interface can be used to override the standard
- * JSSE certificate verification process.
- *
- * @since 4.1
- */
-public interface TrustStrategy {
-
- /**
- * Determines whether the certificate chain can be trusted without consulting the trust manager
- * configured in the actual SSL context. This method can be used to override the standard JSSE
- * certificate verification process.
- *
- * Please note that, if this method returns false, the trust manager configured
- * in the actual SSL context can still clear the certificate as trusted.
- *
- * @param chain the peer certificate chain
- * @param authType the authentication type based on the client certificate
- * @return true if the certificate can be trusted without verification by
- * the trust manager, false otherwise.
- * @throws CertificateException thrown if the certificate is not trusted or invalid.
- */
- boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException;
-
-}
diff --git a/app/src/main/java/net/nullsum/audinaut/util/Util.java b/app/src/main/java/net/nullsum/audinaut/util/Util.java
index b3b80a0..60e56bb 100644
--- a/app/src/main/java/net/nullsum/audinaut/util/Util.java
+++ b/app/src/main/java/net/nullsum/audinaut/util/Util.java
@@ -65,8 +65,6 @@ import net.nullsum.audinaut.domain.RepeatMode;
import net.nullsum.audinaut.receiver.MediaButtonIntentReceiver;
import net.nullsum.audinaut.service.DownloadService;
-import org.apache.http.HttpEntity;
-
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;