From c72fe220a6c0d83a256e5c499887200e8df4996c Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Thu, 30 Oct 2014 22:02:01 -0700 Subject: Start of DLNA cast support --- .../dsub/domain/RemoteControlState.java | 3 +- .../dsub/provider/DLNARouteProvider.java | 195 +++++++++++++++++++++ 2 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 src/github/daneren2005/dsub/provider/DLNARouteProvider.java (limited to 'src') diff --git a/src/github/daneren2005/dsub/domain/RemoteControlState.java b/src/github/daneren2005/dsub/domain/RemoteControlState.java index 9bf3bf91..47895984 100644 --- a/src/github/daneren2005/dsub/domain/RemoteControlState.java +++ b/src/github/daneren2005/dsub/domain/RemoteControlState.java @@ -23,7 +23,8 @@ public enum RemoteControlState { LOCAL(0), JUKEBOX_SERVER(1), CHROMECAST(2), - REMOTE_CLIENT(3); + REMOTE_CLIENT(3), + DLNA(4); private final int mRemoteControlState; diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java new file mode 100644 index 00000000..f84c1eaa --- /dev/null +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -0,0 +1,195 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2014 (C) Scott Jackson +*/ +package github.daneren2005.dsub.provider; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.media.MediaRouter; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v7.media.MediaControlIntent; +import android.support.v7.media.MediaRouteDescriptor; +import android.support.v7.media.MediaRouteDiscoveryRequest; +import android.support.v7.media.MediaRouteProvider; +import android.support.v7.media.MediaRouteProviderDescriptor; + +import java.util.HashMap; +import java.util.Map; + +import github.daneren2005.dsub.domain.RemoteControlState; +import github.daneren2005.dsub.service.DownloadService; +import github.daneren2005.dsub.service.RemoteController; + +/** + * Created by Scott on 11/28/13. + */ +public class DLNARouteProvider extends MediaRouteProvider { + public static final String CATEGORY_DLNA = "github.daneren2005.dsub.DLNA"; + + private DownloadService downloadService; + private RemoteController controller; + + private HashMap devices = new HashMap(); + + public DLNARouteProvider(Context context) { + super(context); + this.downloadService = (DownloadService) context; + } + + private void broadcastDescriptors() { + // Create intents + IntentFilter routeIntentFilter = new IntentFilter(); + routeIntentFilter.addCategory(CATEGORY_DLNA); + routeIntentFilter.addAction(MediaControlIntent.ACTION_START_SESSION); + routeIntentFilter.addAction(MediaControlIntent.ACTION_GET_SESSION_STATUS); + routeIntentFilter.addAction(MediaControlIntent.ACTION_END_SESSION); + + // Create descriptor + MediaRouteProviderDescriptor.Builder providerBuilder = new MediaRouteProviderDescriptor.Builder(); + + // Create route descriptor + for(Map.Entry deviceEntry: devices.entrySet()) { + Device device = deviceEntry.getValue(); + + MediaRouteDescriptor.Builder routeBuilder = new MediaRouteDescriptor.Builder(device.id, device.name); + routeBuilder.addControlFilter(routeIntentFilter) + .setPlaybackStream(AudioManager.STREAM_MUSIC) + .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE) + .setDescription(device.description) + .setVolume(controller == null ? 5 : (int) (controller.getVolume() * 10)) + .setVolumeMax(device.volumeMax) + .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE); + providerBuilder.addRoute(routeBuilder.build()); + } + + setDescriptor(providerBuilder.build()); + } + + public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) { + if (request != null && request.isActiveScan()) { + + } + } + + @Override + public RouteController onCreateRouteController(String routeId) { + return new DLNARouteController(downloadService); + } + + private class DLNARouteController extends RouteController { + private DownloadService downloadService; + + public DLNARouteController(DownloadService downloadService) { + this.downloadService = downloadService; + } + + @Override + public boolean onControlRequest(Intent intent, android.support.v7.media.MediaRouter.ControlRequestCallback callback) { + if (intent.hasCategory(CATEGORY_DLNA)) { + return true; + } else { + return false; + } + } + + @Override + public void onRelease() { + downloadService.setRemoteEnabled(RemoteControlState.LOCAL); + controller = null; + } + + @Override + public void onSelect() { + downloadService.setRemoteEnabled(RemoteControlState.DLNA); + controller = downloadService.getRemoteController(); + } + + @Override + public void onUnselect() { + downloadService.setRemoteEnabled(RemoteControlState.LOCAL); + controller = null; + } + + @Override + public void onUpdateVolume(int delta) { + if(controller != null) { + controller.updateVolume(delta > 0); + } + broadcastDescriptors(); + } + + @Override + public void onSetVolume(int volume) { + if(controller != null) { + controller.setVolume(volume); + } + broadcastDescriptors(); + } + } + + static public class Device implements Parcelable { + public String id; + public String name; + public String description; + public int volume; + public int volumeMax; + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Device createFromParcel(Parcel in) { + return new Device(in); + } + + public Device[] newArray(int size) { + return new Device[size]; + } + }; + + private Device(Parcel in) { + id = in.readString(); + name = in.readString(); + description = in.readString(); + volume = in.readInt(); + volumeMax = in.readInt(); + } + + public Device(String id, String name, String description, int volume, int volumeMax) { + this.id = id; + this.name = name; + this.description = description; + this.volume = volume; + this.volumeMax = volumeMax; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(id); + dest.writeString(name); + dest.writeString(description); + dest.writeInt(volume); + dest.writeInt(volumeMax); + } + } +} -- cgit v1.2.3 From 0164ea01c1e0a10a94aa0476761cd20571af5db5 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 1 Nov 2014 11:30:13 -0700 Subject: Move DLNADevice to own file --- src/github/daneren2005/dsub/domain/DLNADevice.java | 55 +++++++++++++++++++++ .../dsub/provider/DLNARouteProvider.java | 57 ++-------------------- 2 files changed, 59 insertions(+), 53 deletions(-) create mode 100644 src/github/daneren2005/dsub/domain/DLNADevice.java (limited to 'src') diff --git a/src/github/daneren2005/dsub/domain/DLNADevice.java b/src/github/daneren2005/dsub/domain/DLNADevice.java new file mode 100644 index 00000000..9f9daa00 --- /dev/null +++ b/src/github/daneren2005/dsub/domain/DLNADevice.java @@ -0,0 +1,55 @@ +package github.daneren2005.dsub.domain; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Created by Scott on 11/1/2014. + */ +public class DLNADevice implements Parcelable { + public String id; + public String name; + public String description; + public int volume; + public int volumeMax; + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public DLNADevice createFromParcel(Parcel in) { + return new DLNADevice(in); + } + + public DLNADevice[] newArray(int size) { + return new DLNADevice[size]; + } + }; + + private DLNADevice(Parcel in) { + id = in.readString(); + name = in.readString(); + description = in.readString(); + volume = in.readInt(); + volumeMax = in.readInt(); + } + + public DLNADevice(String id, String name, String description, int volume, int volumeMax) { + this.id = id; + this.name = name; + this.description = description; + this.volume = volume; + this.volumeMax = volumeMax; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(id); + dest.writeString(name); + dest.writeString(description); + dest.writeInt(volume); + dest.writeInt(volumeMax); + } +} diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index f84c1eaa..edde4d3e 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -23,8 +23,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.media.MediaRouter; -import android.os.Parcel; -import android.os.Parcelable; import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaRouteDescriptor; import android.support.v7.media.MediaRouteDiscoveryRequest; @@ -34,6 +32,7 @@ import android.support.v7.media.MediaRouteProviderDescriptor; import java.util.HashMap; import java.util.Map; +import github.daneren2005.dsub.domain.DLNADevice; import github.daneren2005.dsub.domain.RemoteControlState; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.RemoteController; @@ -47,7 +46,7 @@ public class DLNARouteProvider extends MediaRouteProvider { private DownloadService downloadService; private RemoteController controller; - private HashMap devices = new HashMap(); + private HashMap devices = new HashMap(); public DLNARouteProvider(Context context) { super(context); @@ -66,8 +65,8 @@ public class DLNARouteProvider extends MediaRouteProvider { MediaRouteProviderDescriptor.Builder providerBuilder = new MediaRouteProviderDescriptor.Builder(); // Create route descriptor - for(Map.Entry deviceEntry: devices.entrySet()) { - Device device = deviceEntry.getValue(); + for(Map.Entry deviceEntry: devices.entrySet()) { + DLNADevice device = deviceEntry.getValue(); MediaRouteDescriptor.Builder routeBuilder = new MediaRouteDescriptor.Builder(device.id, device.name); routeBuilder.addControlFilter(routeIntentFilter) @@ -144,52 +143,4 @@ public class DLNARouteProvider extends MediaRouteProvider { broadcastDescriptors(); } } - - static public class Device implements Parcelable { - public String id; - public String name; - public String description; - public int volume; - public int volumeMax; - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public Device createFromParcel(Parcel in) { - return new Device(in); - } - - public Device[] newArray(int size) { - return new Device[size]; - } - }; - - private Device(Parcel in) { - id = in.readString(); - name = in.readString(); - description = in.readString(); - volume = in.readInt(); - volumeMax = in.readInt(); - } - - public Device(String id, String name, String description, int volume, int volumeMax) { - this.id = id; - this.name = name; - this.description = description; - this.volume = volume; - this.volumeMax = volumeMax; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(id); - dest.writeString(name); - dest.writeString(description); - dest.writeInt(volume); - dest.writeInt(volumeMax); - } - } } -- cgit v1.2.3 From 14b1c6b67195af12b3905dd28a227a318d678825 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 1 Nov 2014 13:12:56 -0700 Subject: More work on discovering and adding DLNA devices --- AndroidManifest.xml | 2 + libs/cling-core-2.0-alpha3.jar | Bin 0 -> 683382 bytes .../dsub/provider/DLNARouteProvider.java | 99 +++++++++++++++++++++ .../daneren2005/dsub/util/MediaRouteManager.java | 7 ++ 4 files changed, 108 insertions(+) create mode 100644 libs/cling-core-2.0-alpha3.jar (limited to 'src') diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 755b2d21..ae37b51a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -85,6 +85,8 @@ + + diff --git a/libs/cling-core-2.0-alpha3.jar b/libs/cling-core-2.0-alpha3.jar new file mode 100644 index 00000000..41bca580 Binary files /dev/null and b/libs/cling-core-2.0-alpha3.jar differ diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index edde4d3e..11c29d28 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -18,16 +18,28 @@ */ package github.daneren2005.dsub.provider; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.media.AudioManager; import android.media.MediaRouter; +import android.os.IBinder; import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaRouteDescriptor; import android.support.v7.media.MediaRouteDiscoveryRequest; import android.support.v7.media.MediaRouteProvider; import android.support.v7.media.MediaRouteProviderDescriptor; +import android.util.Log; + +import org.fourthline.cling.android.AndroidUpnpService; +import org.fourthline.cling.android.AndroidUpnpServiceImpl; +import org.fourthline.cling.model.meta.Device; +import org.fourthline.cling.model.meta.LocalDevice; +import org.fourthline.cling.model.meta.RemoteDevice; +import org.fourthline.cling.registry.Registry; +import org.fourthline.cling.registry.RegistryListener; import java.util.HashMap; import java.util.Map; @@ -41,16 +53,82 @@ import github.daneren2005.dsub.service.RemoteController; * Created by Scott on 11/28/13. */ public class DLNARouteProvider extends MediaRouteProvider { + private static final String TAG = DLNARouteProvider.class.getSimpleName(); public static final String CATEGORY_DLNA = "github.daneren2005.dsub.DLNA"; private DownloadService downloadService; private RemoteController controller; private HashMap devices = new HashMap(); + private AndroidUpnpService dlnaService; + private ServiceConnection dlnaServiceConnection; public DLNARouteProvider(Context context) { super(context); this.downloadService = (DownloadService) context; + dlnaServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + dlnaService = (AndroidUpnpService) service; + dlnaService.getRegistry().addListener(new RegistryListener() { + @Override + public void remoteDeviceDiscoveryStarted(Registry registry, RemoteDevice remoteDevice) { + + } + + @Override + public void remoteDeviceDiscoveryFailed(Registry registry, RemoteDevice remoteDevice, Exception e) { + + } + + @Override + public void remoteDeviceAdded(Registry registry, RemoteDevice remoteDevice) { + deviceAdded(remoteDevice); + } + + @Override + public void remoteDeviceUpdated(Registry registry, RemoteDevice remoteDevice) { + deviceAdded(remoteDevice); + } + + @Override + public void remoteDeviceRemoved(Registry registry, RemoteDevice remoteDevice) { + deviceRemoved(remoteDevice); + } + + @Override + public void localDeviceAdded(Registry registry, LocalDevice localDevice) { + deviceAdded(localDevice); + } + + @Override + public void localDeviceRemoved(Registry registry, LocalDevice localDevice) { + deviceRemoved(localDevice); + } + + @Override + public void beforeShutdown(Registry registry) { + + } + + @Override + public void afterShutdown() { + + } + }); + + for (Device device : dlnaService.getControlPoint().getRegistry().getDevices()) { + deviceAdded(device); + } + dlnaService.getControlPoint().search(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + dlnaService = null; + } + }; + context.bindService(new Intent(context, AndroidUpnpServiceImpl.class), dlnaServiceConnection,Context.BIND_AUTO_CREATE); } private void broadcastDescriptors() { @@ -82,6 +160,7 @@ public class DLNARouteProvider extends MediaRouteProvider { setDescriptor(providerBuilder.build()); } + @Override public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) { if (request != null && request.isActiveScan()) { @@ -93,6 +172,26 @@ public class DLNARouteProvider extends MediaRouteProvider { return new DLNARouteController(downloadService); } + private void deviceAdded(Device device) { + if(device.getType().getType().equals("MediaRenderer") && device instanceof RemoteDevice) { + String id = device.getIdentity().getUdn().toString(); + String name = device.getDetails().getFriendlyName(); + String displayName = device.getDisplayString(); + Log.d(TAG, displayName); + + DLNADevice newDevice = new DLNADevice(id, name, displayName, 0, 10); + devices.put(id, newDevice); + broadcastDescriptors(); + } + } + private void deviceRemoved(Device device) { + if(device.getType().getType().equals("MediaRenderer") && device instanceof RemoteDevice) { + String id = device.getIdentity().getUdn().toString(); + devices.remove(id); + broadcastDescriptors(); + } + } + private class DLNARouteController extends RouteController { private DownloadService downloadService; diff --git a/src/github/daneren2005/dsub/util/MediaRouteManager.java b/src/github/daneren2005/dsub/util/MediaRouteManager.java index 347b0376..e356a1b6 100644 --- a/src/github/daneren2005/dsub/util/MediaRouteManager.java +++ b/src/github/daneren2005/dsub/util/MediaRouteManager.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; import github.daneren2005.dsub.domain.RemoteControlState; +import github.daneren2005.dsub.provider.DLNARouteProvider; import github.daneren2005.dsub.provider.JukeboxRouteProvider; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.RemoteController; @@ -145,6 +146,11 @@ public class MediaRouteManager extends MediaRouter.Callback { router.addProvider(jukeboxProvider); providers.add(jukeboxProvider); offlineProviders.add(jukeboxProvider); + + DLNARouteProvider dlnaProvider = new DLNARouteProvider(downloadService); + router.addProvider(dlnaProvider); + providers.add(dlnaProvider); + offlineProviders.add(dlnaProvider); } public void removeOfflineProviders() { for(MediaRouteProvider provider: offlineProviders) { @@ -165,6 +171,7 @@ public class MediaRouteManager extends MediaRouter.Callback { if(castAvailable) { builder.addControlCategory(CastCompat.getCastControlCategory()); } + builder.addControlCategory(DLNARouteProvider.CATEGORY_DLNA); selector = builder.build(); } } -- cgit v1.2.3 From 00cb1123339b2e62891872efa867bcbe54760da3 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 4 Nov 2014 21:43:19 -0800 Subject: Change to stable since alpha seems to not even start --- libs/cling-core-1.0.5.jar | Bin 0 -> 551922 bytes libs/cling-core-2.0-alpha3.jar | Bin 683382 -> 0 bytes libs/cling-support-1.0.5.jar | Bin 0 -> 359154 bytes libs/teleal-common-1.0.13.jar | Bin 0 -> 231831 bytes .../dsub/provider/DLNARouteProvider.java | 57 +++++++++++++++------ 5 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 libs/cling-core-1.0.5.jar delete mode 100644 libs/cling-core-2.0-alpha3.jar create mode 100644 libs/cling-support-1.0.5.jar create mode 100644 libs/teleal-common-1.0.13.jar (limited to 'src') diff --git a/libs/cling-core-1.0.5.jar b/libs/cling-core-1.0.5.jar new file mode 100644 index 00000000..8079f329 Binary files /dev/null and b/libs/cling-core-1.0.5.jar differ diff --git a/libs/cling-core-2.0-alpha3.jar b/libs/cling-core-2.0-alpha3.jar deleted file mode 100644 index 41bca580..00000000 Binary files a/libs/cling-core-2.0-alpha3.jar and /dev/null differ diff --git a/libs/cling-support-1.0.5.jar b/libs/cling-support-1.0.5.jar new file mode 100644 index 00000000..a0ca6363 Binary files /dev/null and b/libs/cling-support-1.0.5.jar differ diff --git a/libs/teleal-common-1.0.13.jar b/libs/teleal-common-1.0.13.jar new file mode 100644 index 00000000..2d6403ef Binary files /dev/null and b/libs/teleal-common-1.0.13.jar differ diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index 11c29d28..d86ebf20 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -33,15 +33,21 @@ import android.support.v7.media.MediaRouteProvider; import android.support.v7.media.MediaRouteProviderDescriptor; import android.util.Log; -import org.fourthline.cling.android.AndroidUpnpService; -import org.fourthline.cling.android.AndroidUpnpServiceImpl; -import org.fourthline.cling.model.meta.Device; -import org.fourthline.cling.model.meta.LocalDevice; -import org.fourthline.cling.model.meta.RemoteDevice; -import org.fourthline.cling.registry.Registry; -import org.fourthline.cling.registry.RegistryListener; +import org.teleal.cling.android.AndroidUpnpService; +import org.teleal.cling.android.AndroidUpnpServiceImpl; +import org.teleal.cling.model.action.ActionInvocation; +import org.teleal.cling.model.message.UpnpResponse; +import org.teleal.cling.model.meta.Device; +import org.teleal.cling.model.meta.LocalDevice; +import org.teleal.cling.model.meta.RemoteDevice; +import org.teleal.cling.model.meta.StateVariable; +import org.teleal.cling.model.meta.StateVariableAllowedValueRange; +import org.teleal.cling.registry.Registry; +import org.teleal.cling.registry.RegistryListener; +import org.teleal.cling.support.renderingcontrol.callback.GetVolume; import java.util.HashMap; +import java.util.List; import java.util.Map; import github.daneren2005.dsub.domain.DLNADevice; @@ -128,7 +134,7 @@ public class DLNARouteProvider extends MediaRouteProvider { dlnaService = null; } }; - context.bindService(new Intent(context, AndroidUpnpServiceImpl.class), dlnaServiceConnection,Context.BIND_AUTO_CREATE); + context.bindService(new Intent(context, AndroidUpnpServiceImpl.class), dlnaServiceConnection, Context.BIND_AUTO_CREATE); } private void broadcastDescriptors() { @@ -172,16 +178,35 @@ public class DLNARouteProvider extends MediaRouteProvider { return new DLNARouteController(downloadService); } - private void deviceAdded(Device device) { + private void deviceAdded(final Device device) { + final org.teleal.cling.model.meta.Service renderingControl = null; + if(device.getType().getType().equals("MediaRenderer") && device instanceof RemoteDevice) { - String id = device.getIdentity().getUdn().toString(); - String name = device.getDetails().getFriendlyName(); - String displayName = device.getDisplayString(); - Log.d(TAG, displayName); + dlnaService.getControlPoint().execute(new GetVolume(renderingControl) { + @Override + public void received(ActionInvocation actionInvocation, int currentVolume) { + int maxVolume = 100; + StateVariable volume = renderingControl.getStateVariable("Volume"); + if(volume != null) { + StateVariableAllowedValueRange volumeRange = volume.getTypeDetails().getAllowedValueRange(); + maxVolume = (int) volumeRange.getMaximum(); + } - DLNADevice newDevice = new DLNADevice(id, name, displayName, 0, 10); - devices.put(id, newDevice); - broadcastDescriptors(); + String id = device.getIdentity().getUdn().toString(); + String name = device.getDetails().getFriendlyName(); + String displayName = device.getDisplayString(); + + DLNADevice newDevice = new DLNADevice(id, name, displayName, currentVolume, maxVolume); + devices.put(id, newDevice); + broadcastDescriptors(); + } + + @Override + public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String s) { + Log.w(TAG, "Failed to get default volume for DLNA route"); + Log.w(TAG, "Reason: " + s); + } + }); } } private void deviceRemoved(Device device) { -- cgit v1.2.3 From 10c9ad416dafbe33f81746fdec1c48953ec71cee Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 5 Nov 2014 12:46:19 -0800 Subject: Modify DLNARouteController to require a DLNADevice --- .../dsub/provider/DLNARouteProvider.java | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index d86ebf20..25cb14db 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -175,7 +175,13 @@ public class DLNARouteProvider extends MediaRouteProvider { @Override public RouteController onCreateRouteController(String routeId) { - return new DLNARouteController(downloadService); + DLNADevice device = devices.get(routeId); + if(device == null) { + Log.w(TAG, "No device exists for " + routeId); + return null; + } + + return new DLNARouteController(device); } private void deviceAdded(final Device device) { @@ -213,15 +219,22 @@ public class DLNARouteProvider extends MediaRouteProvider { if(device.getType().getType().equals("MediaRenderer") && device instanceof RemoteDevice) { String id = device.getIdentity().getUdn().toString(); devices.remove(id); - broadcastDescriptors(); + + // Make sure we do this on the main thread + downloadService.post(new Runnable() { + @Override + public void run() { + broadcastDescriptors(); + } + }); } } private class DLNARouteController extends RouteController { - private DownloadService downloadService; + private DLNADevice device; - public DLNARouteController(DownloadService downloadService) { - this.downloadService = downloadService; + public DLNARouteController(DLNADevice device) { + this.device = device; } @Override @@ -241,8 +254,8 @@ public class DLNARouteProvider extends MediaRouteProvider { @Override public void onSelect() { - downloadService.setRemoteEnabled(RemoteControlState.DLNA); - controller = downloadService.getRemoteController(); + // controller = new DLNAController(device); + downloadService.setRemoteEnabled(RemoteControlState.DLNA, controller); } @Override -- cgit v1.2.3 From ede21d014b26a1f9d68b11fc7de64f8bd316c571 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 5 Nov 2014 13:38:45 -0800 Subject: Add rendering control service, don't try to add same device more then once at a time --- .../daneren2005/dsub/provider/DLNARouteProvider.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index 25cb14db..d4109a36 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -66,6 +66,7 @@ public class DLNARouteProvider extends MediaRouteProvider { private RemoteController controller; private HashMap devices = new HashMap(); + private List adding = new List(); private AndroidUpnpService dlnaService; private ServiceConnection dlnaServiceConnection; @@ -185,7 +186,17 @@ public class DLNARouteProvider extends MediaRouteProvider { } private void deviceAdded(final Device device) { - final org.teleal.cling.model.meta.Service renderingControl = null; + final org.teleal.cling.model.meta.Service renderingControl = device.findService(new ServiceType("schemas-upnp-org", "RenderingControl")); + if(renderingControl == null) { + return; + } + + final String id = device.getIdentity().getUdn().toString(); + // In the process of looking up it's details already + if(adding.contains(id)) { + return; + } + adding.add(id); if(device.getType().getType().equals("MediaRenderer") && device instanceof RemoteDevice) { dlnaService.getControlPoint().execute(new GetVolume(renderingControl) { @@ -198,6 +209,7 @@ public class DLNARouteProvider extends MediaRouteProvider { maxVolume = (int) volumeRange.getMaximum(); } + // Create a new DLNADevice to represent this item String id = device.getIdentity().getUdn().toString(); String name = device.getDetails().getFriendlyName(); String displayName = device.getDisplayString(); @@ -205,12 +217,14 @@ public class DLNARouteProvider extends MediaRouteProvider { DLNADevice newDevice = new DLNADevice(id, name, displayName, currentVolume, maxVolume); devices.put(id, newDevice); broadcastDescriptors(); + adding.remove(id); } @Override public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String s) { Log.w(TAG, "Failed to get default volume for DLNA route"); Log.w(TAG, "Reason: " + s); + adding.remove(id); } }); } -- cgit v1.2.3 From a1ffd954c2b1d0a4907424187ea8b7db3f697e02 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 5 Nov 2014 14:17:19 -0800 Subject: Allow downloadService to post runnables to service thread --- src/github/daneren2005/dsub/service/DownloadService.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index 50a2d34e..addcb0a2 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -300,6 +300,10 @@ public class DownloadService extends Service { public IBinder onBind(Intent intent) { return binder; } + + public void post(Runnable r) { + handler.post(r); + } public synchronized void download(List songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) { download(songs, save, autoplay, playNext, shuffle, 0, 0); -- cgit v1.2.3 From f5e89a8b20f092a8986168dba5e9e0a4c3e58c46 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 5 Nov 2014 16:02:57 -0800 Subject: Fix crash when adding new device on separate thread --- src/github/daneren2005/dsub/provider/DLNARouteProvider.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index d4109a36..ccd192eb 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -42,10 +42,12 @@ import org.teleal.cling.model.meta.LocalDevice; import org.teleal.cling.model.meta.RemoteDevice; import org.teleal.cling.model.meta.StateVariable; import org.teleal.cling.model.meta.StateVariableAllowedValueRange; +import org.teleal.cling.model.types.ServiceType; import org.teleal.cling.registry.Registry; import org.teleal.cling.registry.RegistryListener; import org.teleal.cling.support.renderingcontrol.callback.GetVolume; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -66,7 +68,7 @@ public class DLNARouteProvider extends MediaRouteProvider { private RemoteController controller; private HashMap devices = new HashMap(); - private List adding = new List(); + private List adding = new ArrayList(); private AndroidUpnpService dlnaService; private ServiceConnection dlnaServiceConnection; @@ -216,7 +218,12 @@ public class DLNARouteProvider extends MediaRouteProvider { DLNADevice newDevice = new DLNADevice(id, name, displayName, currentVolume, maxVolume); devices.put(id, newDevice); - broadcastDescriptors(); + downloadService.post(new Runnable() { + @Override + public void run() { + broadcastDescriptors(); + } + }); adding.remove(id); } -- cgit v1.2.3 From cad9cb728188d267cc8990ceb0f4ca9f8b60856d Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 5 Nov 2014 16:03:37 -0800 Subject: Rename add/remove offline providers to online --- src/github/daneren2005/dsub/service/DownloadService.java | 6 ++---- src/github/daneren2005/dsub/util/MediaRouteManager.java | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index addcb0a2..4cbf2317 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -53,7 +53,6 @@ import github.daneren2005.dsub.view.UpdateView; import github.daneren2005.serverproxy.BufferProxy; import java.io.File; -import java.io.IOError; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -70,7 +69,6 @@ import android.content.SharedPreferences; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.audiofx.AudioEffect; -import android.media.audiofx.Equalizer; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -558,9 +556,9 @@ public class DownloadService extends Service { public void setOnline(final boolean online) { if(online) { - mediaRouter.addOfflineProviders(); + mediaRouter.addOnlineProviders(); } else { - mediaRouter.removeOfflineProviders(); + mediaRouter.removeOnlineProviders(); } lifecycleSupport.post(new Runnable() { diff --git a/src/github/daneren2005/dsub/util/MediaRouteManager.java b/src/github/daneren2005/dsub/util/MediaRouteManager.java index e356a1b6..11e0d387 100644 --- a/src/github/daneren2005/dsub/util/MediaRouteManager.java +++ b/src/github/daneren2005/dsub/util/MediaRouteManager.java @@ -141,7 +141,7 @@ public class MediaRouteManager extends MediaRouter.Callback { } } - public void addOfflineProviders() { + public void addOnlineProviders() { JukeboxRouteProvider jukeboxProvider = new JukeboxRouteProvider(downloadService); router.addProvider(jukeboxProvider); providers.add(jukeboxProvider); @@ -152,7 +152,7 @@ public class MediaRouteManager extends MediaRouter.Callback { providers.add(dlnaProvider); offlineProviders.add(dlnaProvider); } - public void removeOfflineProviders() { + public void removeOnlineProviders() { for(MediaRouteProvider provider: offlineProviders) { router.removeProvider(provider); } @@ -160,7 +160,7 @@ public class MediaRouteManager extends MediaRouter.Callback { private void addProviders() { if(!Util.isOffline(downloadService)) { - addOfflineProviders(); + addOnlineProviders(); } } public void buildSelector() { -- cgit v1.2.3 From 2faca3078d6504ace3f646caf34f82131cb73aae Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 5 Nov 2014 16:05:02 -0800 Subject: Added copyright message --- src/github/daneren2005/dsub/domain/DLNADevice.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src') diff --git a/src/github/daneren2005/dsub/domain/DLNADevice.java b/src/github/daneren2005/dsub/domain/DLNADevice.java index 9f9daa00..b18000d0 100644 --- a/src/github/daneren2005/dsub/domain/DLNADevice.java +++ b/src/github/daneren2005/dsub/domain/DLNADevice.java @@ -1,3 +1,22 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2014 (C) Scott Jackson +*/ + package github.daneren2005.dsub.domain; import android.os.Parcel; -- cgit v1.2.3 From dc0c636622398aab1cd4718fbe70021ad317b59b Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Thu, 6 Nov 2014 20:29:54 -0800 Subject: Add DLNAController stub --- .../dsub/provider/DLNARouteProvider.java | 3 +- .../daneren2005/dsub/service/DLNAController.java | 81 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/github/daneren2005/dsub/service/DLNAController.java (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index ccd192eb..ca6fabe0 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -54,6 +54,7 @@ import java.util.Map; import github.daneren2005.dsub.domain.DLNADevice; import github.daneren2005.dsub.domain.RemoteControlState; +import github.daneren2005.dsub.service.DLNAController; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.RemoteController; @@ -275,7 +276,7 @@ public class DLNARouteProvider extends MediaRouteProvider { @Override public void onSelect() { - // controller = new DLNAController(device); + controller = new DLNAController(device); downloadService.setRemoteEnabled(RemoteControlState.DLNA, controller); } diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java new file mode 100644 index 00000000..dee65d64 --- /dev/null +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -0,0 +1,81 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2014 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.service; + +import github.daneren2005.dsub.domain.DLNADevice; + +public class DLNAController extends RemoteController { + DLNADevice device; + + public DLNAController(DLNADevice device) { + this.device = device; + } + + @Override + public void create(boolean playing, int seconds) { + + } + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public void shutdown() { + + } + + @Override + public void updatePlaylist() { + + } + + @Override + public void changePosition(int seconds) { + + } + + @Override + public void changeTrack(int index, DownloadFile song) { + + } + + @Override + public void setVolume(int volume) { + + } + + @Override + public void updateVolume(boolean up) { + + } + + @Override + public double getVolume() { + return 0; + } + + @Override + public int getRemotePosition() { + return 0; + } +} -- cgit v1.2.3 From da5e8cbdaa155f5e9a32297a297cb57b6ffa3e15 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 26 Nov 2014 17:10:02 -0800 Subject: Get a basic working push to XBMC working --- src/github/daneren2005/dsub/domain/DLNADevice.java | 6 +- .../dsub/provider/DLNARouteProvider.java | 4 +- .../dsub/service/ChromeCastController.java | 20 +-- .../daneren2005/dsub/service/DLNAController.java | 186 ++++++++++++++++++++- .../daneren2005/dsub/service/DownloadService.java | 2 +- src/github/daneren2005/dsub/util/Util.java | 16 ++ 6 files changed, 206 insertions(+), 28 deletions(-) (limited to 'src') 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 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)); } -- cgit v1.2.3 From d09912ac3943f9bceb6033a7a1a37bd0362a8a64 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 29 Nov 2014 16:32:48 -0800 Subject: More DLNA development --- .../daneren2005/dsub/service/DLNAController.java | 100 +++++++++++++++++++-- 1 file changed, 95 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 596f7762..a5e9a8ef 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -15,6 +15,8 @@ package github.daneren2005.dsub.service; +import android.content.SharedPreferences; +import android.os.Looper; import android.util.Log; import org.teleal.cling.controlpoint.ControlPoint; @@ -24,6 +26,7 @@ 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.meta.StateVariable; import org.teleal.cling.model.state.StateVariableValue; import org.teleal.cling.model.types.ServiceType; import org.teleal.cling.support.avtransport.callback.Pause; @@ -32,15 +35,25 @@ 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.contentdirectory.DIDLParser; import org.teleal.cling.support.lastchange.LastChange; - +import org.teleal.cling.support.model.DIDLContent; +import org.teleal.cling.support.model.Res; +import org.teleal.cling.support.model.item.Item; +import org.teleal.cling.support.model.item.MusicTrack; +import org.teleal.cling.support.model.item.VideoItem; +import org.teleal.common.util.MimeType; + +import java.util.Iterator; 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.Constants; import github.daneren2005.dsub.util.Util; +import github.daneren2005.serverproxy.FileProxy; public class DLNAController extends RemoteController { private static final String TAG = DLNAController.class.getSimpleName(); @@ -49,10 +62,17 @@ public class DLNAController extends RemoteController { ControlPoint controlPoint; SubscriptionCallback callback; + private FileProxy proxy; + String rootLocation = ""; + boolean error = false; + public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) { this.downloadService = downloadService; this.controlPoint = controlPoint; this.device = device; + + SharedPreferences prefs = Util.getPreferences(downloadService); + rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null); } @Override @@ -94,7 +114,18 @@ public class DLNAController extends RemoteController { downloadService.setPlayerState(PlayerState.PAUSED); break; case STOPPED: - downloadService.setPlayerState(PlayerState.STOPPED); + boolean failed = false; + for(StateVariableValue val: m.values()) { + if(val.toString().indexOf("TransportStatus val=\"ERROR_OCCURRED\"") != -1) { + failed = true; + } + } + + if(failed) { + failedLoad(); + } else { + downloadService.setPlayerState(PlayerState.STOPPED); + } break; case TRANSITIONING: downloadService.setPlayerState(PlayerState.PREPARING); @@ -121,6 +152,12 @@ public class DLNAController extends RemoteController { @Override public void start() { + if(error) { + Log.w(TAG, "Attempting to restart song"); + startSong(downloadService.getCurrentPlaying(), true, 0); + return; + } + controlPoint.execute(new Play(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) { @Override public void success(ActionInvocation invocation) { @@ -205,15 +242,57 @@ public class DLNAController extends RemoteController { downloadService.setPlayerState(PlayerState.IDLE); return; } + error = false; downloadService.setPlayerState(PlayerState.PREPARING); MusicDirectory.Entry song = currentPlaying.getSong(); try { + // Get url for entry MusicService musicService = MusicServiceFactory.getMusicService(downloadService); - String url = musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate()); - url = Util.replaceInternalUrl(downloadService, url); + String url; + 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 = Util.replaceInternalUrl(downloadService, url); + } + + // Create metadata for entry + Item track; + if(song.isVideo()) { + track = new VideoItem(song.getId(), song.getParent(), song.getTitle(), song.getArtist()); + } else { + MusicTrack musicTrack = new MusicTrack(song.getId(), song.getParent(), song.getTitle(), song.getArtist(), song.getAlbum(), song.getArtist()); + musicTrack.setOriginalTrackNumber(song.getTrack()); + track = musicTrack; + } + + DIDLParser parser = new DIDLParser(); + DIDLContent didl = new DIDLContent(); + didl.addItem(track); + String metadata = ""; + try { + // metadata = parser.generate(didl); + } catch(Exception e) { + Log.w(TAG, "Metadata generation failed", e); + } controlPoint.execute(new SetAVTransportURI(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")), url, metadata) { @Override @@ -249,7 +328,18 @@ public class DLNAController extends RemoteController { } private void failedLoad() { - Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load)); downloadService.setPlayerState(PlayerState.STOPPED); + error = true; + + if(Looper.myLooper() != Looper.getMainLooper()) { + downloadService.post(new Runnable() { + @Override + public void run() { + Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load)); + } + }); + } else { + Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load)); + } } } -- cgit v1.2.3 From d0fedd64b81004d5c0d9bd773f395eab4b0f7519 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 29 Nov 2014 17:13:11 -0800 Subject: Apply more DLNA logic --- .../daneren2005/dsub/service/DLNAController.java | 39 +++++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index a5e9a8ef..2448b088 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -31,6 +31,7 @@ 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.Seek; import org.teleal.cling.support.avtransport.callback.SetAVTransportURI; import org.teleal.cling.support.avtransport.callback.Stop; import org.teleal.cling.support.avtransport.lastchange.AVTransportLastChangeParser; @@ -38,14 +39,16 @@ import org.teleal.cling.support.avtransport.lastchange.AVTransportVariable; import org.teleal.cling.support.contentdirectory.DIDLParser; import org.teleal.cling.support.lastchange.LastChange; import org.teleal.cling.support.model.DIDLContent; -import org.teleal.cling.support.model.Res; +import org.teleal.cling.support.model.SeekMode; import org.teleal.cling.support.model.item.Item; import org.teleal.cling.support.model.item.MusicTrack; import org.teleal.cling.support.model.item.VideoItem; -import org.teleal.common.util.MimeType; +import org.teleal.cling.support.renderingcontrol.callback.SetVolume; -import java.util.Iterator; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Map; +import java.util.TimeZone; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.DLNADevice; @@ -117,6 +120,7 @@ public class DLNAController extends RemoteController { boolean failed = false; for(StateVariableValue val: m.values()) { if(val.toString().indexOf("TransportStatus val=\"ERROR_OCCURRED\"") != -1) { + Log.w(TAG, "Failed to load with event: val.toString()"); failed = true; } } @@ -204,32 +208,49 @@ public class DLNAController extends RemoteController { @Override public void updatePlaylist() { - + if(downloadService.getCurrentPlaying() == null) { + startSong(null, false, 0); + } } @Override public void changePosition(int seconds) { - + SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + controlPoint.execute(new Seek(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")), SeekMode.REL_TIME, df.format(new Date(seconds * 1000))) { + @SuppressWarnings("rawtypes") + @Override + public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMessage) { + Log.w(TAG, "Seek failed: " + defaultMessage); + } + }); } @Override public void changeTrack(int index, DownloadFile song) { - + startSong(song, true, 0); } @Override public void setVolume(int volume) { - + device.volume = volume; + controlPoint.execute(new SetVolume(device.renderer.findService(new ServiceType("schemas-upnp-org", "RenderingControl")), volume) { + @SuppressWarnings("rawtypes") + @Override + public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMessage) { + Log.w(TAG, "Set volume failed: " + defaultMessage); + } + }); } @Override public void updateVolume(boolean up) { - + setVolume(device.volume + (up ? 1 : -1)); } @Override public double getVolume() { - return 0; + return device.volume; } @Override -- cgit v1.2.3 From 6471d5337d542fde05ece58b9337a60b3411fe8b Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Mon, 8 Dec 2014 15:43:44 -0800 Subject: Upgrade to Cling 2.0.1, up min SDK to 4.0+ --- AndroidManifest.xml | 4 +- libs/cling-core-1.0.5.jar | Bin 551922 -> 0 bytes libs/cling-core-2.0.1.jar | Bin 0 -> 686501 bytes libs/cling-support-1.0.5.jar | Bin 359154 -> 0 bytes libs/cling-support-2.0.1.jar | Bin 0 -> 490043 bytes libs/javax.servlet-3.0.0.v201112011016.jar | Bin 0 -> 200387 bytes libs/jetty-all-8.1.16.v20140903.jar | Bin 0 -> 1880786 bytes libs/seamless-http-1.1.0.jar | Bin 0 -> 21646 bytes libs/seamless-util-1.1.0.jar | Bin 0 -> 94456 bytes libs/seamless-xml-1.1.0.jar | Bin 0 -> 63142 bytes libs/teleal-common-1.0.13.jar | Bin 231831 -> 0 bytes src/github/daneren2005/dsub/domain/DLNADevice.java | 2 +- .../dsub/provider/DLNARouteProvider.java | 129 +++++++++++++++++---- .../daneren2005/dsub/service/DLNAController.java | 53 +++++---- 14 files changed, 138 insertions(+), 50 deletions(-) delete mode 100644 libs/cling-core-1.0.5.jar create mode 100644 libs/cling-core-2.0.1.jar delete mode 100644 libs/cling-support-1.0.5.jar create mode 100644 libs/cling-support-2.0.1.jar create mode 100644 libs/javax.servlet-3.0.0.v201112011016.jar create mode 100644 libs/jetty-all-8.1.16.v20140903.jar create mode 100644 libs/seamless-http-1.1.0.jar create mode 100644 libs/seamless-util-1.1.0.jar create mode 100644 libs/seamless-xml-1.1.0.jar delete mode 100644 libs/teleal-common-1.0.13.jar (limited to 'src') diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 25734167..bbedcea4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -30,7 +30,7 @@ - + @@ -86,7 +86,7 @@ - + diff --git a/libs/cling-core-1.0.5.jar b/libs/cling-core-1.0.5.jar deleted file mode 100644 index 8079f329..00000000 Binary files a/libs/cling-core-1.0.5.jar and /dev/null differ diff --git a/libs/cling-core-2.0.1.jar b/libs/cling-core-2.0.1.jar new file mode 100644 index 00000000..632d3038 Binary files /dev/null and b/libs/cling-core-2.0.1.jar differ diff --git a/libs/cling-support-1.0.5.jar b/libs/cling-support-1.0.5.jar deleted file mode 100644 index a0ca6363..00000000 Binary files a/libs/cling-support-1.0.5.jar and /dev/null differ diff --git a/libs/cling-support-2.0.1.jar b/libs/cling-support-2.0.1.jar new file mode 100644 index 00000000..7fa28604 Binary files /dev/null and b/libs/cling-support-2.0.1.jar differ diff --git a/libs/javax.servlet-3.0.0.v201112011016.jar b/libs/javax.servlet-3.0.0.v201112011016.jar new file mode 100644 index 00000000..b1354096 Binary files /dev/null and b/libs/javax.servlet-3.0.0.v201112011016.jar differ diff --git a/libs/jetty-all-8.1.16.v20140903.jar b/libs/jetty-all-8.1.16.v20140903.jar new file mode 100644 index 00000000..25b1d324 Binary files /dev/null and b/libs/jetty-all-8.1.16.v20140903.jar differ diff --git a/libs/seamless-http-1.1.0.jar b/libs/seamless-http-1.1.0.jar new file mode 100644 index 00000000..98ec884a Binary files /dev/null and b/libs/seamless-http-1.1.0.jar differ diff --git a/libs/seamless-util-1.1.0.jar b/libs/seamless-util-1.1.0.jar new file mode 100644 index 00000000..12026b7f Binary files /dev/null and b/libs/seamless-util-1.1.0.jar differ diff --git a/libs/seamless-xml-1.1.0.jar b/libs/seamless-xml-1.1.0.jar new file mode 100644 index 00000000..1e740877 Binary files /dev/null and b/libs/seamless-xml-1.1.0.jar differ diff --git a/libs/teleal-common-1.0.13.jar b/libs/teleal-common-1.0.13.jar deleted file mode 100644 index 2d6403ef..00000000 Binary files a/libs/teleal-common-1.0.13.jar and /dev/null differ diff --git a/src/github/daneren2005/dsub/domain/DLNADevice.java b/src/github/daneren2005/dsub/domain/DLNADevice.java index ba4c2777..2de84013 100644 --- a/src/github/daneren2005/dsub/domain/DLNADevice.java +++ b/src/github/daneren2005/dsub/domain/DLNADevice.java @@ -22,7 +22,7 @@ package github.daneren2005.dsub.domain; import android.os.Parcel; import android.os.Parcelable; -import org.teleal.cling.model.meta.Device; +import org.fourthline.cling.model.meta.Device; /** * Created by Scott on 11/1/2014. diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index 44cde0ef..3601e696 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -33,19 +33,20 @@ import android.support.v7.media.MediaRouteProvider; import android.support.v7.media.MediaRouteProviderDescriptor; import android.util.Log; -import org.teleal.cling.android.AndroidUpnpService; -import org.teleal.cling.android.AndroidUpnpServiceImpl; -import org.teleal.cling.model.action.ActionInvocation; -import org.teleal.cling.model.message.UpnpResponse; -import org.teleal.cling.model.meta.Device; -import org.teleal.cling.model.meta.LocalDevice; -import org.teleal.cling.model.meta.RemoteDevice; -import org.teleal.cling.model.meta.StateVariable; -import org.teleal.cling.model.meta.StateVariableAllowedValueRange; -import org.teleal.cling.model.types.ServiceType; -import org.teleal.cling.registry.Registry; -import org.teleal.cling.registry.RegistryListener; -import org.teleal.cling.support.renderingcontrol.callback.GetVolume; +import org.eclipse.jetty.util.log.Logger; +import org.fourthline.cling.android.AndroidUpnpService; +import org.fourthline.cling.android.AndroidUpnpServiceImpl; +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.message.UpnpResponse; +import org.fourthline.cling.model.meta.Device; +import org.fourthline.cling.model.meta.LocalDevice; +import org.fourthline.cling.model.meta.RemoteDevice; +import org.fourthline.cling.model.meta.StateVariable; +import org.fourthline.cling.model.meta.StateVariableAllowedValueRange; +import org.fourthline.cling.model.types.ServiceType; +import org.fourthline.cling.registry.Registry; +import org.fourthline.cling.registry.RegistryListener; +import org.fourthline.cling.support.renderingcontrol.callback.GetVolume; import java.util.ArrayList; import java.util.HashMap; @@ -58,9 +59,6 @@ import github.daneren2005.dsub.service.DLNAController; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.RemoteController; -/** - * Created by Scott on 11/28/13. - */ public class DLNARouteProvider extends MediaRouteProvider { private static final String TAG = DLNARouteProvider.class.getSimpleName(); public static final String CATEGORY_DLNA = "github.daneren2005.dsub.DLNA"; @@ -75,6 +73,10 @@ public class DLNARouteProvider extends MediaRouteProvider { public DLNARouteProvider(Context context) { super(context); + + // Use custom logger + org.eclipse.jetty.util.log.Log.setLog(new JettyAndroidLog()); + this.downloadService = (DownloadService) context; dlnaServiceConnection = new ServiceConnection() { @Override @@ -83,12 +85,12 @@ public class DLNARouteProvider extends MediaRouteProvider { dlnaService.getRegistry().addListener(new RegistryListener() { @Override public void remoteDeviceDiscoveryStarted(Registry registry, RemoteDevice remoteDevice) { - + Log.i(TAG, "Stared DLNA discovery"); } @Override public void remoteDeviceDiscoveryFailed(Registry registry, RemoteDevice remoteDevice, Exception e) { - + Log.w(TAG, "Failed to discover DLNA devices"); } @Override @@ -138,7 +140,10 @@ public class DLNARouteProvider extends MediaRouteProvider { dlnaService = null; } }; - context.bindService(new Intent(context, AndroidUpnpServiceImpl.class), dlnaServiceConnection, Context.BIND_AUTO_CREATE); + + if(!context.getApplicationContext().bindService(new Intent(context, AndroidUpnpServiceImpl.class), dlnaServiceConnection, Context.BIND_AUTO_CREATE)) { + Log.e(TAG, "Failed to bind to DLNA service"); + } } private void broadcastDescriptors() { @@ -189,7 +194,7 @@ public class DLNARouteProvider extends MediaRouteProvider { } private void deviceAdded(final Device device) { - final org.teleal.cling.model.meta.Service renderingControl = device.findService(new ServiceType("schemas-upnp-org", "RenderingControl")); + final org.fourthline.cling.model.meta.Service renderingControl = device.findService(new ServiceType("schemas-upnp-org", "RenderingControl")); if(renderingControl == null) { return; } @@ -235,6 +240,8 @@ public class DLNARouteProvider extends MediaRouteProvider { adding.remove(id); } }); + } else { + adding.remove(id); } } private void deviceRemoved(Device device) { @@ -302,4 +309,86 @@ public class DLNARouteProvider extends MediaRouteProvider { broadcastDescriptors(); } } + + public static class JettyAndroidLog implements Logger { + final private static java.util.logging.Logger log = java.util.logging.Logger.getLogger("Jetty"); + + public static boolean __isIgnoredEnabled = false; + public String _name; + + public JettyAndroidLog() { + this (JettyAndroidLog.class.getName()); + } + + public JettyAndroidLog(String name) { + _name = name; + } + + public String getName () { + return _name; + } + + public void debug(Throwable th) { + // Log.d(TAG, "", th); + } + + public void debug(String msg, Throwable th) { + // Log.d(TAG, msg, th); + } + + public void debug(String msg, Object... args) { + // Log.d(TAG, msg); + } + + public Logger getLogger(String name) { + return new JettyAndroidLog(name); + } + + public void info(String msg, Object... args) { + Log.i(TAG, msg); + } + + public void info(Throwable th) { + Log.i(TAG, "", th); + } + + public void info(String msg, Throwable th) { + Log.i(TAG, msg, th); + } + + public boolean isDebugEnabled() { + return false; + } + + public void warn(Throwable th) { + Log.w(TAG, "", th); + } + + public void warn(String msg, Object... args) { + Log.w(TAG, msg); + } + + public void warn(String msg, Throwable th) { + Log.w(TAG, msg, th); + } + + public boolean isIgnoredEnabled () { + return __isIgnoredEnabled; + } + + + public void ignore(Throwable ignored) { + if (__isIgnoredEnabled) { + warn("IGNORED", ignored); + } + } + + public void setIgnoredEnabled(boolean enabled) { + __isIgnoredEnabled = enabled; + } + + public void setDebugEnabled(boolean enabled) { + + } + } } diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 2448b088..522bf586 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -19,31 +19,30 @@ import android.content.SharedPreferences; import android.os.Looper; 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.meta.StateVariable; -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.Seek; -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.contentdirectory.DIDLParser; -import org.teleal.cling.support.lastchange.LastChange; -import org.teleal.cling.support.model.DIDLContent; -import org.teleal.cling.support.model.SeekMode; -import org.teleal.cling.support.model.item.Item; -import org.teleal.cling.support.model.item.MusicTrack; -import org.teleal.cling.support.model.item.VideoItem; -import org.teleal.cling.support.renderingcontrol.callback.SetVolume; +import org.fourthline.cling.controlpoint.ControlPoint; +import org.fourthline.cling.controlpoint.SubscriptionCallback; +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.gena.CancelReason; +import org.fourthline.cling.model.gena.GENASubscription; +import org.fourthline.cling.model.message.UpnpResponse; +import org.fourthline.cling.model.meta.Device; +import org.fourthline.cling.model.state.StateVariableValue; +import org.fourthline.cling.model.types.ServiceType; +import org.fourthline.cling.support.avtransport.callback.Pause; +import org.fourthline.cling.support.avtransport.callback.Play; +import org.fourthline.cling.support.avtransport.callback.Seek; +import org.fourthline.cling.support.avtransport.callback.SetAVTransportURI; +import org.fourthline.cling.support.avtransport.callback.Stop; +import org.fourthline.cling.support.avtransport.lastchange.AVTransportLastChangeParser; +import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable; +import org.fourthline.cling.support.contentdirectory.DIDLParser; +import org.fourthline.cling.support.lastchange.LastChange; +import org.fourthline.cling.support.model.DIDLContent; +import org.fourthline.cling.support.model.SeekMode; +import org.fourthline.cling.support.model.item.Item; +import org.fourthline.cling.support.model.item.MusicTrack; +import org.fourthline.cling.support.model.item.VideoItem; +import org.fourthline.cling.support.renderingcontrol.callback.SetVolume; import java.text.SimpleDateFormat; import java.util.Date; @@ -120,7 +119,7 @@ public class DLNAController extends RemoteController { boolean failed = false; for(StateVariableValue val: m.values()) { if(val.toString().indexOf("TransportStatus val=\"ERROR_OCCURRED\"") != -1) { - Log.w(TAG, "Failed to load with event: val.toString()"); + Log.w(TAG, "Failed to load with event: " + val.toString()); failed = true; } } @@ -195,7 +194,7 @@ public class DLNAController extends RemoteController { 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) { + public void failure(ActionInvocation invocation, org.fourthline.cling.model.message.UpnpResponse operation, String defaultMessage) { Log.w(TAG, "Stop failed: " + defaultMessage); } }); -- cgit v1.2.3 From 62043e9f81f52cec492a7736f3514acc54789ff8 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 10 Dec 2014 15:16:52 -0800 Subject: Don't log info either, too much --- src/github/daneren2005/dsub/provider/DLNARouteProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index 3601e696..4a1ea067 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -345,15 +345,15 @@ public class DLNARouteProvider extends MediaRouteProvider { } public void info(String msg, Object... args) { - Log.i(TAG, msg); + // Log.i(TAG, msg); } public void info(Throwable th) { - Log.i(TAG, "", th); + // Log.i(TAG, "", th); } public void info(String msg, Throwable th) { - Log.i(TAG, msg, th); + // Log.i(TAG, msg, th); } public boolean isDebugEnabled() { -- cgit v1.2.3 From 22d7ca9e2d67fcac6966b3abc3c5d43884ebd145 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 10 Dec 2014 15:29:26 -0800 Subject: Hide actual volume quantity behind 10 volume increments --- src/github/daneren2005/dsub/provider/DLNARouteProvider.java | 8 ++++++-- src/github/daneren2005/dsub/service/DLNAController.java | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index 4a1ea067..e2450f1f 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -161,13 +161,17 @@ public class DLNARouteProvider extends MediaRouteProvider { for(Map.Entry deviceEntry: devices.entrySet()) { DLNADevice device = deviceEntry.getValue(); + int increments = device.volumeMax / 10; + int volume = controller == null ? device.volume : (int) controller.getVolume(); + volume = volume / increments; + MediaRouteDescriptor.Builder routeBuilder = new MediaRouteDescriptor.Builder(device.id, device.name); routeBuilder.addControlFilter(routeIntentFilter) .setPlaybackStream(AudioManager.STREAM_MUSIC) .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE) .setDescription(device.description) - .setVolume(controller == null ? 5 : (int) (controller.getVolume() * 10)) - .setVolumeMax(device.volumeMax) + .setVolume(volume) + .setVolumeMax(10) .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE); providerBuilder.addRoute(routeBuilder.build()); } diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 522bf586..b863068b 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -232,6 +232,12 @@ public class DLNAController extends RemoteController { @Override public void setVolume(int volume) { + if(volume < 0) { + volume = 0; + } else if(volume > device.volumeMax) { + volume = device.volumeMax; + } + device.volume = volume; controlPoint.execute(new SetVolume(device.renderer.findService(new ServiceType("schemas-upnp-org", "RenderingControl")), volume) { @SuppressWarnings("rawtypes") @@ -244,7 +250,8 @@ public class DLNAController extends RemoteController { @Override public void updateVolume(boolean up) { - setVolume(device.volume + (up ? 1 : -1)); + int increment = device.volumeMax / 10; + setVolume(device.volume + (up ? increment : -increment)); } @Override -- cgit v1.2.3 From 15612997174a9cce9566166775de1c42f71f18e7 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 10 Dec 2014 17:35:29 -0800 Subject: Add position tracking and basic lose of remote control handling --- .../daneren2005/dsub/service/DLNAController.java | 109 +++++++++++++++++---- .../daneren2005/dsub/service/DownloadService.java | 4 + 2 files changed, 92 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index b863068b..0a90c2d7 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -16,7 +16,9 @@ package github.daneren2005.dsub.service; import android.content.SharedPreferences; +import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.util.Log; import org.fourthline.cling.controlpoint.ControlPoint; @@ -26,8 +28,10 @@ import org.fourthline.cling.model.gena.CancelReason; import org.fourthline.cling.model.gena.GENASubscription; import org.fourthline.cling.model.message.UpnpResponse; import org.fourthline.cling.model.meta.Device; +import org.fourthline.cling.model.meta.Service; import org.fourthline.cling.model.state.StateVariableValue; import org.fourthline.cling.model.types.ServiceType; +import org.fourthline.cling.support.avtransport.callback.GetPositionInfo; import org.fourthline.cling.support.avtransport.callback.Pause; import org.fourthline.cling.support.avtransport.callback.Play; import org.fourthline.cling.support.avtransport.callback.Seek; @@ -38,6 +42,7 @@ import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable; import org.fourthline.cling.support.contentdirectory.DIDLParser; import org.fourthline.cling.support.lastchange.LastChange; import org.fourthline.cling.support.model.DIDLContent; +import org.fourthline.cling.support.model.PositionInfo; import org.fourthline.cling.support.model.SeekMode; import org.fourthline.cling.support.model.item.Item; import org.fourthline.cling.support.model.item.MusicTrack; @@ -48,6 +53,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicLong; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.DLNADevice; @@ -59,6 +65,7 @@ import github.daneren2005.serverproxy.FileProxy; public class DLNAController extends RemoteController { private static final String TAG = DLNAController.class.getSimpleName(); + private static final long STATUS_UPDATE_INTERVAL_SECONDS = 3L; DLNADevice device; ControlPoint controlPoint; @@ -68,6 +75,11 @@ public class DLNAController extends RemoteController { String rootLocation = ""; boolean error = false; + final AtomicLong lastUpdate = new AtomicLong(); + int currentPosition = 0; + String currentPlayingURI; + boolean running = true; + public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) { this.downloadService = downloadService; this.controlPoint = controlPoint; @@ -81,9 +93,7 @@ public class DLNAController extends RemoteController { 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) { + callback = new SubscriptionCallback(getTransportService(), 600) { @Override protected void failed(GENASubscription genaSubscription, UpnpResponse upnpResponse, Exception e, String msg) { Log.w(TAG, "Register subscription callback failed: " + msg, e); @@ -161,7 +171,7 @@ public class DLNAController extends RemoteController { return; } - controlPoint.execute(new Play(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) { + controlPoint.execute(new Play(getTransportService()) { @Override public void success(ActionInvocation invocation) { downloadService.setPlayerState(PlayerState.STARTED); @@ -177,7 +187,7 @@ public class DLNAController extends RemoteController { @Override public void stop() { - controlPoint.execute(new Pause(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) { + controlPoint.execute(new Pause(getTransportService()) { @Override public void success(ActionInvocation invocation) { downloadService.setPlayerState(PlayerState.PAUSED); @@ -192,7 +202,7 @@ public class DLNAController extends RemoteController { @Override public void shutdown() { - controlPoint.execute(new Stop(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport"))) { + controlPoint.execute(new Stop(getTransportService()) { @Override public void failure(ActionInvocation invocation, org.fourthline.cling.model.message.UpnpResponse operation, String defaultMessage) { Log.w(TAG, "Stop failed: " + defaultMessage); @@ -203,6 +213,8 @@ public class DLNAController extends RemoteController { callback.end(); callback = null; } + + running = false; } @Override @@ -216,7 +228,7 @@ public class DLNAController extends RemoteController { public void changePosition(int seconds) { SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); df.setTimeZone(TimeZone.getTimeZone("UTC")); - controlPoint.execute(new Seek(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")), SeekMode.REL_TIME, df.format(new Date(seconds * 1000))) { + controlPoint.execute(new Seek(getTransportService(), SeekMode.REL_TIME, df.format(new Date(seconds * 1000))) { @SuppressWarnings("rawtypes") @Override public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMessage) { @@ -261,7 +273,8 @@ public class DLNAController extends RemoteController { @Override public int getRemotePosition() { - return 0; + int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L); + return currentPosition + secondsSinceLastUpdate; } private void startSong(final DownloadFile currentPlaying, final boolean autoStart, final int position) { @@ -321,25 +334,21 @@ public class DLNAController extends RemoteController { Log.w(TAG, "Metadata generation failed", e); } - controlPoint.execute(new SetAVTransportURI(device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")), url, metadata) { + currentPlayingURI = url; + controlPoint.execute(new SetAVTransportURI(getTransportService(), 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); - } + if(position != 0) { + changePosition(position); + } - @Override - public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String msg) { - Log.w(TAG, "Failed to start playing: " + msg); - failedLoad(); - } - }); + if (autoStart) { + start(); } else { downloadService.setPlayerState(PlayerState.PAUSED); } + + getUpdatedStatus(); } @Override @@ -369,4 +378,62 @@ public class DLNAController extends RemoteController { Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load)); } } + + private Service getTransportService() { + return device.renderer.findService(new ServiceType("schemas-upnp-org", "AVTransport")); + } + + private void getUpdatedStatus() { + // Don't care if shutdown in the meantime + if(!running) { + return; + } + + controlPoint.execute(new GetPositionInfo(getTransportService()) { + @Override + public void received(ActionInvocation actionInvocation, PositionInfo positionInfo) { + // Don't care if shutdown in the meantime + if(!running) { + return; + } + + lastUpdate.set(System.currentTimeMillis()); + + // Playback was stopped + if(positionInfo.getTrackURI() == null) { + if(downloadService.getCurrentPlaying() != null && downloadService.getPlayerState() != PlayerState.IDLE) { + Log.w(TAG, "Nothing is playing on DLNA device"); + downloadService.setCurrentPlaying(null, false); + } + } + // End device started playing something else, no idea what + else if(!positionInfo.getTrackURI().equals(currentPlayingURI) && downloadService.getPlayerState() != PlayerState.IDLE) { + Log.w(TAG, "A different song is playing on the remote device: " + positionInfo.getTrackURI()); + downloadService.setCurrentPlaying(null, false); + } else { + // Let's get the updated position + currentPosition = (int) positionInfo.getTrackElapsedSeconds(); + } + + downloadService.postDelayed(new Runnable() { + @Override + public void run() { + getUpdatedStatus(); + } + }, STATUS_UPDATE_INTERVAL_SECONDS); + } + + @Override + public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String s) { + Log.w(TAG, "Failed to get an update"); + + downloadService.postDelayed(new Runnable() { + @Override + public void run() { + getUpdatedStatus(); + } + }, STATUS_UPDATE_INTERVAL_SECONDS); + } + }); + } } diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index 6482cc14..a1bf7b52 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -302,6 +302,9 @@ public class DownloadService extends Service { public void post(Runnable r) { handler.post(r); } + public void postDelayed(Runnable r, long millis) { + handler.postDelayed(r, millis); + } public synchronized void download(List songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) { download(songs, save, autoplay, playNext, shuffle, 0, 0); @@ -703,6 +706,7 @@ public class DownloadService extends Service { this.currentPlaying = currentPlaying; if(currentPlaying == null) { currentPlayingIndex = -1; + setPlayerState(IDLE); } else { currentPlayingIndex = downloadList.indexOf(currentPlaying); } -- cgit v1.2.3 From a8c10edd6084d7829a65da171a4ef3862921b682 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 16 Dec 2014 09:18:50 -0800 Subject: Still support GB, add compat layer to not crash on older devices --- AndroidManifest.xml | 2 +- src/github/daneren2005/dsub/util/MediaRouteManager.java | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/AndroidManifest.xml b/AndroidManifest.xml index bbedcea4..86a0eb0e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -30,7 +30,7 @@ - + diff --git a/src/github/daneren2005/dsub/util/MediaRouteManager.java b/src/github/daneren2005/dsub/util/MediaRouteManager.java index 11e0d387..2d0c2a87 100644 --- a/src/github/daneren2005/dsub/util/MediaRouteManager.java +++ b/src/github/daneren2005/dsub/util/MediaRouteManager.java @@ -15,6 +15,7 @@ package github.daneren2005.dsub.util; +import android.os.Build; import android.support.v7.media.MediaRouteProvider; import android.support.v7.media.MediaRouteSelector; import android.support.v7.media.MediaRouter; @@ -147,10 +148,12 @@ public class MediaRouteManager extends MediaRouter.Callback { providers.add(jukeboxProvider); offlineProviders.add(jukeboxProvider); - DLNARouteProvider dlnaProvider = new DLNARouteProvider(downloadService); - router.addProvider(dlnaProvider); - providers.add(dlnaProvider); - offlineProviders.add(dlnaProvider); + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + DLNARouteProvider dlnaProvider = new DLNARouteProvider(downloadService); + router.addProvider(dlnaProvider); + providers.add(dlnaProvider); + offlineProviders.add(dlnaProvider); + } } public void removeOnlineProviders() { for(MediaRouteProvider provider: offlineProviders) { @@ -171,7 +174,9 @@ public class MediaRouteManager extends MediaRouter.Callback { if(castAvailable) { builder.addControlCategory(CastCompat.getCastControlCategory()); } - builder.addControlCategory(DLNARouteProvider.CATEGORY_DLNA); + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + builder.addControlCategory(DLNARouteProvider.CATEGORY_DLNA); + } selector = builder.build(); } } -- cgit v1.2.3 From 1778e7234547c9ab4a5001fd7e70dd5abcaac71c Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 17 Dec 2014 10:59:51 -0800 Subject: If DLNA says duration is 0, not seekable. Fix a resuming to LOCAL issue --- .../dsub/fragments/NowPlayingFragment.java | 6 +++--- .../daneren2005/dsub/service/DLNAController.java | 23 +++++++++++++++++++-- .../daneren2005/dsub/service/DownloadService.java | 24 ++++++++++++++++++---- .../daneren2005/dsub/service/RemoteController.java | 3 +++ 4 files changed, 47 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java index 942b0613..056c6e6f 100644 --- a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java +++ b/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java @@ -1249,18 +1249,18 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis onProgressChangedTask = new SilentBackgroundTask(context) { DownloadService downloadService; - boolean isJukeboxEnabled; int millisPlayed; Integer duration; PlayerState playerState; + boolean isSeekable; @Override protected Void doInBackground() throws Throwable { downloadService = getDownloadService(); - isJukeboxEnabled = downloadService.isRemoteEnabled(); millisPlayed = Math.max(0, downloadService.getPlayerPosition()); duration = downloadService.getPlayerDuration(); playerState = getDownloadService().getPlayerState(); + isSeekable = downloadService.isSeekable(); return null; } @@ -1279,7 +1279,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(!seekInProgress) { progressBar.setProgress(millisPlayed); } - progressBar.setEnabled((currentPlaying.isWorkDone() || isJukeboxEnabled) && playerState != PlayerState.PREPARING); + progressBar.setEnabled(isSeekable); } else { positionTextView.setText("0:00"); durationTextView.setText("-:--"); diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 0a90c2d7..78f737ce 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -79,6 +79,7 @@ public class DLNAController extends RemoteController { int currentPosition = 0; String currentPlayingURI; boolean running = true; + boolean seekable = false; public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) { this.downloadService = downloadService; @@ -174,6 +175,7 @@ public class DLNAController extends RemoteController { controlPoint.execute(new Play(getTransportService()) { @Override public void success(ActionInvocation invocation) { + lastUpdate.set(System.currentTimeMillis()); downloadService.setPlayerState(PlayerState.STARTED); } @@ -190,6 +192,9 @@ public class DLNAController extends RemoteController { controlPoint.execute(new Pause(getTransportService()) { @Override public void success(ActionInvocation invocation) { + int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L); + currentPosition += secondsSinceLastUpdate; + downloadService.setPlayerState(PlayerState.PAUSED); } @@ -273,8 +278,17 @@ public class DLNAController extends RemoteController { @Override public int getRemotePosition() { - int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L); - return currentPosition + secondsSinceLastUpdate; + if(downloadService.getPlayerState() == PlayerState.STARTED) { + int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - lastUpdate.get()) / 1000L); + return currentPosition + secondsSinceLastUpdate; + } else { + return currentPosition; + } + } + + @Override + public boolean isSeekable() { + return seekable; } private void startSong(final DownloadFile currentPlaying, final boolean autoStart, final int position) { @@ -348,6 +362,8 @@ public class DLNAController extends RemoteController { downloadService.setPlayerState(PlayerState.PAUSED); } + currentPosition = position; + lastUpdate.set(System.currentTimeMillis()); getUpdatedStatus(); } @@ -397,6 +413,9 @@ public class DLNAController extends RemoteController { return; } + long duration = positionInfo.getTrackDurationSeconds(); + seekable = duration > 0; + lastUpdate.set(System.currentTimeMillis()); // Playback was stopped diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index f23b0995..6caf5c58 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -62,6 +62,7 @@ import java.util.List; import java.util.Timer; import java.util.TimerTask; +import android.annotation.TargetApi; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -872,7 +873,7 @@ public class DownloadService extends Service { nextMediaPlayer = tmp; setCurrentPlaying(nextPlaying, true); setPlayerState(PlayerState.STARTED); - setupHandlers(currentPlaying, false); + setupHandlers(currentPlaying, false, start); setNextPlaying(); // Proxy should not be being used here since the next player was already setup to play @@ -1047,6 +1048,7 @@ public class DownloadService extends Service { } } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public synchronized void reset() { if (bufferTask != null) { bufferTask.cancel(); @@ -1070,6 +1072,7 @@ public class DownloadService extends Service { } } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public synchronized void resetNext() { try { if (nextMediaPlayer != null) { @@ -1303,6 +1306,16 @@ public class DownloadService extends Service { return mediaRouter.getSelector(); } + public boolean isSeekable() { + if(remoteState == LOCAL) { + return currentPlaying.isWorkDone() && playerState != PREPARING; + } else if(remoteController != null) { + return remoteController.isSeekable(); + } else { + return false; + } + } + public boolean isRemoteEnabled() { return remoteState != LOCAL; } @@ -1510,6 +1523,8 @@ public class DownloadService extends Service { synchronized (DownloadService.this) { if (position != 0) { + Util.sleepQuietly(10); + Log.i(TAG, "Restarting player from position " + position); mediaPlayer.seekTo(position); } @@ -1538,7 +1553,7 @@ public class DownloadService extends Service { } }); - setupHandlers(downloadFile, isPartial); + setupHandlers(downloadFile, isPartial, start); mediaPlayer.prepareAsync(); } catch (Exception x) { @@ -1546,6 +1561,7 @@ public class DownloadService extends Service { } } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private synchronized void setupNext(final DownloadFile downloadFile) { try { final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile(); @@ -1596,7 +1612,7 @@ public class DownloadService extends Service { } } - private void setupHandlers(final DownloadFile downloadFile, final boolean isPartial) { + 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) { @@ -1607,7 +1623,7 @@ public class DownloadService extends Service { playNext(); } else { downloadFile.setPlaying(false); - doPlay(downloadFile, pos, true); + doPlay(downloadFile, pos, isPlaying); downloadFile.setPlaying(true); } return true; diff --git a/src/github/daneren2005/dsub/service/RemoteController.java b/src/github/daneren2005/dsub/service/RemoteController.java index 89d4f4fd..02deaf85 100644 --- a/src/github/daneren2005/dsub/service/RemoteController.java +++ b/src/github/daneren2005/dsub/service/RemoteController.java @@ -48,6 +48,9 @@ public abstract class RemoteController { public abstract void setVolume(int volume); public abstract void updateVolume(boolean up); public abstract double getVolume(); + public boolean isSeekable() { + return true; + } public abstract int getRemotePosition(); public int getRemoteDuration() { -- cgit v1.2.3 From b63ffa1eac6f51bd66e092e4760d492bc58e3623 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 26 Dec 2014 16:46:31 -0800 Subject: Auto proceed to next song on finish --- .../daneren2005/dsub/service/DLNAController.java | 24 ++++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 78f737ce..476f0626 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -39,15 +39,19 @@ import org.fourthline.cling.support.avtransport.callback.SetAVTransportURI; import org.fourthline.cling.support.avtransport.callback.Stop; import org.fourthline.cling.support.avtransport.lastchange.AVTransportLastChangeParser; import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable; +import org.fourthline.cling.support.connectionmanager.callback.PrepareForConnection; import org.fourthline.cling.support.contentdirectory.DIDLParser; import org.fourthline.cling.support.lastchange.LastChange; import org.fourthline.cling.support.model.DIDLContent; +import org.fourthline.cling.support.model.PersonWithRole; import org.fourthline.cling.support.model.PositionInfo; +import org.fourthline.cling.support.model.Res; import org.fourthline.cling.support.model.SeekMode; import org.fourthline.cling.support.model.item.Item; import org.fourthline.cling.support.model.item.MusicTrack; import org.fourthline.cling.support.model.item.VideoItem; import org.fourthline.cling.support.renderingcontrol.callback.SetVolume; +import org.seamless.util.MimeType; import java.text.SimpleDateFormat; import java.util.Date; @@ -138,7 +142,8 @@ public class DLNAController extends RemoteController { if(failed) { failedLoad(); } else { - downloadService.setPlayerState(PlayerState.STOPPED); + downloadService.setPlayerState(PlayerState.COMPLETED); + downloadService.next(); } break; case TRANSITIONING: @@ -418,21 +423,8 @@ public class DLNAController extends RemoteController { lastUpdate.set(System.currentTimeMillis()); - // Playback was stopped - if(positionInfo.getTrackURI() == null) { - if(downloadService.getCurrentPlaying() != null && downloadService.getPlayerState() != PlayerState.IDLE) { - Log.w(TAG, "Nothing is playing on DLNA device"); - downloadService.setCurrentPlaying(null, false); - } - } - // End device started playing something else, no idea what - else if(!positionInfo.getTrackURI().equals(currentPlayingURI) && downloadService.getPlayerState() != PlayerState.IDLE) { - Log.w(TAG, "A different song is playing on the remote device: " + positionInfo.getTrackURI()); - downloadService.setCurrentPlaying(null, false); - } else { - // Let's get the updated position - currentPosition = (int) positionInfo.getTrackElapsedSeconds(); - } + // Let's get the updated position + currentPosition = (int) positionInfo.getTrackElapsedSeconds(); downloadService.postDelayed(new Runnable() { @Override -- cgit v1.2.3 From 47b05d6aecc9ed309b85d300d023052bac743893 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 27 Dec 2014 11:35:56 -0800 Subject: Fix get status running too often, skipping current song and starting on connect even when paused --- src/github/daneren2005/dsub/service/DLNAController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 476f0626..d7c9e6db 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -69,7 +69,7 @@ import github.daneren2005.serverproxy.FileProxy; public class DLNAController extends RemoteController { private static final String TAG = DLNAController.class.getSimpleName(); - private static final long STATUS_UPDATE_INTERVAL_SECONDS = 3L; + private static final long STATUS_UPDATE_INTERVAL_SECONDS = 3000L; DLNADevice device; ControlPoint controlPoint; @@ -141,9 +141,12 @@ public class DLNAController extends RemoteController { if(failed) { failedLoad(); - } else { + } else if(downloadService.getPlayerState() == PlayerState.STARTED) { + // Played until the end downloadService.setPlayerState(PlayerState.COMPLETED); downloadService.next(); + } else { + downloadService.setPlayerState(PlayerState.STOPPED); } break; case TRANSITIONING: -- cgit v1.2.3 From 1b12fed0c03a957ce5ef98fd39a282b210f5100e Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 27 Dec 2014 11:55:12 -0800 Subject: Sleep didn't do any good --- src/github/daneren2005/dsub/service/DownloadService.java | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index 6caf5c58..3f643593 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -1523,8 +1523,6 @@ public class DownloadService extends Service { synchronized (DownloadService.this) { if (position != 0) { - Util.sleepQuietly(10); - Log.i(TAG, "Restarting player from position " + position); mediaPlayer.seekTo(position); } -- cgit v1.2.3 From 2254ad9740d1b9b54144c74cc6265d5f66a180fa Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 27 Dec 2014 12:08:59 -0800 Subject: Fix an issue with resuming local casting skipping current song sometimes --- src/github/daneren2005/dsub/service/DownloadService.java | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index 3f643593..aa575145 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -1344,6 +1344,11 @@ public class DownloadService extends Service { setRemoteState(newState, ref, null); } private void setRemoteState(final RemoteControlState newState, final Object ref, final String routeId) { + // Don't try to do anything if already in the correct state + if(remoteState == newState) { + return; + } + boolean isPlaying = playerState == STARTED; int position = getPlayerPosition(); @@ -1358,6 +1363,7 @@ public class DownloadService extends Service { } } + Log.i(TAG, remoteState.name() + " => " + newState.name() + " (" + currentPlaying + ")"); remoteState = newState; switch(newState) { case JUKEBOX_SERVER: @@ -1484,6 +1490,7 @@ public class DownloadService extends Service { subtractPosition = 0; mediaPlayer.setOnCompletionListener(null); + mediaPlayer.setOnPreparedListener(null); mediaPlayer.reset(); setPlayerState(IDLE); try { -- cgit v1.2.3 From 449dcead72523a173bc0d17d1ed90865711b534c Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 27 Dec 2014 12:11:06 -0800 Subject: Added example metadata from Wireshark from BubbleUPnP --- src/github/daneren2005/dsub/service/DLNAController.java | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index d7c9e6db..2afb0fea 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -351,6 +351,7 @@ public class DLNAController extends RemoteController { String metadata = ""; try { + // 0http://192.168.1.3:57645/external/audio/media/39883.mp3<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/" xmlns:sec="http://www.sec.co.kr/"><item id="/external/audio/albums/484/39883" parentID="/external/audio/albums/484" restricted="1"><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:title>03-Miss Murder.complete</dc:title><dc:creator>AFI</dc:creator><upnp:artist>AFI</upnp:artist><upnp:albumArtURI>http://192.168.1.3:57645/external/audio/albums/484.jpg</upnp:albumArtURI><upnp:genre>Rock</upnp:genre><dc:date>2006-01-01</dc:date><upnp:album>&lt;unknown&gt;</upnp:album><upnp:originalTrackNumber>3</upnp:originalTrackNumber><res protocolInfo="http-get:*:audio/mpeg:*" bitrate="24000" size="4961736" duration="0:03:26.000">http://192.168.1.3:57645/external/audio/media/39883.mp3</res></item></DIDL-Lite> // metadata = parser.generate(didl); } catch(Exception e) { Log.w(TAG, "Metadata generation failed", e); -- cgit v1.2.3 From 39613268ac58647bd7b1fd0cfac1e592ce99c67f Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 27 Dec 2014 12:13:48 -0800 Subject: Fix error when trying to query seekable status with no current song --- src/github/daneren2005/dsub/service/DownloadService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index aa575145..df780df5 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -1308,7 +1308,7 @@ public class DownloadService extends Service { public boolean isSeekable() { if(remoteState == LOCAL) { - return currentPlaying.isWorkDone() && playerState != PREPARING; + return currentPlaying != null && currentPlaying.isWorkDone() && playerState != PREPARING; } else if(remoteController != null) { return remoteController.isSeekable(); } else { -- cgit v1.2.3 From 7ab6051ee7f42c9d20f15c0faf1335b9461c7170 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Thu, 15 Jan 2015 14:07:04 -0800 Subject: Fix crash if we try to reference a null remoteController --- src/github/daneren2005/dsub/service/DownloadService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index df780df5..29fb00b2 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -407,7 +407,7 @@ public class DownloadService extends Service { } private void updateJukeboxPlaylist() { - if (remoteState != LOCAL) { + if (remoteState != LOCAL && remoteController != null) { remoteController.updatePlaylist(); } } -- cgit v1.2.3 From 746dd8e2cf2bcee6cfe4bed23261bf7c2be6020f Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 16 Jan 2015 12:09:23 -0800 Subject: Add try/catch block around device add --- .../dsub/provider/DLNARouteProvider.java | 66 ++++++++++++---------- 1 file changed, 35 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index e2450f1f..34ac182c 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -211,39 +211,43 @@ public class DLNARouteProvider extends MediaRouteProvider { adding.add(id); if(device.getType().getType().equals("MediaRenderer") && device instanceof RemoteDevice) { - dlnaService.getControlPoint().execute(new GetVolume(renderingControl) { - @Override - public void received(ActionInvocation actionInvocation, int currentVolume) { - int maxVolume = 100; - StateVariable volume = renderingControl.getStateVariable("Volume"); - if(volume != null) { - StateVariableAllowedValueRange volumeRange = volume.getTypeDetails().getAllowedValueRange(); - maxVolume = (int) volumeRange.getMaximum(); - } - - // Create a new DLNADevice to represent this item - String id = device.getIdentity().getUdn().toString(); - String name = device.getDetails().getFriendlyName(); - String displayName = device.getDisplayString(); - - DLNADevice newDevice = new DLNADevice(device, id, name, displayName, currentVolume, maxVolume); - devices.put(id, newDevice); - downloadService.post(new Runnable() { - @Override - public void run() { - broadcastDescriptors(); + try { + dlnaService.getControlPoint().execute(new GetVolume(renderingControl) { + @Override + public void received(ActionInvocation actionInvocation, int currentVolume) { + int maxVolume = 100; + StateVariable volume = renderingControl.getStateVariable("Volume"); + if (volume != null) { + StateVariableAllowedValueRange volumeRange = volume.getTypeDetails().getAllowedValueRange(); + maxVolume = (int) volumeRange.getMaximum(); } - }); - adding.remove(id); - } - @Override - public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String s) { - Log.w(TAG, "Failed to get default volume for DLNA route"); - Log.w(TAG, "Reason: " + s); - adding.remove(id); - } - }); + // Create a new DLNADevice to represent this item + String id = device.getIdentity().getUdn().toString(); + String name = device.getDetails().getFriendlyName(); + String displayName = device.getDisplayString(); + + DLNADevice newDevice = new DLNADevice(device, id, name, displayName, currentVolume, maxVolume); + devices.put(id, newDevice); + downloadService.post(new Runnable() { + @Override + public void run() { + broadcastDescriptors(); + } + }); + adding.remove(id); + } + + @Override + public void failure(ActionInvocation actionInvocation, UpnpResponse upnpResponse, String s) { + Log.w(TAG, "Failed to get default volume for DLNA route"); + Log.w(TAG, "Reason: " + s); + adding.remove(id); + } + }); + } catch(Exception e) { + Log.e(TAG, "Failed to add device", e); + } } else { adding.remove(id); } -- cgit v1.2.3 From eb1ad23b49cfd4de477c73279cda7d5c1ad3b1a9 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 16 Jan 2015 12:35:11 -0800 Subject: Fix crash on startup after casting due to a check not to do anything if remoteState hasn't changed --- src/github/daneren2005/dsub/service/DownloadService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index 29fb00b2..37741b92 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -414,10 +414,10 @@ public class DownloadService extends Service { public synchronized void restore(List songs, List toDelete, int currentPlayingIndex, int currentPlayingPosition) { SharedPreferences prefs = Util.getPreferences(this); - remoteState = RemoteControlState.values()[prefs.getInt(Constants.PREFERENCES_KEY_CONTROL_MODE, 0)]; - if(remoteState != LOCAL) { + RemoteControlState newState = RemoteControlState.values()[prefs.getInt(Constants.PREFERENCES_KEY_CONTROL_MODE, 0)]; + if(newState != LOCAL) { String id = prefs.getString(Constants.PREFERENCES_KEY_CONTROL_ID, null); - setRemoteState(remoteState, null, id); + setRemoteState(newState, null, id); } if(prefs.getBoolean(Constants.PREFERENCES_KEY_REMOVE_PLAYED, false)) { removePlayed = true; -- cgit v1.2.3 From 8377aabdd7ccc42ee40fd242448cb0b9b8e21008 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sun, 18 Jan 2015 15:25:21 -0800 Subject: Make sure to still call startRemoteScan when download service hasn't started yet (ie: resuming directly to now playing) --- .../dsub/fragments/NowPlayingFragment.java | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java index b6daea98..4ec439b2 100644 --- a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java +++ b/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java @@ -84,12 +84,9 @@ import github.daneren2005.dsub.activity.SubsonicActivity; public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener { private static final String TAG = NowPlayingFragment.class.getSimpleName(); - - public static final int DIALOG_SAVE_PLAYLIST = 100; private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 10; - private static final int COLOR_BUTTON_ENABLED = Color.rgb(51, 181, 229); - private static final int COLOR_BUTTON_DISABLED = Color.rgb(206, 213, 211); private static final int INCREMENT_TIME = 5000; + private static final int SERVICE_BACKOFF = 200; private static final int ACTION_PREVIOUS = 1; private static final int ACTION_NEXT = 2; @@ -873,6 +870,21 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } if(downloadService != null) { downloadService.startRemoteScan(); + } else { + // Make sure to call remote scan once the service is ready + final Runnable waitForService = new Runnable() { + @Override + public void run() { + DownloadService service = getDownloadService(); + if(service != null) { + service.startRemoteScan(); + } else { + handler.postDelayed(this, SERVICE_BACKOFF); + } + } + }; + + handler.postDelayed(waitForService, SERVICE_BACKOFF); } } @@ -1009,7 +1021,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis lengthBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if(fromUser) { + if (fromUser) { int length = getMinutes(progress); lengthBox.setText(Util.formatDuration(length)); } -- cgit v1.2.3 From bf43b264b32e33bb5de556da4dff521a1f894472 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Mon, 19 Jan 2015 18:54:25 -0800 Subject: Fix issue with DLNA cast not going to IDLE when size == 1 --- src/github/daneren2005/dsub/service/DLNAController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 2afb0fea..1ad48ef2 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -144,7 +144,7 @@ public class DLNAController extends RemoteController { } else if(downloadService.getPlayerState() == PlayerState.STARTED) { // Played until the end downloadService.setPlayerState(PlayerState.COMPLETED); - downloadService.next(); + downloadService.onSongCompleted(); } else { downloadService.setPlayerState(PlayerState.STOPPED); } -- cgit v1.2.3 From e4bfbeab9bf2c41f957d9152eac6f80e18d0073b Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Mon, 19 Jan 2015 19:45:55 -0800 Subject: Get some basic metadata working --- .../daneren2005/dsub/service/DLNAController.java | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 1ad48ef2..fc19b6a1 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -340,7 +340,25 @@ public class DLNAController extends RemoteController { if(song.isVideo()) { track = new VideoItem(song.getId(), song.getParent(), song.getTitle(), song.getArtist()); } else { - MusicTrack musicTrack = new MusicTrack(song.getId(), song.getParent(), song.getTitle(), song.getArtist(), song.getAlbum(), song.getArtist()); + String contentType = song.getTranscodedContentType(); + MimeType mimeType; + // If we can parse the content type, use it instead of hard coding + if(contentType.indexOf("/") != -1 && contentType.indexOf("/") != (contentType.length() - 1)) { + String[] typeParts = contentType.split("/"); + mimeType = new MimeType(typeParts[0], typeParts[1]); + } else { + mimeType = new MimeType("audio", "mpeg"); + } + + Res res = new Res(mimeType, song.getSize(), url); + + if(song.getDuration() != null) { + SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + res.setDuration(df.format(new Date(song.getDuration() * 1000))); + } + + MusicTrack musicTrack = new MusicTrack(song.getId(), song.getParent(), song.getTitle(), song.getArtist(), song.getAlbum(), song.getArtist(), res); musicTrack.setOriginalTrackNumber(song.getTrack()); track = musicTrack; } @@ -352,7 +370,7 @@ public class DLNAController extends RemoteController { String metadata = ""; try { // 0http://192.168.1.3:57645/external/audio/media/39883.mp3<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/" xmlns:sec="http://www.sec.co.kr/"><item id="/external/audio/albums/484/39883" parentID="/external/audio/albums/484" restricted="1"><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:title>03-Miss Murder.complete</dc:title><dc:creator>AFI</dc:creator><upnp:artist>AFI</upnp:artist><upnp:albumArtURI>http://192.168.1.3:57645/external/audio/albums/484.jpg</upnp:albumArtURI><upnp:genre>Rock</upnp:genre><dc:date>2006-01-01</dc:date><upnp:album>&lt;unknown&gt;</upnp:album><upnp:originalTrackNumber>3</upnp:originalTrackNumber><res protocolInfo="http-get:*:audio/mpeg:*" bitrate="24000" size="4961736" duration="0:03:26.000">http://192.168.1.3:57645/external/audio/media/39883.mp3</res></item></DIDL-Lite> - // metadata = parser.generate(didl); + metadata = parser.generate(didl); } catch(Exception e) { Log.w(TAG, "Metadata generation failed", e); } -- cgit v1.2.3 From eadc4a0b703c259c0d2ca61c1c1d8e0cb26cab70 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Mon, 19 Jan 2015 19:52:14 -0800 Subject: Add cover art to metadata --- src/github/daneren2005/dsub/service/DLNAController.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index fc19b6a1..7e4f9ce7 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -43,6 +43,7 @@ import org.fourthline.cling.support.connectionmanager.callback.PrepareForConnect import org.fourthline.cling.support.contentdirectory.DIDLParser; import org.fourthline.cling.support.lastchange.LastChange; import org.fourthline.cling.support.model.DIDLContent; +import org.fourthline.cling.support.model.DIDLObject; import org.fourthline.cling.support.model.PersonWithRole; import org.fourthline.cling.support.model.PositionInfo; import org.fourthline.cling.support.model.Res; @@ -53,6 +54,7 @@ import org.fourthline.cling.support.model.item.VideoItem; import org.fourthline.cling.support.renderingcontrol.callback.SetVolume; import org.seamless.util.MimeType; +import java.net.URI; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; @@ -360,6 +362,14 @@ public class DLNAController extends RemoteController { MusicTrack musicTrack = new MusicTrack(song.getId(), song.getParent(), song.getTitle(), song.getArtist(), song.getAlbum(), song.getArtist(), res); musicTrack.setOriginalTrackNumber(song.getTrack()); + + if(song.getCoverArt() != null) { + String coverArt = musicService.getCoverArtUrl(downloadService, song); + coverArt = Util.replaceInternalUrl(downloadService, coverArt); + DIDLObject.Property.UPNP.ALBUM_ART_URI albumArtUri = new DIDLObject.Property.UPNP.ALBUM_ART_URI(URI.create(coverArt)); + musicTrack.addProperty(albumArtUri); + } + track = musicTrack; } @@ -369,7 +379,6 @@ public class DLNAController extends RemoteController { String metadata = ""; try { - // 0http://192.168.1.3:57645/external/audio/media/39883.mp3<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/" xmlns:sec="http://www.sec.co.kr/"><item id="/external/audio/albums/484/39883" parentID="/external/audio/albums/484" restricted="1"><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:title>03-Miss Murder.complete</dc:title><dc:creator>AFI</dc:creator><upnp:artist>AFI</upnp:artist><upnp:albumArtURI>http://192.168.1.3:57645/external/audio/albums/484.jpg</upnp:albumArtURI><upnp:genre>Rock</upnp:genre><dc:date>2006-01-01</dc:date><upnp:album>&lt;unknown&gt;</upnp:album><upnp:originalTrackNumber>3</upnp:originalTrackNumber><res protocolInfo="http-get:*:audio/mpeg:*" bitrate="24000" size="4961736" duration="0:03:26.000">http://192.168.1.3:57645/external/audio/media/39883.mp3</res></item></DIDL-Lite> metadata = parser.generate(didl); } catch(Exception e) { Log.w(TAG, "Metadata generation failed", e); -- cgit v1.2.3 From 397e6f7689cfee2f19e9ab682bb897e0aa9f9304 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 21 Jan 2015 11:42:21 -0800 Subject: Add support for offline DLNA casting --- .../daneren2005/dsub/service/DLNAController.java | 31 +++++++++++++++++----- .../daneren2005/dsub/util/MediaRouteManager.java | 19 +++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 7e4f9ce7..dc92d6cf 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -54,6 +54,7 @@ import org.fourthline.cling.support.model.item.VideoItem; import org.fourthline.cling.support.renderingcontrol.callback.SetVolume; import org.seamless.util.MimeType; +import java.io.File; import java.net.URI; import java.text.SimpleDateFormat; import java.util.Date; @@ -66,6 +67,7 @@ import github.daneren2005.dsub.domain.DLNADevice; 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.serverproxy.FileProxy; @@ -342,10 +344,16 @@ public class DLNAController extends RemoteController { if(song.isVideo()) { track = new VideoItem(song.getId(), song.getParent(), song.getTitle(), song.getArtist()); } else { - String contentType = song.getTranscodedContentType(); + String contentType = null; + if(song.getTranscodedContentType() != null) { + contentType = song.getTranscodedContentType(); + } else if(song.getContentType() != null) { + contentType = song.getContentType(); + } + MimeType mimeType; // If we can parse the content type, use it instead of hard coding - if(contentType.indexOf("/") != -1 && contentType.indexOf("/") != (contentType.length() - 1)) { + if(contentType != null && contentType.indexOf("/") != -1 && contentType.indexOf("/") != (contentType.length() - 1)) { String[] typeParts = contentType.split("/"); mimeType = new MimeType(typeParts[0], typeParts[1]); } else { @@ -364,10 +372,21 @@ public class DLNAController extends RemoteController { musicTrack.setOriginalTrackNumber(song.getTrack()); if(song.getCoverArt() != null) { - String coverArt = musicService.getCoverArtUrl(downloadService, song); - coverArt = Util.replaceInternalUrl(downloadService, coverArt); - DIDLObject.Property.UPNP.ALBUM_ART_URI albumArtUri = new DIDLObject.Property.UPNP.ALBUM_ART_URI(URI.create(coverArt)); - musicTrack.addProperty(albumArtUri); + String coverArt = null; + if(proxy == null) { + coverArt = musicService.getCoverArtUrl(downloadService, song); + coverArt = Util.replaceInternalUrl(downloadService, coverArt); + } else { + File coverArtFile = FileUtil.getAlbumArtFile(downloadService, song); + if(coverArtFile != null && coverArtFile.exists()) { + coverArt = proxy.getPublicAddress(coverArtFile.getPath()); + } + } + + if(coverArt != null) { + DIDLObject.Property.UPNP.ALBUM_ART_URI albumArtUri = new DIDLObject.Property.UPNP.ALBUM_ART_URI(URI.create(coverArt)); + musicTrack.addProperty(albumArtUri); + } } track = musicTrack; diff --git a/src/github/daneren2005/dsub/util/MediaRouteManager.java b/src/github/daneren2005/dsub/util/MediaRouteManager.java index 2d0c2a87..9aa54c4b 100644 --- a/src/github/daneren2005/dsub/util/MediaRouteManager.java +++ b/src/github/daneren2005/dsub/util/MediaRouteManager.java @@ -47,7 +47,7 @@ public class MediaRouteManager extends MediaRouter.Callback { private MediaRouter router; private MediaRouteSelector selector; private List providers = new ArrayList(); - private List offlineProviders = new ArrayList(); + private List onlineProviders = new ArrayList(); static { try { @@ -146,17 +146,10 @@ public class MediaRouteManager extends MediaRouter.Callback { JukeboxRouteProvider jukeboxProvider = new JukeboxRouteProvider(downloadService); router.addProvider(jukeboxProvider); providers.add(jukeboxProvider); - offlineProviders.add(jukeboxProvider); - - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - DLNARouteProvider dlnaProvider = new DLNARouteProvider(downloadService); - router.addProvider(dlnaProvider); - providers.add(dlnaProvider); - offlineProviders.add(dlnaProvider); - } + onlineProviders.add(jukeboxProvider); } public void removeOnlineProviders() { - for(MediaRouteProvider provider: offlineProviders) { + for(MediaRouteProvider provider: onlineProviders) { router.removeProvider(provider); } } @@ -165,6 +158,12 @@ public class MediaRouteManager extends MediaRouter.Callback { if(!Util.isOffline(downloadService)) { addOnlineProviders(); } + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + DLNARouteProvider dlnaProvider = new DLNARouteProvider(downloadService); + router.addProvider(dlnaProvider); + providers.add(dlnaProvider); + } } public void buildSelector() { MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); -- cgit v1.2.3 From 5c6a4951f623a7b8d01892f2622353b1529ad25c Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 21 Jan 2015 12:09:47 -0800 Subject: Check if DLNA device supports seeking --- .../daneren2005/dsub/service/DLNAController.java | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index dc92d6cf..24cd3030 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -16,9 +16,7 @@ package github.daneren2005.dsub.service; import android.content.SharedPreferences; -import android.os.Handler; import android.os.Looper; -import android.os.Message; import android.util.Log; import org.fourthline.cling.controlpoint.ControlPoint; @@ -27,7 +25,7 @@ import org.fourthline.cling.model.action.ActionInvocation; import org.fourthline.cling.model.gena.CancelReason; import org.fourthline.cling.model.gena.GENASubscription; import org.fourthline.cling.model.message.UpnpResponse; -import org.fourthline.cling.model.meta.Device; +import org.fourthline.cling.model.meta.Action; import org.fourthline.cling.model.meta.Service; import org.fourthline.cling.model.state.StateVariableValue; import org.fourthline.cling.model.types.ServiceType; @@ -39,12 +37,10 @@ import org.fourthline.cling.support.avtransport.callback.SetAVTransportURI; import org.fourthline.cling.support.avtransport.callback.Stop; import org.fourthline.cling.support.avtransport.lastchange.AVTransportLastChangeParser; import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable; -import org.fourthline.cling.support.connectionmanager.callback.PrepareForConnection; import org.fourthline.cling.support.contentdirectory.DIDLParser; import org.fourthline.cling.support.lastchange.LastChange; import org.fourthline.cling.support.model.DIDLContent; import org.fourthline.cling.support.model.DIDLObject; -import org.fourthline.cling.support.model.PersonWithRole; import org.fourthline.cling.support.model.PositionInfo; import org.fourthline.cling.support.model.Res; import org.fourthline.cling.support.model.SeekMode; @@ -78,6 +74,7 @@ public class DLNAController extends RemoteController { DLNADevice device; ControlPoint controlPoint; SubscriptionCallback callback; + boolean supportsSeek = false; private FileProxy proxy; String rootLocation = ""; @@ -87,7 +84,7 @@ public class DLNAController extends RemoteController { int currentPosition = 0; String currentPlayingURI; boolean running = true; - boolean seekable = false; + boolean hasDuration = false; public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) { this.downloadService = downloadService; @@ -110,6 +107,13 @@ public class DLNAController extends RemoteController { @Override protected void established(GENASubscription genaSubscription) { + Action[] actions = genaSubscription.getService().getActions(); + for(Action action: actions) { + if("Seek".equals(action.getName())) { + supportsSeek = true; + } + } + startSong(downloadService.getCurrentPlaying(), playing, seconds); } @@ -300,7 +304,7 @@ public class DLNAController extends RemoteController { @Override public boolean isSeekable() { - return seekable; + return supportsSeek && hasDuration; } private void startSong(final DownloadFile currentPlaying, final boolean autoStart, final int position) { @@ -469,7 +473,7 @@ public class DLNAController extends RemoteController { } long duration = positionInfo.getTrackDurationSeconds(); - seekable = duration > 0; + hasDuration = duration > 0; lastUpdate.set(System.currentTimeMillis()); -- cgit v1.2.3 From 1a1ea416f66dadb146d93b230ab6234003ed5ee8 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 21 Jan 2015 12:27:15 -0800 Subject: Also check to make sure that the server support REL_TIME seeking --- src/github/daneren2005/dsub/service/DLNAController.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 24cd3030..6a288e8a 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -27,6 +27,7 @@ import org.fourthline.cling.model.gena.GENASubscription; import org.fourthline.cling.model.message.UpnpResponse; import org.fourthline.cling.model.meta.Action; import org.fourthline.cling.model.meta.Service; +import org.fourthline.cling.model.meta.StateVariable; import org.fourthline.cling.model.state.StateVariableValue; import org.fourthline.cling.model.types.ServiceType; import org.fourthline.cling.support.avtransport.callback.GetPositionInfo; @@ -107,10 +108,13 @@ public class DLNAController extends RemoteController { @Override protected void established(GENASubscription genaSubscription) { - Action[] actions = genaSubscription.getService().getActions(); - for(Action action: actions) { - if("Seek".equals(action.getName())) { - supportsSeek = true; + Action seekAction = genaSubscription.getService().getAction("Seek"); + if(seekAction != null) { + StateVariable seekMode = genaSubscription.getService().getStateVariable("A_ARG_TYPE_SeekMode"); + for(String allowedValue: seekMode.getTypeDetails().getAllowedValues()) { + if("REL_TIME".equals(allowedValue)) { + supportsSeek = true; + } } } -- cgit v1.2.3 From fb28c08c158a2fe40d1f1b65e031d8142afebfa4 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 21 Jan 2015 13:58:21 -0800 Subject: Stop the proxy when shutting down --- src/github/daneren2005/dsub/service/DLNAController.java | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 6a288e8a..207f6f21 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -239,6 +239,11 @@ public class DLNAController extends RemoteController { callback = null; } + if(proxy != null) { + proxy.stop(); + proxy = null; + } + running = false; } -- cgit v1.2.3 From a05ab3a268382c1be40a61cb289494814ea7ba7c Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 21 Jan 2015 15:17:46 -0800 Subject: When casting, we still want to clear bookmarks and setup podcasts for deletion --- .../daneren2005/dsub/service/ChromeCastController.java | 1 + .../daneren2005/dsub/service/DLNAController.java | 1 + .../daneren2005/dsub/service/DownloadService.java | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/ChromeCastController.java b/src/github/daneren2005/dsub/service/ChromeCastController.java index 6f35ac1d..40f5d73d 100644 --- a/src/github/daneren2005/dsub/service/ChromeCastController.java +++ b/src/github/daneren2005/dsub/service/ChromeCastController.java @@ -450,6 +450,7 @@ public class ChromeCastController extends RemoteController { case MediaStatus.PLAYER_STATE_IDLE: if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) { downloadService.setPlayerState(PlayerState.COMPLETED); + downloadService.postPlayCleanup(); downloadService.onSongCompleted(); } else if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_INTERRUPTED) { if (downloadService.getPlayerState() != PlayerState.PREPARING) { diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index 207f6f21..c1ac4d09 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -156,6 +156,7 @@ public class DLNAController extends RemoteController { } else if(downloadService.getPlayerState() == PlayerState.STARTED) { // Played until the end downloadService.setPlayerState(PlayerState.COMPLETED); + downloadService.postPlayCleanup(); downloadService.onSongCompleted(); } else { downloadService.setPlayerState(PlayerState.STOPPED); diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java index ef69c9d3..473815dc 100644 --- a/src/github/daneren2005/dsub/service/DownloadService.java +++ b/src/github/daneren2005/dsub/service/DownloadService.java @@ -1654,12 +1654,7 @@ public class DownloadService extends Service { Log.i(TAG, "Ending position " + pos + " of " + duration); if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000)) || nextSetup) { playNext(); - - // Finished loading, delete when list is cleared - if (downloadFile.getSong() instanceof PodcastEpisode) { - toDelete.add(downloadFile); - } - clearCurrentBookmark(downloadFile.getSong(), true); + postPlayCleanup(downloadFile); } else { // If file is not completely downloaded, restart the playback from the current position. synchronized (DownloadService.this) { @@ -1941,6 +1936,17 @@ public class DownloadService extends Service { } } } + + public void postPlayCleanup() { + postPlayCleanup(currentPlaying); + } + public void postPlayCleanup(DownloadFile downloadFile) { + // Finished loading, delete when list is cleared + if (downloadFile.getSong() instanceof PodcastEpisode) { + toDelete.add(downloadFile); + } + clearCurrentBookmark(downloadFile.getSong(), true); + } private boolean isPastCutoff() { return isPastCutoff(getPlayerPosition(), getPlayerDuration()); -- cgit v1.2.3 From d65220d2d52ab6d6ca626c30782cc50ce39b82e9 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 24 Jan 2015 16:02:45 -0800 Subject: Remove logs, constantly fills logs with too much crap --- src/github/daneren2005/dsub/provider/DLNARouteProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java index 34ac182c..e7f1afb3 100644 --- a/src/github/daneren2005/dsub/provider/DLNARouteProvider.java +++ b/src/github/daneren2005/dsub/provider/DLNARouteProvider.java @@ -369,15 +369,15 @@ public class DLNARouteProvider extends MediaRouteProvider { } public void warn(Throwable th) { - Log.w(TAG, "", th); + // Log.w(TAG, "", th); } public void warn(String msg, Object... args) { - Log.w(TAG, msg); + // Log.w(TAG, msg); } public void warn(String msg, Throwable th) { - Log.w(TAG, msg, th); + // Log.w(TAG, msg, th); } public boolean isIgnoredEnabled () { -- cgit v1.2.3 From 3f1488fe6f15d114aedc5da35ca0ae413d19045b Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 24 Jan 2015 16:49:43 -0800 Subject: Fix casting online songs in offline mode to DLNA --- src/github/daneren2005/dsub/service/DLNAController.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/github/daneren2005/dsub/service/DLNAController.java b/src/github/daneren2005/dsub/service/DLNAController.java index c1ac4d09..5d87e478 100644 --- a/src/github/daneren2005/dsub/service/DLNAController.java +++ b/src/github/daneren2005/dsub/service/DLNAController.java @@ -331,13 +331,20 @@ public class DLNAController extends RemoteController { // Get url for entry MusicService musicService = MusicServiceFactory.getMusicService(downloadService); String url; + // In offline mode or playing offline song if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) { if(proxy == null) { proxy = new FileProxy(downloadService); proxy.start(); } - url = proxy.getPublicAddress(song.getId()); + // Offline song + if(song.getId().indexOf(rootLocation) != -1) { + url = proxy.getPublicAddress(song.getId()); + } else { + // Playing online song in offline mode + url = proxy.getPublicAddress(currentPlaying.getCompleteFile().getPath()); + } } else { if(proxy != null) { proxy.stop(); -- cgit v1.2.3