aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2014-02-24 16:08:37 -0800
committerScott Jackson <daneren2005@gmail.com>2014-02-24 16:08:37 -0800
commit3f9b769df513093500bcd1466bc178cac74763b9 (patch)
tree7141165047a2c771477ec94d057f16d980c2394f
parentbd6f50faf3f7b4a60d224363d8b3e73bd4f4a478 (diff)
parent1ad51c4b9c8fcea55a517e626f9747e0a78d33f6 (diff)
downloaddsub-3f9b769df513093500bcd1466bc178cac74763b9.tar.gz
dsub-3f9b769df513093500bcd1466bc178cac74763b9.tar.bz2
dsub-3f9b769df513093500bcd1466bc178cac74763b9.zip
Merge branch 'master' of github.com:daneren2005/Subsonic
-rw-r--r--.gitmodules3
-rw-r--r--AndroidManifest.xml4
-rw-r--r--README4
m---------ServerProxy0
-rw-r--r--Subsonic.iml1
-rw-r--r--project.properties3
-rw-r--r--res/layout/abstract_activity.xml3
-rw-r--r--res/menu/nowplaying_offline.xml9
-rw-r--r--res/raw/changelog.xml27
-rw-r--r--src/github/daneren2005/dsub/activity/SubsonicActivity.java2
-rw-r--r--src/github/daneren2005/dsub/fragments/DownloadFragment.java3
-rw-r--r--src/github/daneren2005/dsub/fragments/MainFragment.java4
-rw-r--r--src/github/daneren2005/dsub/service/ChromeCastController.java75
-rw-r--r--src/github/daneren2005/dsub/service/DownloadFile.java37
-rw-r--r--src/github/daneren2005/dsub/service/DownloadService.java52
-rw-r--r--src/github/daneren2005/dsub/service/JukeboxController.java2
-rw-r--r--src/github/daneren2005/dsub/service/RESTMusicService.java4
-rw-r--r--src/github/daneren2005/dsub/service/StreamProxy.java311
-rw-r--r--src/github/daneren2005/dsub/util/BackgroundTask.java9
-rw-r--r--src/github/daneren2005/dsub/util/LoadingTask.java2
-rw-r--r--src/github/daneren2005/dsub/util/MediaRouteManager.java19
21 files changed, 202 insertions, 372 deletions
diff --git a/.gitmodules b/.gitmodules
index 5a0b6b8c..dcb0d0b7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "DragSortListView"]
path = DragSortListView
url = https://github.com/bauerca/drag-sort-listview.git
+[submodule "ServerProxy"]
+ path = ServerProxy
+ url = https://github.com/daneren2005/ServerProxy.git
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e667dc22..f423f2d0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="github.daneren2005.dsub"
android:installLocation="internalOnly"
- android:versionCode="89"
- android:versionName="4.5 Beta 3">
+ android:versionCode="92"
+ android:versionName="4.5.2">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
diff --git a/README b/README
index f3e63f4c..b8f1a8a2 100644
--- a/README
+++ b/README
@@ -3,7 +3,7 @@ Run these commands to grab dependent libraries:
git submodule init
git submodule update
-Go to DragSortListView/library and build project files with:
+Go to DragSortListView/library, and ServerProxy and build project files with:
android update project --path ./
Replace the file DragSortListView/library/libs/android-support-v4.jar with the one from main projects libs/
@@ -11,12 +11,10 @@ Replace the file DragSortListView/library/libs/android-support-v4.jar with the o
Roadmap of major planned features in rough order that I plan to work on them in (little features get sprinkled in wherever):
-Tag Browsing
Album Art Grid View
-Display albums in a grid
-Display artists if supported (yes for tags, no for folders currently)
RemoteControl
- -Chromecast
-DLNA/UpNP Client
-DLNA/UpNP Renderer
New Tabs
diff --git a/ServerProxy b/ServerProxy
new file mode 160000
+Subproject 205d8b2bf878fb4350f0c456fe2526dacb6fa91
diff --git a/Subsonic.iml b/Subsonic.iml
index 31ebc6b4..0e656645 100644
--- a/Subsonic.iml
+++ b/Subsonic.iml
@@ -21,6 +21,7 @@
<orderEntry type="module" module-name="mediarouter" />
<orderEntry type="library" name="google-player-services" level="project" />
<orderEntry type="module" module-name="google-play-services_lib" />
+ <orderEntry type="module" module-name="ServerProxy" />
</component>
</module>
diff --git a/project.properties b/project.properties
index 80f83a52..d03eb683 100644
--- a/project.properties
+++ b/project.properties
@@ -12,4 +12,5 @@ target=android-19
android.library.reference.1=../../../../Program Files (x86)/Android/android-sdk/extras/android/support/v7/appcompat
android.library.reference.2=DragSortListView/library
android.library.reference.3=../../../../Program Files (x86)/Android/android-sdk/extras/android/support/v7/mediarouter
-android.library.reference.4=../../../../Program Files (x86)/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib \ No newline at end of file
+android.library.reference.4=../../../../Program Files (x86)/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib
+android.library.reference.5=ServerProxy \ No newline at end of file
diff --git a/res/layout/abstract_activity.xml b/res/layout/abstract_activity.xml
index c5bf45ca..784d753c 100644
--- a/res/layout/abstract_activity.xml
+++ b/res/layout/abstract_activity.xml
@@ -8,8 +8,7 @@
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clickable="true" />
+ android:layout_height="match_parent"/>
<!-- The navigation drawer -->
<ListView android:id="@+id/left_drawer"
android:layout_width="240dp"
diff --git a/res/menu/nowplaying_offline.xml b/res/menu/nowplaying_offline.xml
index 41d1d5d1..7656bf34 100644
--- a/res/menu/nowplaying_offline.xml
+++ b/res/menu/nowplaying_offline.xml
@@ -7,12 +7,19 @@
android:icon="?attr/shuffle"
android:title="@string/download.menu_shuffle"
compat:showAsAction="always|withText"/>
+
+ <item
+ android:id="@+id/menu_mediaroute"
+ compat:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
+ compat:actionViewClass="android.support.v7.app.MediaRouteButton"
+ compat:showAsAction="always"
+ android:title="@string/menu.cast"/>
<item
android:id="@+id/menu_remove_all"
android:icon="?attr/remove"
android:title="@string/download.menu_remove_all"
- compat:showAsAction="always|withText"/>
+ compat:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/menu_screen_on_off"
diff --git a/res/raw/changelog.xml b/res/raw/changelog.xml
index 9a12dcf3..2bc55816 100644
--- a/res/raw/changelog.xml
+++ b/res/raw/changelog.xml
@@ -1,29 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
- <release version="4.5 Beta 3" versioncode="89" releasedate="2/20/2014">
- <change>Fix keeping state (playing, position) when switching between Chromecast and back</change>
- <change>Fix play button switching to paused while it's still loading on Chromecast</change>
- <change>Fix for pressing next close together</change>
- <change>Fix some offline caching issues when sync is on with multiple servers</change>
- <change>Never sync the demo server</change>
- <change>Clear album art cache on exit</change>
+ <release version="4.5.2" versioncode="92" releasedate="2/23/2014">
+ <change>Add support for casting in offline mode</change>
+ <change>Use internal IP for ChromeCast if using a http address</change>
+ <change>Fix loading getting stuck on 4.1 and below devices</change>
</release>
- <release version="4.5 Beta 2" versioncode="88" releasedate="2/17/2014">
- <change>Fix loading when transcoded content type is null (ie: mp3 -> mp3)</change>
- <change>Fix multiple Jukebox options in cast menu</change>
- <change>Fix pressing next skipping twice</change>
- <change>Fix crash when going to download list</change>
- <change>Fix route state not being restored after exiting app and coming back later</change>
- <change>Memory optimizations</change>
+ <release version="4.5.1" versioncode="91" releasedate="2/21/2014">
+ <change>Fix crash on Android 2.2/2.3 devices</change>
</release>
- <release version="4.5 Beta 1" versioncode="87" releasedate="2/15/2014">
+ <release version="4.5" versioncode="90" releasedate="2/21/2014">
<change>Add ChromeCast support for music only (Android 2.3+ only)</change>
+ <change>Video/Offline mode to follow</change>
<change>More detailed download notification (Android 4.2+)</change>
<change>Cancel button on download notification (Android 4.2+)</change>
<change>Update background threading architecture to be more efficient</change>
+ <change>Memory optimizations</change>
+ <change>Never sync the demo server</change>
+ <change>Clear album art cache on exit</change>
<change>Fix cache cleaner not using pinned music in space calculations</change>
<change>Don't scrobble podcasts</change>
<change>Fix offline scrobbling bug</change>
+ <change>Fix some offline caching issues when sync is on with multiple servers</change>
</release>
<release version="4.4" versioncode="86" releasedate="2/5/2014">
<change>Browse by ID3 Tags instead of folder structure (off by default)</change>
diff --git a/src/github/daneren2005/dsub/activity/SubsonicActivity.java b/src/github/daneren2005/dsub/activity/SubsonicActivity.java
index c1534639..62349480 100644
--- a/src/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/src/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -649,7 +649,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
}
- public boolean isDestroyed() {
+ public boolean isDestroyedCompat() {
return destroyed;
}
diff --git a/src/github/daneren2005/dsub/fragments/DownloadFragment.java b/src/github/daneren2005/dsub/fragments/DownloadFragment.java
index 0a146b33..87cffd03 100644
--- a/src/github/daneren2005/dsub/fragments/DownloadFragment.java
+++ b/src/github/daneren2005/dsub/fragments/DownloadFragment.java
@@ -13,6 +13,7 @@ import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
+import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.MediaRouteButton;
import android.view.ContextMenu;
import android.view.Display;
@@ -505,7 +506,7 @@ public class DownloadFragment extends SubsonicFragment implements OnGestureListe
if(downloadService != null) {
MenuItem mediaRouteItem = menu.findItem(R.id.menu_mediaroute);
if(mediaRouteItem != null) {
- MediaRouteButton mediaRouteButton = (MediaRouteButton) mediaRouteItem.getActionView();
+ MediaRouteButton mediaRouteButton = (MediaRouteButton) MenuItemCompat.getActionView(mediaRouteItem);
mediaRouteButton.setRouteSelector(downloadService.getRemoteSelector());
}
}
diff --git a/src/github/daneren2005/dsub/fragments/MainFragment.java b/src/github/daneren2005/dsub/fragments/MainFragment.java
index ebb9318e..801238de 100644
--- a/src/github/daneren2005/dsub/fragments/MainFragment.java
+++ b/src/github/daneren2005/dsub/fragments/MainFragment.java
@@ -209,6 +209,10 @@ public class MainFragment extends SubsonicFragment {
boolean isOffline = Util.isOffline(context);
Util.setOffline(context, !isOffline);
context.invalidate();
+ DownloadService service = getDownloadService();
+ if (service != null) {
+ service.setOnline(isOffline);
+ }
if(isOffline) {
int scrobblesCount = Util.offlineScrobblesCount(context);
diff --git a/src/github/daneren2005/dsub/service/ChromeCastController.java b/src/github/daneren2005/dsub/service/ChromeCastController.java
index dc360797..02661f14 100644
--- a/src/github/daneren2005/dsub/service/ChromeCastController.java
+++ b/src/github/daneren2005/dsub/service/ChromeCastController.java
@@ -15,6 +15,7 @@
package github.daneren2005.dsub.service;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
@@ -32,14 +33,17 @@ import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.common.images.WebImage;
+import java.io.File;
import java.io.IOException;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.util.compat.CastCompat;
+import github.daneren2005.serverproxy.FileProxy;
/**
* Created by owner on 2/9/14.
@@ -58,12 +62,17 @@ public class ChromeCastController extends RemoteController {
private boolean error = false;
private boolean ignoreNextPaused = false;
+ private FileProxy proxy;
+ private String rootLocation;
private RemoteMediaPlayer mediaPlayer;
private double gain = 0.5;
public ChromeCastController(DownloadService downloadService, CastDevice castDevice) {
this.downloadService = downloadService;
this.castDevice = castDevice;
+
+ SharedPreferences prefs = Util.getPreferences(downloadService);
+ rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
}
@Override
@@ -154,6 +163,11 @@ public class ChromeCastController extends RemoteController {
apiClient.disconnect();
}
apiClient = null;
+
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
}
@Override
@@ -209,9 +223,30 @@ public class ChromeCastController extends RemoteController {
try {
MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
- String url = song.isVideo() ? musicService.getHlsUrl(song.getId(), currentPlaying.getBitRate(), downloadService) : musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate());
- // Use separate profile for Chromecast so users can do ogg on phone, mp3 for CC
- url = url.replace(Constants.REST_CLIENT_ID, Constants.CHROMECAST_CLIENT_ID);
+ String url;
+ // Offline, use file proxy
+ if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
+ if(proxy == null) {
+ proxy = new FileProxy(downloadService);
+ proxy.start();
+ }
+
+ url = proxy.getPublicAddress(song.getId());
+ } else {
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ if(song.isVideo()) {
+ url = musicService.getHlsUrl(song.getId(), currentPlaying.getBitRate(), downloadService);
+ } else {
+ url = musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate());
+ }
+
+ url = fixURLs(url);
+ Log.i(TAG, "Cast url: " + url);
+ }
// Setup song/video information
MediaMetadata meta = new MediaMetadata(song.isVideo() ? MediaMetadata.MEDIA_TYPE_MOVIE : MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
@@ -223,8 +258,20 @@ public class ChromeCastController extends RemoteController {
meta.putString(MediaMetadata.KEY_ARTIST, song.getArtist());
meta.putString(MediaMetadata.KEY_ALBUM_ARTIST, song.getArtist());
meta.putString(MediaMetadata.KEY_ALBUM_TITLE, song.getAlbum());
- String coverArt = musicService.getCoverArtUrl(downloadService, song);
- meta.addImage(new WebImage(Uri.parse(coverArt)));
+
+ String coverArt = "";
+ if(proxy == null) {
+ coverArt = musicService.getCoverArtUrl(downloadService, song);
+ coverArt = fixURLs(coverArt);
+ meta.addImage(new WebImage(Uri.parse(coverArt)));
+ } else {
+ File coverArtFile = FileUtil.getAlbumArtFile(downloadService, song);
+ if(coverArtFile != null && coverArtFile.exists()) {
+ coverArt = proxy.getPublicAddress(coverArtFile.getPath());
+ meta.addImage(new WebImage(Uri.parse(coverArt)));
+ }
+ }
+ Log.i(TAG, "Cover art: " + coverArt);
}
String contentType;
@@ -233,8 +280,10 @@ public class ChromeCastController extends RemoteController {
}
else if(song.getTranscodedContentType() != null) {
contentType = song.getTranscodedContentType();
- } else {
+ } else if(song.getContentType() != null) {
contentType = song.getContentType();
+ } else {
+ contentType = "audio/mpeg";
}
// Load it into a MediaInfo wrapper
@@ -268,6 +317,20 @@ public class ChromeCastController extends RemoteController {
}
}
+ private String fixURLs(String url) {
+ // Only change to internal when using https
+ if(url.indexOf("https") != -1) {
+ SharedPreferences prefs = Util.getPreferences(downloadService);
+ int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
+ String externalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
+ String internalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_INTERNAL_URL + instance, null);
+ url = url.replace(internalUrl, externalUrl);
+ }
+
+ // Use separate profile for Chromecast so users can do ogg on phone, mp3 for CC
+ return url.replace(Constants.REST_CLIENT_ID, Constants.CHROMECAST_CLIENT_ID);
+ }
+
private void failedLoad() {
Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load));
downloadService.setPlayerState(PlayerState.STOPPED);
diff --git a/src/github/daneren2005/dsub/service/DownloadFile.java b/src/github/daneren2005/dsub/service/DownloadFile.java
index 9598b572..d7c3959c 100644
--- a/src/github/daneren2005/dsub/service/DownloadFile.java
+++ b/src/github/daneren2005/dsub/service/DownloadFile.java
@@ -36,6 +36,8 @@ import github.daneren2005.dsub.util.CancellableTask;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.util.CacheCleaner;
+import github.daneren2005.serverproxy.BufferFile;
+
import org.apache.http.Header;
import org.apache.http.HttpResponse;
@@ -45,7 +47,7 @@ import org.apache.http.HttpStatus;
* @author Sindre Mehus
* @version $Id$
*/
-public class DownloadFile {
+public class DownloadFile implements BufferFile {
private static final String TAG = DownloadFile.class.getSimpleName();
private static final int MAX_FAILURES = 5;
private final Context context;
@@ -63,7 +65,7 @@ public class DownloadFile {
private boolean isPlaying = false;
private boolean saveWhenDone = false;
private boolean completeWhenDone = false;
- private Integer contentLength = null;
+ private Long contentLength = null;
private long currentSpeed = 0;
public DownloadFile(Context context, MusicDirectory.Entry song, boolean save) {
@@ -108,7 +110,7 @@ public class DownloadFile {
return br;
}
- public Integer getContentLength() {
+ public Long getContentLength() {
return contentLength;
}
@@ -125,6 +127,7 @@ public class DownloadFile {
}
}
+ @Override
public long getEstimatedSize() {
if(contentLength != null) {
return contentLength;
@@ -136,7 +139,7 @@ public class DownloadFile {
} else if(song.getDuration() == null) {
return 0;
} else {
- int br = (getBitRate() * 1024) / 8;
+ int br = (getBitRate() * 1000) / 8;
int duration = song.getDuration();
return br * duration;
}
@@ -171,6 +174,17 @@ public class DownloadFile {
}
}
+ @Override
+ public File getFile() {
+ if (saveFile.exists()) {
+ return saveFile;
+ } else if (completeFile.exists()) {
+ return completeFile;
+ } else {
+ return partialFile;
+ }
+ }
+
public File getCompleteFile() {
if (saveFile.exists()) {
return saveFile;
@@ -195,11 +209,22 @@ public class DownloadFile {
return saveFile.exists() || completeFile.exists();
}
+ @Override
public synchronized boolean isWorkDone() {
return saveFile.exists() || (completeFile.exists() && !save) || saveWhenDone || completeWhenDone;
}
- public synchronized boolean isDownloading() {
+ @Override
+ public void onStart() {
+ setPlaying(true);
+ }
+
+ @Override
+ public void onStop() {
+ setPlaying(false);
+ }
+
+ public synchronized boolean isDownloading() {
return downloadTask != null && downloadTask.isRunning();
}
@@ -353,7 +378,7 @@ public class DownloadFile {
String contentLengthString = contentLengthHeader.getValue();
if(contentLengthString != null) {
Log.i(TAG, "Content Length: " + contentLengthString);
- contentLength = Integer.parseInt(contentLengthString);
+ contentLength = Long.parseLong(contentLengthString);
}
}
in = response.getEntity().getContent();
diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java
index 01753026..924f53a7 100644
--- a/src/github/daneren2005/dsub/service/DownloadService.java
+++ b/src/github/daneren2005/dsub/service/DownloadService.java
@@ -43,6 +43,7 @@ import github.daneren2005.dsub.util.ShufflePlayBuffer;
import github.daneren2005.dsub.util.SimpleServiceBinder;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.util.compat.RemoteControlClientHelper;
+import github.daneren2005.serverproxy.BufferProxy;
import java.io.File;
import java.util.ArrayList;
@@ -133,7 +134,7 @@ public class DownloadService extends Service {
private boolean showVisualization;
private RemoteControlState remoteState = RemoteControlState.LOCAL;
private PositionCache positionCache;
- private StreamProxy proxy;
+ private BufferProxy proxy;
private Timer sleepTimer;
private int timerDuration;
@@ -274,6 +275,10 @@ public class DownloadService extends Service {
remoteController.stop();
remoteController.shutdown();
}
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
mediaRouter.destroy();
Util.hidePlayingNotification(this, this, handler);
Util.hideDownloadingNotification(this);
@@ -368,7 +373,7 @@ public class DownloadService extends Service {
}
}
- public void restore(List<MusicDirectory.Entry> songs, List<MusicDirectory.Entry> toDelete, int currentPlayingIndex, int currentPlayingPosition) {
+ public synchronized void restore(List<MusicDirectory.Entry> songs, List<MusicDirectory.Entry> toDelete, int currentPlayingIndex, int currentPlayingPosition) {
SharedPreferences prefs = Util.getPreferences(this);
remoteState = RemoteControlState.values()[prefs.getInt(Constants.PREFERENCES_KEY_CONTROL_MODE, 0)];
if(remoteState != RemoteControlState.LOCAL) {
@@ -518,6 +523,15 @@ public class DownloadService extends Service {
updateJukeboxPlaylist();
}
+ public synchronized void setOnline(boolean online) {
+ if(online) {
+ mediaRouter.addOfflineProviders();
+ } else {
+ mediaRouter.removeOfflineProviders();
+ clearIncomplete();
+ }
+ }
+
public synchronized int size() {
return downloadList.size();
}
@@ -552,6 +566,13 @@ public class DownloadService extends Service {
}
updateJukeboxPlaylist();
setNextPlaying();
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ suggestedPlaylistName = null;
+ suggestedPlaylistId = null;
}
public synchronized void remove(int which) {
@@ -830,17 +851,7 @@ public class DownloadService extends Service {
nextPlayingIndex++;
}
if (index != -1 && nextPlayingIndex < size()) {
- if(nextPlaying != null && downloadList.get(nextPlayingIndex) == nextPlaying && nextPlayerState == PlayerState.PREPARED && remoteState == RemoteControlState.LOCAL) {
- if(mediaPlayer.isPlaying()) {
- mediaPlayer.stop();
- }
- mediaPlayer.setOnErrorListener(null);
- mediaPlayer.setOnCompletionListener(null);
- mediaPlayer.reset();
- playNext(true);
- } else {
- play(nextPlayingIndex);
- }
+ play(nextPlayingIndex);
}
}
@@ -1146,6 +1157,16 @@ public class DownloadService extends Service {
if (currentDownloading != null) {
currentDownloading.cancelDownload();
}
+
+ // Cancels current setup tasks
+ if(bufferTask != null && bufferTask.isRunning()) {
+ bufferTask.cancel();
+ bufferTask = null;
+ }
+ if(nextPlayingTask != null && nextPlayingTask.isRunning()) {
+ nextPlayingTask.cancel();
+ nextPlayingTask = null;
+ }
}
SharedPreferences prefs = Util.getPreferences(this);
@@ -1216,10 +1237,11 @@ public class DownloadService extends Service {
String dataSource = file.getPath();
if(isPartial) {
if (proxy == null) {
- proxy = new StreamProxy(this);
+ proxy = new BufferProxy(this);
proxy.start();
}
- dataSource = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), URLEncoder.encode(dataSource, Constants.UTF_8));
+ proxy.setBufferFile(downloadFile);
+ dataSource = proxy.getPrivateAddress(dataSource);
Log.i(TAG, "Data Source: " + dataSource);
} else if(proxy != null) {
proxy.stop();
diff --git a/src/github/daneren2005/dsub/service/JukeboxController.java b/src/github/daneren2005/dsub/service/JukeboxController.java
index 7f248f03..ebebbaf8 100644
--- a/src/github/daneren2005/dsub/service/JukeboxController.java
+++ b/src/github/daneren2005/dsub/service/JukeboxController.java
@@ -217,9 +217,9 @@ public class JukeboxController extends RemoteController {
@Override
public void run() {
Util.toast(downloadService, resourceId, false);
+ downloadService.setRemoteEnabled(RemoteControlState.LOCAL);
}
});
- downloadService.setRemoteEnabled(RemoteControlState.LOCAL);
}
private MusicService getMusicService() {
diff --git a/src/github/daneren2005/dsub/service/RESTMusicService.java b/src/github/daneren2005/dsub/service/RESTMusicService.java
index 8ea29a41..879339cc 100644
--- a/src/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/src/github/daneren2005/dsub/service/RESTMusicService.java
@@ -623,7 +623,7 @@ public class RESTMusicService implements MusicService {
@Override
public String getCoverArtUrl(Context context, MusicDirectory.Entry entry) throws Exception {
- StringBuilder builder = new StringBuilder(getRestUrl(context, "getCoverArt", false));
+ StringBuilder builder = new StringBuilder(getRestUrl(context, "getCoverArt"));
builder.append("&id=").append(entry.getId());
String url = rewriteUrlWithRedirect(context, builder.toString());
return url;
@@ -710,7 +710,7 @@ public class RESTMusicService implements MusicService {
@Override
public String getMusicUrl(Context context, MusicDirectory.Entry song, int maxBitrate) throws Exception {
- StringBuilder builder = new StringBuilder(getRestUrl(context, "stream", false));
+ StringBuilder builder = new StringBuilder(getRestUrl(context, "stream"));
builder.append("&id=").append(song.getId());
builder.append("&maxBitRate=").append(maxBitrate);
diff --git a/src/github/daneren2005/dsub/service/StreamProxy.java b/src/github/daneren2005/dsub/service/StreamProxy.java
deleted file mode 100644
index d660b6d3..00000000
--- a/src/github/daneren2005/dsub/service/StreamProxy.java
+++ /dev/null
@@ -1,311 +0,0 @@
-package github.daneren2005.dsub.service;
-
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.URLDecoder;
-import java.net.UnknownHostException;
-import java.util.StringTokenizer;
-
-import org.apache.http.Header;
-import org.apache.http.HttpRequest;
-import org.apache.http.message.BasicHttpRequest;
-
-import android.os.AsyncTask;
-import android.os.Looper;
-import android.util.Log;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.Constants;
-
-public class StreamProxy implements Runnable {
- private static final String TAG = StreamProxy.class.getSimpleName();
-
- private Thread thread;
- private boolean isRunning;
- private ServerSocket socket;
- private int port;
- private DownloadService downloadService;
-
- public StreamProxy(DownloadService downloadService) {
-
- // Create listening socket
- try {
- socket = new ServerSocket(0, 0, InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }));
- socket.setSoTimeout(5000);
- port = socket.getLocalPort();
- this.downloadService = downloadService;
- } catch (UnknownHostException e) { // impossible
- } catch (IOException e) {
- Log.e(TAG, "IOException initializing server", e);
- }
- }
-
- public int getPort() {
- return port;
- }
-
- public void start() {
- thread = new Thread(this, "StreamProxy");
- thread.start();
- }
-
- public void stop() {
- isRunning = false;
- thread.interrupt();
- }
-
- @Override
- public void run() {
- isRunning = true;
- while (isRunning) {
- try {
- Socket client = socket.accept();
- if (client == null) {
- continue;
- }
- Log.i(TAG, "client connected");
-
- StreamToMediaPlayerTask task = new StreamToMediaPlayerTask(client);
- if (task.processRequest()) {
- new Thread(task).start();
- }
-
- } catch (SocketTimeoutException e) {
- // Do nothing
- } catch (IOException e) {
- Log.e(TAG, "Error connecting to client", e);
- }
- }
- Log.i(TAG, "Proxy interrupted. Shutting down.");
- }
-
- private class StreamToMediaPlayerTask implements Runnable {
- DownloadFile downloadFile;
- File file;
- Socket client;
- int cbSkip = 0;
-
- public StreamToMediaPlayerTask(Socket client) {
- this.client = client;
- }
-
- private HttpRequest readRequest() {
- HttpRequest request = null;
- InputStream is;
- String firstLine;
- BufferedReader reader = null;
- try {
- is = client.getInputStream();
- reader = new BufferedReader(new InputStreamReader(is), 8192);
- firstLine = reader.readLine();
- } catch (IOException e) {
- Log.e(TAG, "Error parsing request", e);
- return request;
- }
-
- if (firstLine == null) {
- Log.i(TAG, "Proxy client closed connection without a request.");
- return request;
- }
-
- StringTokenizer st = new StringTokenizer(firstLine);
- String method = st.nextToken();
- String uri = st.nextToken();
- String realUri = uri.substring(1);
- Log.i(TAG, realUri);
- request = new BasicHttpRequest(method, realUri);
-
- // Get all of the headers
- try {
- String line;
- while((line = reader.readLine()) != null && !"".equals(line)) {
- String headerName = line.substring(0, line.indexOf(':'));
- String headerValue = line.substring(line.indexOf(": ") + 2);
- request.addHeader(headerName, headerValue);
- }
- } catch(IOException e) {
- // Don't really care once past first line
- }
-
- return request;
- }
-
- public boolean processRequest() {
- HttpRequest request = readRequest();
- if (request == null) {
- return false;
- }
-
- // Read HTTP headers
- Log.i(TAG, "Processing request");
-
- String localPath;
- try {
- localPath = URLDecoder.decode(request.getRequestLine().getUri(), Constants.UTF_8);
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Unsupported encoding", e);
- return false;
- }
-
- Log.i(TAG, "Processing request for file " + localPath);
- downloadFile = downloadService.getCurrentPlaying();
- File partialFile = new File(localPath);
- if (!partialFile.equals(downloadFile.getPartialFile())) {
- Log.e(TAG, "File " + localPath + " does not exist");
- return false;
- }
-
- // Use either partial or complete if downloading finished while StreamProxy was idle
- file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
-
- // Try to get range requested
- Header rangeHeader = request.getFirstHeader("Range");
-
- if(rangeHeader != null) {
- String range = rangeHeader.getValue();
- int index = range.indexOf("=");
- if(index >= 0) {
- range = range.substring(index + 1);
-
- index = range.indexOf("-");
- if(index > 0) {
- range = range.substring(0, index);
- }
-
- cbSkip = Integer.parseInt(range);
-
- // Make sure to not try to read past where the file is downloaded
- if(cbSkip >= file.length()) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- @Override
- public void run() {
- Log.i(TAG, "Streaming song in background");
- MusicDirectory.Entry song = downloadFile.getSong();
-
- Integer contentLength = downloadFile.getContentLength();
- if(contentLength == null && downloadFile.isWorkDone()) {
- contentLength = (int)file.length();
- }
-
- // Create HTTP header
- String headers;
- if(cbSkip == 0) {
- headers = "HTTP/1.0 200 OK\r\n";
- } else {
- headers = "HTTP/1.0 206 OK\r\n;";
- headers += "Content-Range: bytes " + cbSkip + "-" + (file.length() - 1) + "/";
- if(contentLength == null) {
- headers += "*";
- } else {
- headers += contentLength;
- }
-
- Log.i(TAG, "Streaming starts from: " + cbSkip);
- }
- headers += "Content-Type: " + "application/octet-stream" + "\r\n";
-
- long fileSize;
- if(contentLength == null) {
- fileSize = downloadFile.getBitRate() * ((song.getDuration() != null) ? song.getDuration() : 0) * 1000 / 8;
- } else {
- fileSize = contentLength;
- headers += "Content-Length: " + fileSize + "\r\n";
- }
- Log.i(TAG, "Streaming fileSize: " + fileSize);
-
- headers += "Connection: close\r\n";
- headers += "\r\n";
-
- long cbToSend = fileSize - cbSkip;
- OutputStream output = null;
- byte[] buff = new byte[64 * 1024];
- try {
- output = new BufferedOutputStream(client.getOutputStream(), 32*1024);
- output.write(headers.getBytes());
-
- // Make sure to have file lock
- downloadFile.setPlaying(true);
-
- // Loop as long as there's stuff to send
- while (isRunning && !client.isClosed()) {
-
- // See if there's more to send
- int cbSentThisBatch = 0;
- if (file.exists()) {
- FileInputStream input = new FileInputStream(file);
- input.skip(cbSkip);
- int cbToSendThisBatch = input.available();
- while (cbToSendThisBatch > 0) {
- int cbToRead = Math.min(cbToSendThisBatch, buff.length);
- int cbRead = input.read(buff, 0, cbToRead);
- if (cbRead == -1) {
- break;
- }
- cbToSendThisBatch -= cbRead;
- cbToSend -= cbRead;
- output.write(buff, 0, cbRead);
- output.flush();
- cbSkip += cbRead;
- cbSentThisBatch += cbRead;
- }
- input.close();
- }
-
- // Done regardless of whether or not it thinks it is
- if(downloadFile.isWorkDone() && cbSkip >= file.length()) {
- break;
- }
-
- // If we did nothing this batch, block for a second
- if (cbSentThisBatch == 0) {
- Log.d(TAG, "Blocking until more data appears (" + cbToSend + ")");
- Thread.sleep(1000);
- }
- }
-
- // Release file lock, use of stream proxy means nothing else is using it
- downloadFile.setPlaying(false);
- }
- catch (SocketException socketException) {
- Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly");
-
- // Release file lock, use of stream proxy means nothing else is using it
- downloadFile.setPlaying(false);
- }
- catch (Exception e) {
- Log.e(TAG, "Exception thrown from streaming task:");
- Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
- }
-
- // Cleanup
- try {
- if (output != null) {
- output.close();
- }
- client.close();
- }
- catch (IOException e) {
- Log.e(TAG, "IOException while cleaning up streaming task:");
- Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
- }
- }
- }
-}
diff --git a/src/github/daneren2005/dsub/util/BackgroundTask.java b/src/github/daneren2005/dsub/util/BackgroundTask.java
index b9fe212f..fbeaea74 100644
--- a/src/github/daneren2005/dsub/util/BackgroundTask.java
+++ b/src/github/daneren2005/dsub/util/BackgroundTask.java
@@ -53,7 +53,7 @@ public abstract class BackgroundTask<T> implements ProgressListener {
private static Handler handler = null;
static {
try {
- handler = new Handler();
+ handler = new Handler(Looper.getMainLooper());
} catch(Exception e) {
// Not called from main thread
}
@@ -69,6 +69,13 @@ public abstract class BackgroundTask<T> implements ProgressListener {
thread.start();
}
}
+ if(handler == null) {
+ try {
+ handler = new Handler(Looper.getMainLooper());
+ } catch(Exception e) {
+ // Not called from main thread
+ }
+ }
}
public static void stopThreads() {
diff --git a/src/github/daneren2005/dsub/util/LoadingTask.java b/src/github/daneren2005/dsub/util/LoadingTask.java
index c47931ee..17215abd 100644
--- a/src/github/daneren2005/dsub/util/LoadingTask.java
+++ b/src/github/daneren2005/dsub/util/LoadingTask.java
@@ -52,7 +52,7 @@ public abstract class LoadingTask<T> extends BackgroundTask<T> {
@Override
protected boolean isCancelled() {
- return (tabActivity instanceof SubsonicActivity && tabActivity.isDestroyed()) || cancelled;
+ return (tabActivity instanceof SubsonicActivity && ((SubsonicActivity) tabActivity).isDestroyedCompat()) || cancelled;
}
@Override
diff --git a/src/github/daneren2005/dsub/util/MediaRouteManager.java b/src/github/daneren2005/dsub/util/MediaRouteManager.java
index 4a124402..d41a5516 100644
--- a/src/github/daneren2005/dsub/util/MediaRouteManager.java
+++ b/src/github/daneren2005/dsub/util/MediaRouteManager.java
@@ -42,6 +42,7 @@ public class MediaRouteManager extends MediaRouter.Callback {
private MediaRouter router;
private MediaRouteSelector selector;
private List<MediaRouteProvider> providers = new ArrayList<MediaRouteProvider>();
+ private List<MediaRouteProvider> offlineProviders = new ArrayList<MediaRouteProvider>();
static {
try {
@@ -116,10 +117,22 @@ public class MediaRouteManager extends MediaRouter.Callback {
}
}
+ public void addOfflineProviders() {
+ JukeboxRouteProvider jukeboxProvider = new JukeboxRouteProvider(downloadService);
+ router.addProvider(jukeboxProvider);
+ providers.add(jukeboxProvider);
+ offlineProviders.add(jukeboxProvider);
+ }
+ public void removeOfflineProviders() {
+ for(MediaRouteProvider provider: offlineProviders) {
+ router.removeProvider(provider);
+ }
+ }
+
private void addProviders() {
- JukeboxRouteProvider routeProvider = new JukeboxRouteProvider(downloadService);
- router.addProvider(routeProvider);
- providers.add(routeProvider);
+ if(!Util.isOffline(downloadService)) {
+ addOfflineProviders();
+ }
}
private void buildSelector() {
MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();