aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2014-11-26 17:10:02 -0800
committerScott Jackson <daneren2005@gmail.com>2014-11-26 17:10:02 -0800
commitda5e8cbdaa155f5e9a32297a297cb57b6ffa3e15 (patch)
tree5775858889bc561cd80de6de883dc002b34fe97c /src
parent6074b309ddef36a4c304ce6bf88e88f221eba01b (diff)
downloaddsub-da5e8cbdaa155f5e9a32297a297cb57b6ffa3e15.tar.gz
dsub-da5e8cbdaa155f5e9a32297a297cb57b6ffa3e15.tar.bz2
dsub-da5e8cbdaa155f5e9a32297a297cb57b6ffa3e15.zip
Get a basic working push to XBMC working
Diffstat (limited to 'src')
-rw-r--r--src/github/daneren2005/dsub/domain/DLNADevice.java6
-rw-r--r--src/github/daneren2005/dsub/provider/DLNARouteProvider.java4
-rw-r--r--src/github/daneren2005/dsub/service/ChromeCastController.java20
-rw-r--r--src/github/daneren2005/dsub/service/DLNAController.java186
-rw-r--r--src/github/daneren2005/dsub/service/DownloadService.java2
-rw-r--r--src/github/daneren2005/dsub/util/Util.java16
6 files changed, 206 insertions, 28 deletions
diff --git a/src/github/daneren2005/dsub/domain/DLNADevice.java b/src/github/daneren2005/dsub/domain/DLNADevice.java
index b18000d0..ba4c2777 100644
--- a/src/github/daneren2005/dsub/domain/DLNADevice.java
+++ b/src/github/daneren2005/dsub/domain/DLNADevice.java
@@ -22,10 +22,13 @@ package github.daneren2005.dsub.domain;
import android.os.Parcel;
import android.os.Parcelable;
+import org.teleal.cling.model.meta.Device;
+
/**
* Created by Scott on 11/1/2014.
*/
public class DLNADevice implements Parcelable {
+ public Device renderer;
public String id;
public String name;
public String description;
@@ -50,7 +53,8 @@ public class DLNADevice implements Parcelable {
volumeMax = in.readInt();
}
- public DLNADevice(String id, String name, String description, int volume, int volumeMax) {
+ public DLNADevice(Device renderer, String id, String name, String description, int volume, int volumeMax) {
+ this.renderer = renderer;
this.id = id;
this.name = name;
this.description = description;
diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java
index ca6fabe0..44cde0ef 100644
--- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java
+++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java
@@ -217,7 +217,7 @@ public class DLNARouteProvider extends MediaRouteProvider {
String name = device.getDetails().getFriendlyName();
String displayName = device.getDisplayString();
- DLNADevice newDevice = new DLNADevice(id, name, displayName, currentVolume, maxVolume);
+ DLNADevice newDevice = new DLNADevice(device, id, name, displayName, currentVolume, maxVolume);
devices.put(id, newDevice);
downloadService.post(new Runnable() {
@Override
@@ -276,7 +276,7 @@ public class DLNARouteProvider extends MediaRouteProvider {
@Override
public void onSelect() {
- controller = new DLNAController(device);
+ controller = new DLNAController(downloadService, dlnaService.getControlPoint(), device);
downloadService.setRemoteEnabled(RemoteControlState.DLNA, controller);
}
diff --git a/src/github/daneren2005/dsub/service/ChromeCastController.java b/src/github/daneren2005/dsub/service/ChromeCastController.java
index 0c8f38a6..6f35ac1d 100644
--- a/src/github/daneren2005/dsub/service/ChromeCastController.java
+++ b/src/github/daneren2005/dsub/service/ChromeCastController.java
@@ -283,7 +283,7 @@ public class ChromeCastController extends RemoteController {
url = musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate());
}
- url = fixURLs(url);
+ url = Util.replaceInternalUrl(downloadService, url);
}
// Setup song/video information
@@ -300,7 +300,7 @@ public class ChromeCastController extends RemoteController {
String coverArt = "";
if(proxy == null) {
coverArt = musicService.getCoverArtUrl(downloadService, song);
- coverArt = fixURLs(coverArt);
+ coverArt = Util.replaceInternalUrl(downloadService, coverArt);
meta.addImage(new WebImage(Uri.parse(coverArt)));
} else {
File coverArtFile = FileUtil.getAlbumArtFile(downloadService, song);
@@ -354,22 +354,6 @@ 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 internalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_INTERNAL_URL + instance, null);
- if(internalUrl != null && !"".equals(internalUrl)) {
- String externalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_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/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java
index dee65d64..596f7762 100644
--- a/src/github/daneren2005/dsub/service/DLNAController.java
+++ b/src/github/daneren2005/dsub/service/DLNAController.java
@@ -15,33 +15,154 @@
package github.daneren2005.dsub.service;
+import android.util.Log;
+
+import org.teleal.cling.controlpoint.ControlPoint;
+import org.teleal.cling.controlpoint.SubscriptionCallback;
+import org.teleal.cling.model.action.ActionInvocation;
+import org.teleal.cling.model.gena.CancelReason;
+import org.teleal.cling.model.gena.GENASubscription;
+import org.teleal.cling.model.message.UpnpResponse;
+import org.teleal.cling.model.meta.Device;
+import org.teleal.cling.model.state.StateVariableValue;
+import org.teleal.cling.model.types.ServiceType;
+import org.teleal.cling.support.avtransport.callback.Pause;
+import org.teleal.cling.support.avtransport.callback.Play;
+import org.teleal.cling.support.avtransport.callback.SetAVTransportURI;
+import org.teleal.cling.support.avtransport.callback.Stop;
+import org.teleal.cling.support.avtransport.lastchange.AVTransportLastChangeParser;
+import org.teleal.cling.support.avtransport.lastchange.AVTransportVariable;
+import org.teleal.cling.support.lastchange.LastChange;
+
+import java.util.Map;
+
+import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.DLNADevice;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.util.Util;
public class DLNAController extends RemoteController {
+ private static final String TAG = DLNAController.class.getSimpleName();
+
DLNADevice device;
+ ControlPoint controlPoint;
+ SubscriptionCallback callback;
- public DLNAController(DLNADevice device) {
+ public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) {
+ this.downloadService = downloadService;
+ this.controlPoint = controlPoint;
this.device = device;
}
@Override
- public void create(boolean playing, int seconds) {
-
+ public void create(final boolean playing, final int seconds) {
+ downloadService.setPlayerState(PlayerState.PREPARING);
+
+ Device renderer = device.renderer;
+
+ callback = new SubscriptionCallback(renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")), 600) {
+ @Override
+ protected void failed(GENASubscription genaSubscription, UpnpResponse upnpResponse, Exception e, String msg) {
+ Log.w(TAG, "Register subscription callback failed: " + msg, e);
+ }
+
+ @Override
+ protected void established(GENASubscription genaSubscription) {
+ startSong(downloadService.getCurrentPlaying(), playing, seconds);
+ }
+
+ @Override
+ protected void ended(GENASubscription genaSubscription, CancelReason cancelReason, UpnpResponse upnpResponse) {
+
+ }
+
+ @Override
+ protected void eventReceived(GENASubscription genaSubscription) {
+ Map<String, StateVariableValue> m = genaSubscription.getCurrentValues();
+ try {
+ LastChange lastChange = new LastChange(new AVTransportLastChangeParser(), m.get("LastChange").toString());
+ if (playing || lastChange.getEventedValue(0, AVTransportVariable.TransportState.class) == null) {
+ return;
+ }
+
+ switch (lastChange.getEventedValue(0, AVTransportVariable.TransportState.class).getValue()) {
+ case PLAYING:
+ downloadService.setPlayerState(PlayerState.STARTED);
+ break;
+ case PAUSED_PLAYBACK:
+ downloadService.setPlayerState(PlayerState.PAUSED);
+ break;
+ case STOPPED:
+ downloadService.setPlayerState(PlayerState.STOPPED);
+ break;
+ case TRANSITIONING:
+ downloadService.setPlayerState(PlayerState.PREPARING);
+ break;
+ case NO_MEDIA_PRESENT:
+ downloadService.setPlayerState(PlayerState.IDLE);
+ break;
+ default:
+ }
+ }
+ catch (Exception e) {
+ Log.w(TAG, "Failed to parse UPNP event", e);
+ failedLoad();
+ }
+ }
+
+ @Override
+ protected void eventsMissed(GENASubscription genaSubscription, int i) {
+
+ }
+ };
+ controlPoint.execute(callback);
}
@Override
public void start() {
-
+ controlPoint.execute(new Play(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ downloadService.setPlayerState(PlayerState.STARTED);
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Failed to start playing: " + msg);
+ failedLoad();
+ }
+ });
}
@Override
public void stop() {
-
+ controlPoint.execute(new Pause(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ downloadService.setPlayerState(PlayerState.PAUSED);
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Failed to pause playing: " + msg);
+ }
+ });
}
@Override
public void shutdown() {
-
+ controlPoint.execute(new Stop(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) {
+ @Override
+ public void failure(ActionInvocation invocation, org.teleal.cling.model.message.UpnpResponse operation, String defaultMessage) {
+ Log.w(TAG, "Stop failed: " + defaultMessage);
+ }
+ });
+
+ if(callback != null) {
+ callback.end();
+ callback = null;
+ }
}
@Override
@@ -78,4 +199,57 @@ public class DLNAController extends RemoteController {
public int getRemotePosition() {
return 0;
}
+
+ private void startSong(final DownloadFile currentPlaying, final boolean autoStart, final int position) {
+ if(currentPlaying == null) {
+ downloadService.setPlayerState(PlayerState.IDLE);
+ return;
+ }
+
+ downloadService.setPlayerState(PlayerState.PREPARING);
+ MusicDirectory.Entry song = currentPlaying.getSong();
+
+ try {
+ MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
+ String url = musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate());
+ url = Util.replaceInternalUrl(downloadService, url);
+ String metadata = "";
+
+ controlPoint.execute(new SetAVTransportURI(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")), url, metadata) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ if (autoStart) {
+ controlPoint.execute(new Play(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) {
+ @Override
+ public void success(ActionInvocation invocation) {
+ downloadService.setPlayerState(PlayerState.STARTED);
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Failed to start playing: " + msg);
+ failedLoad();
+ }
+ });
+ } else {
+ downloadService.setPlayerState(PlayerState.PAUSED);
+ }
+ }
+
+ @Override
+ public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) {
+ Log.w(TAG, "Set URI failed: " + msg);
+ failedLoad();
+ }
+ });
+ } catch (Exception e) {
+ Log.w(TAG, "Failed startSong", e);
+ failedLoad();
+ }
+ }
+
+ 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/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java
index 8b45fba4..6482cc14 100644
--- a/src/github/daneren2005/dsub/service/DownloadService.java
+++ b/src/github/daneren2005/dsub/service/DownloadService.java
@@ -1345,7 +1345,7 @@ public class DownloadService extends Service {
case JUKEBOX_SERVER:
remoteController = new JukeboxController(this, handler);
break;
- case CHROMECAST:
+ case CHROMECAST: case DLNA:
if(ref == null) {
remoteState = RemoteControlState.LOCAL;
break;
diff --git a/src/github/daneren2005/dsub/util/Util.java b/src/github/daneren2005/dsub/util/Util.java
index e9724ded..205690f8 100644
--- a/src/github/daneren2005/dsub/util/Util.java
+++ b/src/github/daneren2005/dsub/util/Util.java
@@ -357,6 +357,22 @@ public final class Util {
return builder.toString();
}
+ public static String replaceInternalUrl(Context context, String url) {
+ // Only change to internal when using https
+ if(url.indexOf("https") != -1) {
+ SharedPreferences prefs = Util.getPreferences(context);
+ int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
+ String internalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_INTERNAL_URL + instance, null);
+ if(internalUrl != null && !"".equals(internalUrl)) {
+ String externalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_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);
+ }
+
public static boolean isTagBrowsing(Context context) {
return isTagBrowsing(context, Util.getActiveServer(context));
}