From 85456d948366fcf564499c88a0d83291cde23a34 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 14 Mar 2015 21:13:17 -0700 Subject: #428 Start of handoff logic. For now just saves current playing queue --- .../daneren2005/dsub/domain/PlayerQueue.java | 30 ++++++++ .../dsub/provider/DSubWidgetProvider.java | 3 +- .../dsub/service/CachedMusicService.java | 11 +++ .../service/DownloadServiceLifecycleSupport.java | 52 ++++++++++---- .../daneren2005/dsub/service/MusicService.java | 5 ++ .../dsub/service/OfflineMusicService.java | 11 +++ .../daneren2005/dsub/service/RESTMusicService.java | 35 ++++++++++ .../dsub/service/parser/PlayQueueParser.java | 81 ++++++++++++++++++++++ 8 files changed, 215 insertions(+), 13 deletions(-) create mode 100644 src/github/daneren2005/dsub/domain/PlayerQueue.java create mode 100644 src/github/daneren2005/dsub/service/parser/PlayQueueParser.java (limited to 'src') diff --git a/src/github/daneren2005/dsub/domain/PlayerQueue.java b/src/github/daneren2005/dsub/domain/PlayerQueue.java new file mode 100644 index 00000000..32f29725 --- /dev/null +++ b/src/github/daneren2005/dsub/domain/PlayerQueue.java @@ -0,0 +1,30 @@ +/* + 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 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.domain; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class PlayerQueue implements Serializable { + public List songs = new ArrayList(); + public List toDelete = new ArrayList(); + public int currentPlayingIndex; + public int currentPlayingPosition; + public boolean renameCurrent = false; + public Date changed = null; +} diff --git a/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java b/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java index 0bf0fe9d..444b6cff 100644 --- a/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java +++ b/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java @@ -43,6 +43,7 @@ import github.daneren2005.dsub.activity.DownloadActivity; import github.daneren2005.dsub.activity.SubsonicActivity; import github.daneren2005.dsub.activity.SubsonicFragmentActivity; import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.PlayerQueue; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.DownloadServiceLifecycleSupport; import github.daneren2005.dsub.util.Constants; @@ -165,7 +166,7 @@ public class DSubWidgetProvider extends AppWidgetProvider { MusicDirectory.Entry currentPlaying = null; if(service == null) { // Deserialize from playling list to setup - DownloadServiceLifecycleSupport.State state = FileUtil.deserialize(context, DownloadServiceLifecycleSupport.FILENAME_DOWNLOADS_SER, DownloadServiceLifecycleSupport.State.class); + PlayerQueue state = FileUtil.deserialize(context, DownloadServiceLifecycleSupport.FILENAME_DOWNLOADS_SER, PlayerQueue.class); if(state != null && state.currentPlayingIndex != -1) { currentPlaying = state.songs.get(state.currentPlayingIndex); } diff --git a/src/github/daneren2005/dsub/service/CachedMusicService.java b/src/github/daneren2005/dsub/service/CachedMusicService.java index 79399483..9d3444cb 100644 --- a/src/github/daneren2005/dsub/service/CachedMusicService.java +++ b/src/github/daneren2005/dsub/service/CachedMusicService.java @@ -37,6 +37,7 @@ import github.daneren2005.dsub.domain.Bookmark; import github.daneren2005.dsub.domain.ChatMessage; import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.domain.Indexes; +import github.daneren2005.dsub.domain.PlayerQueue; import github.daneren2005.dsub.domain.PodcastEpisode; import github.daneren2005.dsub.domain.RemoteStatus; import github.daneren2005.dsub.domain.Lyrics; @@ -923,6 +924,16 @@ public class CachedMusicService implements MusicService { return musicService.getVideos(refresh, context, progressListener); } + @Override + public void savePlayQueue(List songs, Entry currentPlaying, int position, Context context, ProgressListener progressListener) throws Exception { + musicService.savePlayQueue(songs, currentPlaying, position, context, progressListener); + } + + @Override + public PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception { + return musicService.getPlayQueue(context, progressListener); + } + @Override public int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception{ return musicService.processOfflineSyncs(context, progressListener); diff --git a/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java b/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java index afcea3e5..8af16a76 100644 --- a/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java +++ b/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java @@ -20,6 +20,7 @@ package github.daneren2005.dsub.service; import java.io.Serializable; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.atomic.AtomicBoolean; @@ -37,10 +38,14 @@ import android.telephony.TelephonyManager; import android.util.Log; import android.view.KeyEvent; import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.PlayerQueue; import github.daneren2005.dsub.domain.PlayerState; +import github.daneren2005.dsub.domain.ServerInfo; +import github.daneren2005.dsub.util.BackgroundTask; import github.daneren2005.dsub.util.CacheCleaner; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.FileUtil; +import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.Util; import static github.daneren2005.dsub.domain.PlayerState.PREPARING; @@ -61,6 +66,7 @@ public class DownloadServiceLifecycleSupport { private ReentrantLock lock = new ReentrantLock(); private final AtomicBoolean setup = new AtomicBoolean(false); private long lastPressTime = 0; + private SilentBackgroundTask currentSavePlayQueueTask = null; /** * This receiver manages the intent that could come from other applications. @@ -272,7 +278,7 @@ public class DownloadServiceLifecycleSupport { } public void serializeDownloadQueueNow(List songs) { - State state = new State(); + final PlayerQueue state = new PlayerQueue(); for (DownloadFile downloadFile : songs) { state.songs.add(downloadFile.getSong()); } @@ -286,9 +292,41 @@ public class DownloadServiceLifecycleSupport { if(currentPlaying != null) { state.renameCurrent = currentPlaying.isWorkDone() && !currentPlaying.isCompleteFileAvailable(); } + state.changed = new Date(); Log.i(TAG, "Serialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition); FileUtil.serialize(downloadService, state, FILENAME_DOWNLOADS_SER); + + // If we are on Subsonic 5.2+, save play queue + if(ServerInfo.checkServerVersion(downloadService, "1.12") && !ServerInfo.isMadsonic(downloadService) && !Util.isOffline(downloadService) && state.songs.size() > 0) { + // Cancel any currently running tasks + if(currentSavePlayQueueTask != null) { + currentSavePlayQueueTask.cancel(); + } + + currentSavePlayQueueTask = new SilentBackgroundTask(downloadService) { + @Override + protected Void doInBackground() throws Throwable { + try { + MusicService musicService = MusicServiceFactory.getMusicService(downloadService); + musicService.savePlayQueue(state.songs, state.songs.get(state.currentPlayingIndex), state.currentPlayingPosition, downloadService, null); + currentSavePlayQueueTask = null; + } catch (Exception e) { + Log.e(TAG, "Failed to save playing queue to server", e); + currentSavePlayQueueTask = null; + } + + return null; + } + + @Override + protected void error(Throwable error) { + currentSavePlayQueueTask = null; + super.error(error); + } + }; + currentSavePlayQueueTask.execute(); + } } public void post(Runnable runnable) { @@ -296,7 +334,7 @@ public class DownloadServiceLifecycleSupport { } private void deserializeDownloadQueueNow() { - State state = FileUtil.deserialize(downloadService, FILENAME_DOWNLOADS_SER, State.class); + PlayerQueue state = FileUtil.deserialize(downloadService, FILENAME_DOWNLOADS_SER, PlayerQueue.class); if (state == null) { return; } @@ -399,14 +437,4 @@ public class DownloadServiceLifecycleSupport { }); } } - - public static class State implements Serializable { - private static final long serialVersionUID = -6346438781062572271L; - - public List songs = new ArrayList(); - public List toDelete = new ArrayList(); - public int currentPlayingIndex; - public int currentPlayingPosition; - public boolean renameCurrent = false; - } } diff --git a/src/github/daneren2005/dsub/service/MusicService.java b/src/github/daneren2005/dsub/service/MusicService.java index b4b99911..95b1bde6 100644 --- a/src/github/daneren2005/dsub/service/MusicService.java +++ b/src/github/daneren2005/dsub/service/MusicService.java @@ -30,6 +30,7 @@ import github.daneren2005.dsub.domain.Bookmark; import github.daneren2005.dsub.domain.ChatMessage; import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.domain.Indexes; +import github.daneren2005.dsub.domain.PlayerQueue; import github.daneren2005.dsub.domain.RemoteStatus; import github.daneren2005.dsub.domain.Lyrics; import github.daneren2005.dsub.domain.MusicDirectory; @@ -185,6 +186,10 @@ public interface MusicService { Bitmap getBitmap(String url, int size, Context context, ProgressListener progressListener, SilentBackgroundTask task) throws Exception; MusicDirectory getVideos(boolean refresh, Context context, ProgressListener progressListener) throws Exception; + + void savePlayQueue(List songs, MusicDirectory.Entry currentPlaying, int position, Context context, ProgressListener progressListener) throws Exception; + + PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception; int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception; diff --git a/src/github/daneren2005/dsub/service/OfflineMusicService.java b/src/github/daneren2005/dsub/service/OfflineMusicService.java index c66ba598..8b38d061 100644 --- a/src/github/daneren2005/dsub/service/OfflineMusicService.java +++ b/src/github/daneren2005/dsub/service/OfflineMusicService.java @@ -41,6 +41,7 @@ import github.daneren2005.dsub.domain.ArtistInfo; import github.daneren2005.dsub.domain.ChatMessage; import github.daneren2005.dsub.domain.Genre; import github.daneren2005.dsub.domain.Indexes; +import github.daneren2005.dsub.domain.PlayerQueue; import github.daneren2005.dsub.domain.PodcastEpisode; import github.daneren2005.dsub.domain.RemoteStatus; import github.daneren2005.dsub.domain.Lyrics; @@ -803,6 +804,16 @@ public class OfflineMusicService implements MusicService { throw new OfflineException(ERRORMSG); } + @Override + public void savePlayQueue(List songs, MusicDirectory.Entry currentPlaying, int position, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException(ERRORMSG); + } + + @Override + public PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException(ERRORMSG); + } + @Override public int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception{ throw new OfflineException(ERRORMSG); diff --git a/src/github/daneren2005/dsub/service/RESTMusicService.java b/src/github/daneren2005/dsub/service/RESTMusicService.java index 6caf2ade..de60ae12 100644 --- a/src/github/daneren2005/dsub/service/RESTMusicService.java +++ b/src/github/daneren2005/dsub/service/RESTMusicService.java @@ -81,6 +81,7 @@ import github.daneren2005.dsub.service.parser.LicenseParser; import github.daneren2005.dsub.service.parser.LyricsParser; import github.daneren2005.dsub.service.parser.MusicDirectoryParser; import github.daneren2005.dsub.service.parser.MusicFoldersParser; +import github.daneren2005.dsub.service.parser.PlayQueueParser; import github.daneren2005.dsub.service.parser.PlaylistParser; import github.daneren2005.dsub.service.parser.PlaylistsParser; import github.daneren2005.dsub.service.parser.PodcastChannelParser; @@ -1548,6 +1549,40 @@ public class RESTMusicService implements MusicService { } } + @Override + public void savePlayQueue(List songs, MusicDirectory.Entry currentPlaying, int position, Context context, ProgressListener progressListener) throws Exception { + List parameterNames = new LinkedList(); + List parameterValues = new LinkedList(); + + for(MusicDirectory.Entry song: songs) { + parameterNames.add("id"); + parameterValues.add(song.getId()); + } + + parameterNames.add("current"); + parameterValues.add(currentPlaying.getId()); + + parameterNames.add("position"); + parameterValues.add(position); + + Reader reader = getReader(context, progressListener, "savePlayQueue", null, parameterNames, parameterValues); + try { + new ErrorParser(context, getInstance(context)).parse(reader); + } finally { + Util.close(reader); + } + } + + @Override + public PlayerQueue getPlayQueue(Context context, ProgressListener progressListener) throws Exception { + Reader reader = getReader(context, progressListener, "getPlayQueue", null); + try { + return new PlayQueueParser(context, getInstance(context)).parse(reader, progressListener); + } finally { + Util.close(reader); + } + } + @Override public int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception{ return processOfflineScrobbles(context, progressListener) + processOfflineStars(context, progressListener); diff --git a/src/github/daneren2005/dsub/service/parser/PlayQueueParser.java b/src/github/daneren2005/dsub/service/parser/PlayQueueParser.java new file mode 100644 index 00000000..7dc62105 --- /dev/null +++ b/src/github/daneren2005/dsub/service/parser/PlayQueueParser.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 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.service.parser; + +import android.content.Context; + +import org.xmlpull.v1.XmlPullParser; + +import java.io.Reader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Locale; + +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.PlayerQueue; +import github.daneren2005.dsub.util.ProgressListener; + +public class PlayQueueParser extends MusicDirectoryEntryParser { + public PlayQueueParser(Context context, int instance) { + super(context, instance); + } + + public PlayerQueue parse(Reader reader, ProgressListener progressListener) throws Exception { + init(reader); + + PlayerQueue state = new PlayerQueue(); + String currentId = null; + String changed = null; + int eventType; + do { + eventType = nextParseEvent(); + if (eventType == XmlPullParser.START_TAG) { + String name = getElementName(); + if("playQueue".equals(name)) { + currentId = get("current"); + state.currentPlayingPosition = getInteger("position"); + try { + state.changed = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(get("changed")); + } catch (ParseException e) { + state.changed = null; + } + } else if ("entry".equals(name)) { + MusicDirectory.Entry entry = parseEntry(""); + // Only add songs + if(!entry.isVideo()) { + state.songs.add(entry); + } + } else if ("error".equals(name)) { + handleError(); + } + } + } while (eventType != XmlPullParser.END_DOCUMENT); + + if(currentId != null) { + for (MusicDirectory.Entry entry : state.songs) { + if (entry.getId().equals(currentId)) { + state.currentPlayingIndex = state.songs.indexOf(entry); + } + } + } else { + state.currentPlayingIndex = 0; + state.currentPlayingPosition = 0; + } + + validate(); + return state; + } +} -- cgit v1.2.3