diff options
Diffstat (limited to 'subsonic-android/src/net/sourceforge/subsonic/androidapp/service')
35 files changed, 0 insertions, 5461 deletions
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java deleted file mode 100644 index 14aed954..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/CachedMusicService.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.apache.http.HttpResponse; - -import android.content.Context; -import android.graphics.Bitmap; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.domain.Version; -import net.sourceforge.subsonic.androidapp.util.CancellableTask; -import net.sourceforge.subsonic.androidapp.util.LRUCache; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import net.sourceforge.subsonic.androidapp.util.TimeLimitedCache; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public class CachedMusicService implements MusicService { - - private static final int MUSIC_DIR_CACHE_SIZE = 20; - private static final int TTL_MUSIC_DIR = 5 * 60; // Five minutes - - private final MusicService musicService; - private final LRUCache<String, TimeLimitedCache<MusicDirectory>> cachedMusicDirectories; - private final TimeLimitedCache<Boolean> cachedLicenseValid = new TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS); - private final TimeLimitedCache<Indexes> cachedIndexes = new TimeLimitedCache<Indexes>(60 * 60, TimeUnit.SECONDS); - private final TimeLimitedCache<List<Playlist>> cachedPlaylists = new TimeLimitedCache<List<Playlist>>(60, TimeUnit.SECONDS); - private final TimeLimitedCache<List<MusicFolder>> cachedMusicFolders = new TimeLimitedCache<List<MusicFolder>>(10 * 3600, TimeUnit.SECONDS); - private String restUrl; - - public CachedMusicService(MusicService musicService) { - this.musicService = musicService; - cachedMusicDirectories = new LRUCache<String, TimeLimitedCache<MusicDirectory>>(MUSIC_DIR_CACHE_SIZE); - } - - @Override - public void ping(Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - musicService.ping(context, progressListener); - } - - @Override - public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - Boolean result = cachedLicenseValid.get(); - if (result == null) { - result = musicService.isLicenseValid(context, progressListener); - cachedLicenseValid.set(result, result ? 30L * 60L : 2L * 60L, TimeUnit.SECONDS); - } - return result; - } - - @Override - public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - if (refresh) { - cachedMusicFolders.clear(); - } - List<MusicFolder> result = cachedMusicFolders.get(); - if (result == null) { - result = musicService.getMusicFolders(refresh, context, progressListener); - cachedMusicFolders.set(result); - } - return result; - } - - @Override - public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - if (refresh) { - cachedIndexes.clear(); - cachedMusicFolders.clear(); - cachedMusicDirectories.clear(); - } - Indexes result = cachedIndexes.get(); - if (result == null) { - result = musicService.getIndexes(musicFolderId, refresh, context, progressListener); - cachedIndexes.set(result); - } - return result; - } - - @Override - public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - TimeLimitedCache<MusicDirectory> cache = refresh ? null : cachedMusicDirectories.get(id); - MusicDirectory dir = cache == null ? null : cache.get(); - if (dir == null) { - dir = musicService.getMusicDirectory(id, refresh, context, progressListener); - cache = new TimeLimitedCache<MusicDirectory>(TTL_MUSIC_DIR, TimeUnit.SECONDS); - cache.set(dir); - cachedMusicDirectories.put(id, cache); - } - return dir; - } - - @Override - public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { - return musicService.search(criteria, context, progressListener); - } - - @Override - public MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception { - return musicService.getPlaylist(id, name, context, progressListener); - } - - @Override - public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - checkSettingsChanged(context); - List<Playlist> result = refresh ? null : cachedPlaylists.get(); - if (result == null) { - result = musicService.getPlaylists(refresh, context, progressListener); - cachedPlaylists.set(result); - } - return result; - } - - @Override - public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception { - musicService.createPlaylist(id, name, entries, context, progressListener); - } - - @Override - public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { - return musicService.getLyrics(artist, title, context, progressListener); - } - - @Override - public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { - musicService.scrobble(id, submission, context, progressListener); - } - - @Override - public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { - return musicService.getAlbumList(type, size, offset, context, progressListener); - } - - @Override - public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { - return musicService.getRandomSongs(size, context, progressListener); - } - - @Override - public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { - return musicService.getCoverArt(context, entry, size, saveToFile, progressListener); - } - - @Override - public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception { - return musicService.getDownloadInputStream(context, song, offset, maxBitrate, task); - } - - @Override - public Version getLocalVersion(Context context) throws Exception { - return musicService.getLocalVersion(context); - } - - @Override - public Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception { - return musicService.getLatestVersion(context, progressListener); - } - - @Override - public String getVideoUrl(Context context, String id) { - return musicService.getVideoUrl(context, id); - } - - @Override - public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception { - return musicService.updateJukeboxPlaylist(ids, context, progressListener); - } - - @Override - public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { - return musicService.skipJukebox(index, offsetSeconds, context, progressListener); - } - - @Override - public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { - return musicService.stopJukebox(context, progressListener); - } - - @Override - public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { - return musicService.startJukebox(context, progressListener); - } - - @Override - public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { - return musicService.getJukeboxStatus(context, progressListener); - } - - @Override - public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { - return musicService.setJukeboxGain(gain, context, progressListener); - } - - private void checkSettingsChanged(Context context) { - String newUrl = Util.getRestUrl(context, null); - if (!Util.equals(newUrl, restUrl)) { - cachedMusicFolders.clear(); - cachedMusicDirectories.clear(); - cachedLicenseValid.clear(); - cachedIndexes.clear(); - cachedPlaylists.clear(); - restUrl = newUrl; - } - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadFile.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadFile.java deleted file mode 100644 index 46373afe..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadFile.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import android.content.Context; -import android.os.PowerManager; -import android.util.DisplayMetrics; -import android.util.Log; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.util.CancellableTask; -import net.sourceforge.subsonic.androidapp.util.FileUtil; -import net.sourceforge.subsonic.androidapp.util.Util; -import net.sourceforge.subsonic.androidapp.util.CacheCleaner; - -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; - -/** - * @author Sindre Mehus - * @version $Id$ - */ -public class DownloadFile { - - private static final String TAG = DownloadFile.class.getSimpleName(); - private final Context context; - private final MusicDirectory.Entry song; - private final File partialFile; - private final File completeFile; - private final File saveFile; - - private final MediaStoreService mediaStoreService; - private CancellableTask downloadTask; - private boolean save; - private boolean failed; - private int bitRate; - - public DownloadFile(Context context, MusicDirectory.Entry song, boolean save) { - this.context = context; - this.song = song; - this.save = save; - saveFile = FileUtil.getSongFile(context, song); - bitRate = Util.getMaxBitrate(context); - partialFile = new File(saveFile.getParent(), FileUtil.getBaseName(saveFile.getName()) + - "." + bitRate + ".partial." + FileUtil.getExtension(saveFile.getName())); - completeFile = new File(saveFile.getParent(), FileUtil.getBaseName(saveFile.getName()) + - ".complete." + FileUtil.getExtension(saveFile.getName())); - mediaStoreService = new MediaStoreService(context); - } - - public MusicDirectory.Entry getSong() { - return song; - } - - /** - * Returns the effective bit rate. - */ - public int getBitRate() { - if (bitRate > 0) { - return bitRate; - } - return song.getBitRate() == null ? 160 : song.getBitRate(); - } - - public synchronized void download() { - FileUtil.createDirectoryForParent(saveFile); - failed = false; - downloadTask = new DownloadTask(); - downloadTask.start(); - } - - public synchronized void cancelDownload() { - if (downloadTask != null) { - downloadTask.cancel(); - } - } - - public File getCompleteFile() { - if (saveFile.exists()) { - return saveFile; - } - - if (completeFile.exists()) { - return completeFile; - } - - return saveFile; - } - - public File getPartialFile() { - return partialFile; - } - - public boolean isSaved() { - return saveFile.exists(); - } - - public synchronized boolean isCompleteFileAvailable() { - return saveFile.exists() || completeFile.exists(); - } - - public synchronized boolean isWorkDone() { - return saveFile.exists() || (completeFile.exists() && !save); - } - - public synchronized boolean isDownloading() { - return downloadTask != null && downloadTask.isRunning(); - } - - public synchronized boolean isDownloadCancelled() { - return downloadTask != null && downloadTask.isCancelled(); - } - - public boolean shouldSave() { - return save; - } - - public boolean isFailed() { - return failed; - } - - public void delete() { - cancelDownload(); - Util.delete(partialFile); - Util.delete(completeFile); - Util.delete(saveFile); - mediaStoreService.deleteFromMediaStore(this); - } - - public void unpin() { - if (saveFile.exists()) { - saveFile.renameTo(completeFile); - } - } - - public boolean cleanup() { - boolean ok = true; - if (completeFile.exists() || saveFile.exists()) { - ok = Util.delete(partialFile); - } - if (saveFile.exists()) { - ok &= Util.delete(completeFile); - } - return ok; - } - - // In support of LRU caching. - public void updateModificationDate() { - updateModificationDate(saveFile); - updateModificationDate(partialFile); - updateModificationDate(completeFile); - } - - private void updateModificationDate(File file) { - if (file.exists()) { - boolean ok = file.setLastModified(System.currentTimeMillis()); - if (!ok) { - Log.w(TAG, "Failed to set last-modified date on " + file); - } - } - } - - @Override - public String toString() { - return "DownloadFile (" + song + ")"; - } - - private class DownloadTask extends CancellableTask { - - @Override - public void execute() { - - InputStream in = null; - FileOutputStream out = null; - PowerManager.WakeLock wakeLock = null; - try { - - if (Util.isScreenLitOnDownload(context)) { - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, toString()); - wakeLock.acquire(); - Log.i(TAG, "Acquired wake lock " + wakeLock); - } - - if (saveFile.exists()) { - Log.i(TAG, saveFile + " already exists. Skipping."); - return; - } - if (completeFile.exists()) { - if (save) { - Util.atomicCopy(completeFile, saveFile); - } else { - Log.i(TAG, completeFile + " already exists. Skipping."); - } - return; - } - - MusicService musicService = MusicServiceFactory.getMusicService(context); - - // Attempt partial HTTP GET, appending to the file if it exists. - HttpResponse response = musicService.getDownloadInputStream(context, song, partialFile.length(), bitRate, DownloadTask.this); - in = response.getEntity().getContent(); - boolean partial = response.getStatusLine().getStatusCode() == HttpStatus.SC_PARTIAL_CONTENT; - if (partial) { - Log.i(TAG, "Executed partial HTTP GET, skipping " + partialFile.length() + " bytes"); - } - - out = new FileOutputStream(partialFile, partial); - long n = copy(in, out); - Log.i(TAG, "Downloaded " + n + " bytes to " + partialFile); - out.flush(); - out.close(); - - if (isCancelled()) { - throw new Exception("Download of '" + song + "' was cancelled"); - } - - downloadAndSaveCoverArt(musicService); - - if (save) { - Util.atomicCopy(partialFile, saveFile); - mediaStoreService.saveInMediaStore(DownloadFile.this); - } else { - Util.atomicCopy(partialFile, completeFile); - } - - } catch (Exception x) { - Util.close(out); - Util.delete(completeFile); - Util.delete(saveFile); - if (!isCancelled()) { - failed = true; - Log.w(TAG, "Failed to download '" + song + "'.", x); - } - - } finally { - Util.close(in); - Util.close(out); - if (wakeLock != null) { - wakeLock.release(); - Log.i(TAG, "Released wake lock " + wakeLock); - } - new CacheCleaner(context, DownloadServiceImpl.getInstance()).clean(); - } - } - - @Override - public String toString() { - return "DownloadTask (" + song + ")"; - } - - private void downloadAndSaveCoverArt(MusicService musicService) throws Exception { - try { - if (song.getCoverArt() != null) { - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - int size = Math.min(metrics.widthPixels, metrics.heightPixels); - musicService.getCoverArt(context, song, size, true, null); - } - } catch (Exception x) { - Log.e(TAG, "Failed to get cover art.", x); - } - } - - private long copy(final InputStream in, OutputStream out) throws IOException, InterruptedException { - - // Start a thread that will close the input stream if the task is - // cancelled, thus causing the copy() method to return. - new Thread() { - @Override - public void run() { - while (true) { - Util.sleepQuietly(3000L); - if (isCancelled()) { - Util.close(in); - return; - } - if (!isRunning()) { - return; - } - } - } - }.start(); - - byte[] buffer = new byte[1024 * 16]; - long count = 0; - int n; - long lastLog = System.currentTimeMillis(); - - while (!isCancelled() && (n = in.read(buffer)) != -1) { - out.write(buffer, 0, n); - count += n; - - long now = System.currentTimeMillis(); - if (now - lastLog > 3000L) { // Only every so often. - Log.i(TAG, "Downloaded " + Util.formatBytes(count) + " of " + song); - lastLog = now; - } - } - return count; - } - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadService.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadService.java deleted file mode 100644 index b136bdbc..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadService.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.util.List; - -import net.sourceforge.subsonic.androidapp.audiofx.EqualizerController; -import net.sourceforge.subsonic.androidapp.audiofx.VisualizerController; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.PlayerState; -import net.sourceforge.subsonic.androidapp.domain.RepeatMode; - -/** - * @author Sindre Mehus - * @version $Id$ - */ -public interface DownloadService { - - void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext); - - void setShufflePlayEnabled(boolean enabled); - - boolean isShufflePlayEnabled(); - - void shuffle(); - - RepeatMode getRepeatMode(); - - void setRepeatMode(RepeatMode repeatMode); - - boolean getKeepScreenOn(); - - void setKeepScreenOn(boolean screenOn); - - boolean getShowVisualization(); - - void setShowVisualization(boolean showVisualization); - - void clear(); - - void clearIncomplete(); - - int size(); - - void remove(DownloadFile downloadFile); - - List<DownloadFile> getDownloads(); - - int getCurrentPlayingIndex(); - - DownloadFile getCurrentPlaying(); - - DownloadFile getCurrentDownloading(); - - void play(int index); - - void seekTo(int position); - - void previous(); - - void next(); - - void pause(); - - void start(); - - void reset(); - - PlayerState getPlayerState(); - - int getPlayerPosition(); - - int getPlayerDuration(); - - void delete(List<MusicDirectory.Entry> songs); - - void unpin(List<MusicDirectory.Entry> songs); - - DownloadFile forSong(MusicDirectory.Entry song); - - long getDownloadListUpdateRevision(); - - void setSuggestedPlaylistName(String name); - - String getSuggestedPlaylistName(); - - EqualizerController getEqualizerController(); - - VisualizerController getVisualizerController(); - - boolean isJukeboxEnabled(); - - void setJukeboxEnabled(boolean b); - - void adjustJukeboxVolume(boolean up); -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java deleted file mode 100644 index 2e668fea..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java +++ /dev/null @@ -1,930 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.media.AudioManager; -import android.media.MediaPlayer; -import android.os.Handler; -import android.os.IBinder; -import android.os.PowerManager; -import android.util.Log; -import net.sourceforge.subsonic.androidapp.audiofx.EqualizerController; -import net.sourceforge.subsonic.androidapp.audiofx.VisualizerController; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.PlayerState; -import net.sourceforge.subsonic.androidapp.domain.RepeatMode; -import net.sourceforge.subsonic.androidapp.util.CancellableTask; -import net.sourceforge.subsonic.androidapp.util.LRUCache; -import net.sourceforge.subsonic.androidapp.util.ShufflePlayBuffer; -import net.sourceforge.subsonic.androidapp.util.SimpleServiceBinder; -import net.sourceforge.subsonic.androidapp.util.Util; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import static net.sourceforge.subsonic.androidapp.domain.PlayerState.*; - -/** - * @author Sindre Mehus - * @version $Id$ - */ -public class DownloadServiceImpl extends Service implements DownloadService { - - private static final String TAG = DownloadServiceImpl.class.getSimpleName(); - - public static final String CMD_PLAY = "net.sourceforge.subsonic.androidapp.CMD_PLAY"; - public static final String CMD_TOGGLEPAUSE = "net.sourceforge.subsonic.androidapp.CMD_TOGGLEPAUSE"; - public static final String CMD_PAUSE = "net.sourceforge.subsonic.androidapp.CMD_PAUSE"; - public static final String CMD_STOP = "net.sourceforge.subsonic.androidapp.CMD_STOP"; - public static final String CMD_PREVIOUS = "net.sourceforge.subsonic.androidapp.CMD_PREVIOUS"; - public static final String CMD_NEXT = "net.sourceforge.subsonic.androidapp.CMD_NEXT"; - - private final IBinder binder = new SimpleServiceBinder<DownloadService>(this); - private MediaPlayer mediaPlayer; - private final List<DownloadFile> downloadList = new ArrayList<DownloadFile>(); - private final Handler handler = new Handler(); - private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this); - private final ShufflePlayBuffer shufflePlayBuffer = new ShufflePlayBuffer(this); - - private final LRUCache<MusicDirectory.Entry, DownloadFile> downloadFileCache = new LRUCache<MusicDirectory.Entry, DownloadFile>(100); - private final List<DownloadFile> cleanupCandidates = new ArrayList<DownloadFile>(); - private final Scrobbler scrobbler = new Scrobbler(); - private final JukeboxService jukeboxService = new JukeboxService(this); - private DownloadFile currentPlaying; - private DownloadFile currentDownloading; - private CancellableTask bufferTask; - private PlayerState playerState = IDLE; - private boolean shufflePlay; - private long revision; - private static DownloadService instance; - private String suggestedPlaylistName; - private PowerManager.WakeLock wakeLock; - private boolean keepScreenOn = false; - - private static boolean equalizerAvailable; - private static boolean visualizerAvailable; - private EqualizerController equalizerController; - private VisualizerController visualizerController; - private boolean showVisualization; - private boolean jukeboxEnabled; - - static { - try { - EqualizerController.checkAvailable(); - equalizerAvailable = true; - } catch (Throwable t) { - equalizerAvailable = false; - } - } - static { - try { - VisualizerController.checkAvailable(); - visualizerAvailable = true; - } catch (Throwable t) { - visualizerAvailable = false; - } - } - - @Override - public void onCreate() { - super.onCreate(); - - mediaPlayer = new MediaPlayer(); - mediaPlayer.setWakeMode(this, PowerManager.PARTIAL_WAKE_LOCK); - - mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public boolean onError(MediaPlayer mediaPlayer, int what, int more) { - handleError(new Exception("MediaPlayer error: " + what + " (" + more + ")")); - return false; - } - }); - - if (equalizerAvailable) { - equalizerController = new EqualizerController(this, mediaPlayer); - if (!equalizerController.isAvailable()) { - equalizerController = null; - } else { - equalizerController.loadSettings(); - } - } - if (visualizerAvailable) { - visualizerController = new VisualizerController(this, mediaPlayer); - if (!visualizerController.isAvailable()) { - visualizerController = null; - } - } - - PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); - wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName()); - wakeLock.setReferenceCounted(false); - - instance = this; - lifecycleSupport.onCreate(); - } - - @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); - lifecycleSupport.onStart(intent); - } - - @Override - public void onDestroy() { - super.onDestroy(); - lifecycleSupport.onDestroy(); - mediaPlayer.release(); - shufflePlayBuffer.shutdown(); - if (equalizerController != null) { - equalizerController.release(); - } - if (visualizerController != null) { - visualizerController.release(); - } - - instance = null; - } - - public static DownloadService getInstance() { - return instance; - } - - @Override - public IBinder onBind(Intent intent) { - return binder; - } - - @Override - public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext) { - shufflePlay = false; - int offset = 1; - - if (songs.isEmpty()) { - return; - } - if (playNext) { - if (autoplay && getCurrentPlayingIndex() >= 0) { - offset = 0; - } - for (MusicDirectory.Entry song : songs) { - DownloadFile downloadFile = new DownloadFile(this, song, save); - downloadList.add(getCurrentPlayingIndex() + offset, downloadFile); - offset++; - } - revision++; - } else { - for (MusicDirectory.Entry song : songs) { - DownloadFile downloadFile = new DownloadFile(this, song, save); - downloadList.add(downloadFile); - } - revision++; - } - updateJukeboxPlaylist(); - - if (autoplay) { - play(0); - } else { - if (currentPlaying == null) { - currentPlaying = downloadList.get(0); - } - checkDownloads(); - } - lifecycleSupport.serializeDownloadQueue(); - } - - private void updateJukeboxPlaylist() { - if (jukeboxEnabled) { - jukeboxService.updatePlaylist(); - } - } - - public void restore(List<MusicDirectory.Entry> songs, int currentPlayingIndex, int currentPlayingPosition) { - download(songs, false, false, false); - if (currentPlayingIndex != -1) { - play(currentPlayingIndex, false); - if (currentPlaying.isCompleteFileAvailable()) { - doPlay(currentPlaying, currentPlayingPosition, false); - } - } - } - - @Override - public synchronized void setShufflePlayEnabled(boolean enabled) { - if (shufflePlay == enabled) { - return; - } - - shufflePlay = enabled; - if (shufflePlay) { - clear(); - checkDownloads(); - } - } - - @Override - public synchronized boolean isShufflePlayEnabled() { - return shufflePlay; - } - - @Override - public synchronized void shuffle() { - Collections.shuffle(downloadList); - if (currentPlaying != null) { - downloadList.remove(getCurrentPlayingIndex()); - downloadList.add(0, currentPlaying); - } - revision++; - lifecycleSupport.serializeDownloadQueue(); - updateJukeboxPlaylist(); - } - - @Override - public RepeatMode getRepeatMode() { - return Util.getRepeatMode(this); - } - - @Override - public void setRepeatMode(RepeatMode repeatMode) { - Util.setRepeatMode(this, repeatMode); - } - - @Override - public boolean getKeepScreenOn() { - return keepScreenOn; - } - - @Override - public void setKeepScreenOn(boolean keepScreenOn) { - this.keepScreenOn = keepScreenOn; - } - - @Override - public boolean getShowVisualization() { - return showVisualization; - } - - @Override - public void setShowVisualization(boolean showVisualization) { - this.showVisualization = showVisualization; - } - - @Override - public synchronized DownloadFile forSong(MusicDirectory.Entry song) { - for (DownloadFile downloadFile : downloadList) { - if (downloadFile.getSong().equals(song)) { - return downloadFile; - } - } - - DownloadFile downloadFile = downloadFileCache.get(song); - if (downloadFile == null) { - downloadFile = new DownloadFile(this, song, false); - downloadFileCache.put(song, downloadFile); - } - return downloadFile; - } - - @Override - public synchronized void clear() { - clear(true); - } - - @Override - public synchronized void clearIncomplete() { - reset(); - Iterator<DownloadFile> iterator = downloadList.iterator(); - while (iterator.hasNext()) { - DownloadFile downloadFile = iterator.next(); - if (!downloadFile.isCompleteFileAvailable()) { - iterator.remove(); - } - } - lifecycleSupport.serializeDownloadQueue(); - updateJukeboxPlaylist(); - } - - @Override - public synchronized int size() { - return downloadList.size(); - } - - public synchronized void clear(boolean serialize) { - reset(); - downloadList.clear(); - revision++; - if (currentDownloading != null) { - currentDownloading.cancelDownload(); - currentDownloading = null; - } - setCurrentPlaying(null, false); - - if (serialize) { - lifecycleSupport.serializeDownloadQueue(); - } - updateJukeboxPlaylist(); - } - - @Override - public synchronized void remove(DownloadFile downloadFile) { - if (downloadFile == currentDownloading) { - currentDownloading.cancelDownload(); - currentDownloading = null; - } - if (downloadFile == currentPlaying) { - reset(); - setCurrentPlaying(null, false); - } - downloadList.remove(downloadFile); - revision++; - lifecycleSupport.serializeDownloadQueue(); - updateJukeboxPlaylist(); - } - - @Override - public synchronized void delete(List<MusicDirectory.Entry> songs) { - for (MusicDirectory.Entry song : songs) { - forSong(song).delete(); - } - } - - @Override - public synchronized void unpin(List<MusicDirectory.Entry> songs) { - for (MusicDirectory.Entry song : songs) { - forSong(song).unpin(); - } - } - - synchronized void setCurrentPlaying(int currentPlayingIndex, boolean showNotification) { - try { - setCurrentPlaying(downloadList.get(currentPlayingIndex), showNotification); - } catch (IndexOutOfBoundsException x) { - // Ignored - } - } - - synchronized void setCurrentPlaying(DownloadFile currentPlaying, boolean showNotification) { - this.currentPlaying = currentPlaying; - - if (currentPlaying != null) { - Util.broadcastNewTrackInfo(this, currentPlaying.getSong()); - } else { - Util.broadcastNewTrackInfo(this, null); - } - - if (currentPlaying != null && showNotification) { - Util.showPlayingNotification(this, this, handler, currentPlaying.getSong()); - } else { - Util.hidePlayingNotification(this, this, handler); - } - } - - @Override - public synchronized int getCurrentPlayingIndex() { - return downloadList.indexOf(currentPlaying); - } - - @Override - public DownloadFile getCurrentPlaying() { - return currentPlaying; - } - - @Override - public DownloadFile getCurrentDownloading() { - return currentDownloading; - } - - @Override - public synchronized List<DownloadFile> getDownloads() { - return new ArrayList<DownloadFile>(downloadList); - } - - /** Plays either the current song (resume) or the first/next one in queue. */ - public synchronized void play() - { - int current = getCurrentPlayingIndex(); - if (current == -1) { - play(0); - } else { - play(current); - } - } - - @Override - public synchronized void play(int index) { - play(index, true); - } - - private synchronized void play(int index, boolean start) { - if (index < 0 || index >= size()) { - reset(); - setCurrentPlaying(null, false); - } else { - setCurrentPlaying(index, start); - checkDownloads(); - if (start) { - if (jukeboxEnabled) { - jukeboxService.skip(getCurrentPlayingIndex(), 0); - setPlayerState(STARTED); - } else { - bufferAndPlay(); - } - } - } - } - - /** Plays or resumes the playback, depending on the current player state. */ - public synchronized void togglePlayPause() - { - if (playerState == PAUSED || playerState == COMPLETED) { - start(); - } else if (playerState == STOPPED || playerState == IDLE) { - play(); - } else if (playerState == STARTED) { - pause(); - } - } - - @Override - public synchronized void seekTo(int position) { - try { - if (jukeboxEnabled) { - jukeboxService.skip(getCurrentPlayingIndex(), position / 1000); - } else { - mediaPlayer.seekTo(position); - } - } catch (Exception x) { - handleError(x); - } - } - - @Override - public synchronized void previous() { - int index = getCurrentPlayingIndex(); - if (index == -1) { - return; - } - - // Restart song if played more than five seconds. - if (getPlayerPosition() > 5000 || index == 0) { - play(index); - } else { - play(index - 1); - } - } - - @Override - public synchronized void next() { - int index = getCurrentPlayingIndex(); - if (index != -1) { - play(index + 1); - } - } - - private void onSongCompleted() { - int index = getCurrentPlayingIndex(); - if (index != -1) { - switch (getRepeatMode()) { - case OFF: - play(index + 1); - break; - case ALL: - play((index + 1) % size()); - break; - case SINGLE: - play(index); - break; - default: - break; - } - } - } - - @Override - public synchronized void pause() { - try { - if (playerState == STARTED) { - if (jukeboxEnabled) { - jukeboxService.stop(); - } else { - mediaPlayer.pause(); - } - setPlayerState(PAUSED); - } - } catch (Exception x) { - handleError(x); - } - } - - @Override - public synchronized void start() { - try { - if (jukeboxEnabled) { - jukeboxService.start(); - } else { - mediaPlayer.start(); - } - setPlayerState(STARTED); - } catch (Exception x) { - handleError(x); - } - } - - @Override - public synchronized void reset() { - if (bufferTask != null) { - bufferTask.cancel(); - } - try { - mediaPlayer.reset(); - setPlayerState(IDLE); - } catch (Exception x) { - handleError(x); - } - } - - @Override - public synchronized int getPlayerPosition() { - try { - if (playerState == IDLE || playerState == DOWNLOADING || playerState == PREPARING) { - return 0; - } - if (jukeboxEnabled) { - return jukeboxService.getPositionSeconds() * 1000; - } else { - return mediaPlayer.getCurrentPosition(); - } - } catch (Exception x) { - handleError(x); - return 0; - } - } - - @Override - public synchronized int getPlayerDuration() { - if (currentPlaying != null) { - Integer duration = currentPlaying.getSong().getDuration(); - if (duration != null) { - return duration * 1000; - } - } - if (playerState != IDLE && playerState != DOWNLOADING && playerState != PlayerState.PREPARING) { - try { - return mediaPlayer.getDuration(); - } catch (Exception x) { - handleError(x); - } - } - return 0; - } - - @Override - public PlayerState getPlayerState() { - return playerState; - } - - synchronized void setPlayerState(PlayerState playerState) { - Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")"); - - if (playerState == PAUSED) { - lifecycleSupport.serializeDownloadQueue(); - } - - boolean show = this.playerState == PAUSED && playerState == PlayerState.STARTED; - boolean hide = this.playerState == STARTED && playerState == PlayerState.PAUSED; - Util.broadcastPlaybackStatusChange(this, playerState); - - this.playerState = playerState; - if (show) { - Util.showPlayingNotification(this, this, handler, currentPlaying.getSong()); - } else if (hide) { - Util.hidePlayingNotification(this, this, handler); - } - - if (playerState == STARTED) { - scrobbler.scrobble(this, currentPlaying, false); - } else if (playerState == COMPLETED) { - scrobbler.scrobble(this, currentPlaying, true); - } - } - - @Override - public void setSuggestedPlaylistName(String name) { - this.suggestedPlaylistName = name; - } - - @Override - public String getSuggestedPlaylistName() { - return suggestedPlaylistName; - } - - @Override - public EqualizerController getEqualizerController() { - return equalizerController; - } - - @Override - public VisualizerController getVisualizerController() { - return visualizerController; - } - - @Override - public boolean isJukeboxEnabled() { - return jukeboxEnabled; - } - - @Override - public void setJukeboxEnabled(boolean jukeboxEnabled) { - this.jukeboxEnabled = jukeboxEnabled; - jukeboxService.setEnabled(jukeboxEnabled); - if (jukeboxEnabled) { - reset(); - - // Cancel current download, if necessary. - if (currentDownloading != null) { - currentDownloading.cancelDownload(); - } - } - } - - @Override - public void adjustJukeboxVolume(boolean up) { - jukeboxService.adjustVolume(up); - } - - private synchronized void bufferAndPlay() { - reset(); - - bufferTask = new BufferTask(currentPlaying, 0); - bufferTask.start(); - } - - private synchronized void doPlay(final DownloadFile downloadFile, int position, boolean start) { - try { - final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile(); - downloadFile.updateModificationDate(); - mediaPlayer.setOnCompletionListener(null); - mediaPlayer.reset(); - setPlayerState(IDLE); - mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - mediaPlayer.setDataSource(file.getPath()); - setPlayerState(PREPARING); - mediaPlayer.prepare(); - setPlayerState(PREPARED); - - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mediaPlayer) { - - // Acquire a temporary wakelock, since when we return from - // this callback the MediaPlayer will release its wakelock - // and allow the device to go to sleep. - wakeLock.acquire(60000); - - setPlayerState(COMPLETED); - - // If COMPLETED and not playing partial file, we are *really" finished - // with the song and can move on to the next. - if (!file.equals(downloadFile.getPartialFile())) { - onSongCompleted(); - return; - } - - // If file is not completely downloaded, restart the playback from the current position. - int pos = mediaPlayer.getCurrentPosition(); - synchronized (DownloadServiceImpl.this) { - - // Work-around for apparent bug on certain phones: If close (less than ten seconds) to the end - // of the song, skip to the next rather than restarting it. - Integer duration = downloadFile.getSong().getDuration() == null ? null : downloadFile.getSong().getDuration() * 1000; - if (duration != null) { - if (Math.abs(duration - pos) < 10000) { - Log.i(TAG, "Skipping restart from " + pos + " of " + duration); - onSongCompleted(); - return; - } - } - - Log.i(TAG, "Requesting restart from " + pos + " of " + duration); - reset(); - bufferTask = new BufferTask(downloadFile, pos); - bufferTask.start(); - } - } - }); - - if (position != 0) { - Log.i(TAG, "Restarting player from position " + position); - mediaPlayer.seekTo(position); - } - - if (start) { - mediaPlayer.start(); - setPlayerState(STARTED); - } else { - setPlayerState(PAUSED); - } - lifecycleSupport.serializeDownloadQueue(); - - } catch (Exception x) { - handleError(x); - } - } - - private void handleError(Exception x) { - Log.w(TAG, "Media player error: " + x, x); - mediaPlayer.reset(); - setPlayerState(IDLE); - } - - protected synchronized void checkDownloads() { - - if (!Util.isExternalStoragePresent() || !lifecycleSupport.isExternalStorageAvailable()) { - return; - } - - if (shufflePlay) { - checkShufflePlay(); - } - - if (jukeboxEnabled || !Util.isNetworkConnected(this)) { - return; - } - - if (downloadList.isEmpty()) { - return; - } - - // Need to download current playing? - if (currentPlaying != null && - currentPlaying != currentDownloading && - !currentPlaying.isCompleteFileAvailable()) { - - // Cancel current download, if necessary. - if (currentDownloading != null) { - currentDownloading.cancelDownload(); - } - - currentDownloading = currentPlaying; - currentDownloading.download(); - cleanupCandidates.add(currentDownloading); - } - - // Find a suitable target for download. - else if (currentDownloading == null || currentDownloading.isWorkDone() || currentDownloading.isFailed()) { - - int n = size(); - if (n == 0) { - return; - } - - int preloaded = 0; - - int start = currentPlaying == null ? 0 : getCurrentPlayingIndex(); - int i = start; - do { - DownloadFile downloadFile = downloadList.get(i); - if (!downloadFile.isWorkDone()) { - if (downloadFile.shouldSave() || preloaded < Util.getPreloadCount(this)) { - currentDownloading = downloadFile; - currentDownloading.download(); - cleanupCandidates.add(currentDownloading); - break; - } - } else if (currentPlaying != downloadFile) { - preloaded++; - } - - i = (i + 1) % n; - } while (i != start); - } - - // Delete obsolete .partial and .complete files. - cleanup(); - } - - private synchronized void checkShufflePlay() { - - final int listSize = 20; - boolean wasEmpty = downloadList.isEmpty(); - - long revisionBefore = revision; - - // First, ensure that list is at least 20 songs long. - int size = size(); - if (size < listSize) { - for (MusicDirectory.Entry song : shufflePlayBuffer.get(listSize - size)) { - DownloadFile downloadFile = new DownloadFile(this, song, false); - downloadList.add(downloadFile); - revision++; - } - } - - int currIndex = currentPlaying == null ? 0 : getCurrentPlayingIndex(); - - // Only shift playlist if playing song #5 or later. - if (currIndex > 4) { - int songsToShift = currIndex - 2; - for (MusicDirectory.Entry song : shufflePlayBuffer.get(songsToShift)) { - downloadList.add(new DownloadFile(this, song, false)); - downloadList.get(0).cancelDownload(); - downloadList.remove(0); - revision++; - } - } - - if (revisionBefore != revision) { - updateJukeboxPlaylist(); - } - - if (wasEmpty && !downloadList.isEmpty()) { - play(0); - } - } - - public long getDownloadListUpdateRevision() { - return revision; - } - - private synchronized void cleanup() { - Iterator<DownloadFile> iterator = cleanupCandidates.iterator(); - while (iterator.hasNext()) { - DownloadFile downloadFile = iterator.next(); - if (downloadFile != currentPlaying && downloadFile != currentDownloading) { - if (downloadFile.cleanup()) { - iterator.remove(); - } - } - } - } - - private class BufferTask extends CancellableTask { - - private static final int BUFFER_LENGTH_SECONDS = 5; - - private final DownloadFile downloadFile; - private final int position; - private final long expectedFileSize; - private final File partialFile; - - public BufferTask(DownloadFile downloadFile, int position) { - this.downloadFile = downloadFile; - this.position = position; - partialFile = downloadFile.getPartialFile(); - - // Calculate roughly how many bytes BUFFER_LENGTH_SECONDS corresponds to. - int bitRate = downloadFile.getBitRate(); - long byteCount = Math.max(100000, bitRate * 1024 / 8 * BUFFER_LENGTH_SECONDS); - - // Find out how large the file should grow before resuming playback. - expectedFileSize = partialFile.length() + byteCount; - } - - @Override - public void execute() { - setPlayerState(DOWNLOADING); - - while (!bufferComplete()) { - Util.sleepQuietly(1000L); - if (isCancelled()) { - return; - } - } - doPlay(downloadFile, position, true); - } - - private boolean bufferComplete() { - boolean completeFileAvailable = downloadFile.isCompleteFileAvailable(); - long size = partialFile.length(); - - Log.i(TAG, "Buffering " + partialFile + " (" + size + "/" + expectedFileSize + ", " + completeFileAvailable + ")"); - return completeFileAvailable || size >= expectedFileSize; - } - - @Override - public String toString() { - return "BufferTask (" + downloadFile + ")"; - } - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceLifecycleSupport.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceLifecycleSupport.java deleted file mode 100644 index f6076059..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceLifecycleSupport.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.view.KeyEvent; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.PlayerState; -import net.sourceforge.subsonic.androidapp.util.CacheCleaner; -import net.sourceforge.subsonic.androidapp.util.FileUtil; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public class DownloadServiceLifecycleSupport { - - private static final String TAG = DownloadServiceLifecycleSupport.class.getSimpleName(); - private static final String FILENAME_DOWNLOADS_SER = "downloadstate.ser"; - - private final DownloadServiceImpl downloadService; - private ScheduledExecutorService executorService; - private BroadcastReceiver headsetEventReceiver; - private BroadcastReceiver ejectEventReceiver; - private PhoneStateListener phoneStateListener; - private boolean externalStorageAvailable= true; - - /** - * This receiver manages the intent that could come from other applications. - */ - private BroadcastReceiver intentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - Log.i(TAG, "intentReceiver.onReceive: " + action); - if (DownloadServiceImpl.CMD_PLAY.equals(action)) { - downloadService.play(); - } else if (DownloadServiceImpl.CMD_NEXT.equals(action)) { - downloadService.next(); - } else if (DownloadServiceImpl.CMD_PREVIOUS.equals(action)) { - downloadService.previous(); - } else if (DownloadServiceImpl.CMD_TOGGLEPAUSE.equals(action)) { - downloadService.togglePlayPause(); - } else if (DownloadServiceImpl.CMD_PAUSE.equals(action)) { - downloadService.pause(); - } else if (DownloadServiceImpl.CMD_STOP.equals(action)) { - downloadService.pause(); - downloadService.seekTo(0); - } - } - }; - - - public DownloadServiceLifecycleSupport(DownloadServiceImpl downloadService) { - this.downloadService = downloadService; - } - - public void onCreate() { - Runnable downloadChecker = new Runnable() { - @Override - public void run() { - try { - downloadService.checkDownloads(); - } catch (Throwable x) { - Log.e(TAG, "checkDownloads() failed.", x); - } - } - }; - - executorService = Executors.newScheduledThreadPool(2); - executorService.scheduleWithFixedDelay(downloadChecker, 5, 5, TimeUnit.SECONDS); - - // Pause when headset is unplugged. - headsetEventReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Log.i(TAG, "Headset event for: " + intent.getExtras().get("name")); - if (intent.getExtras().getInt("state") == 0) { - downloadService.pause(); - } - } - }; - downloadService.registerReceiver(headsetEventReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); - - // Stop when SD card is ejected. - ejectEventReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - externalStorageAvailable = Intent.ACTION_MEDIA_MOUNTED.equals(intent.getAction()); - if (!externalStorageAvailable) { - Log.i(TAG, "External media is ejecting. Stopping playback."); - downloadService.reset(); - } else { - Log.i(TAG, "External media is available."); - } - } - }; - IntentFilter ejectFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT); - ejectFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); - ejectFilter.addDataScheme("file"); - downloadService.registerReceiver(ejectEventReceiver, ejectFilter); - - // React to media buttons. - Util.registerMediaButtonEventReceiver(downloadService); - - // Pause temporarily on incoming phone calls. - phoneStateListener = new MyPhoneStateListener(); - TelephonyManager telephonyManager = (TelephonyManager) downloadService.getSystemService(Context.TELEPHONY_SERVICE); - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); - - // Register the handler for outside intents. - IntentFilter commandFilter = new IntentFilter(); - commandFilter.addAction(DownloadServiceImpl.CMD_PLAY); - commandFilter.addAction(DownloadServiceImpl.CMD_TOGGLEPAUSE); - commandFilter.addAction(DownloadServiceImpl.CMD_PAUSE); - commandFilter.addAction(DownloadServiceImpl.CMD_STOP); - commandFilter.addAction(DownloadServiceImpl.CMD_PREVIOUS); - commandFilter.addAction(DownloadServiceImpl.CMD_NEXT); - downloadService.registerReceiver(intentReceiver, commandFilter); - - deserializeDownloadQueue(); - - new CacheCleaner(downloadService, downloadService).clean(); - } - - public void onStart(Intent intent) { - if (intent != null && intent.getExtras() != null) { - KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT); - if (event != null) { - handleKeyEvent(event); - } - } - } - - public void onDestroy() { - executorService.shutdown(); - serializeDownloadQueue(); - downloadService.clear(false); - downloadService.unregisterReceiver(ejectEventReceiver); - downloadService.unregisterReceiver(headsetEventReceiver); - downloadService.unregisterReceiver(intentReceiver); - - TelephonyManager telephonyManager = (TelephonyManager) downloadService.getSystemService(Context.TELEPHONY_SERVICE); - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE); - } - - public boolean isExternalStorageAvailable() { - return externalStorageAvailable; - } - - public void serializeDownloadQueue() { - State state = new State(); - for (DownloadFile downloadFile : downloadService.getDownloads()) { - state.songs.add(downloadFile.getSong()); - } - state.currentPlayingIndex = downloadService.getCurrentPlayingIndex(); - state.currentPlayingPosition = downloadService.getPlayerPosition(); - - Log.i(TAG, "Serialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition); - FileUtil.serialize(downloadService, state, FILENAME_DOWNLOADS_SER); - } - - private void deserializeDownloadQueue() { - State state = FileUtil.deserialize(downloadService, FILENAME_DOWNLOADS_SER); - if (state == null) { - return; - } - Log.i(TAG, "Deserialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition); - downloadService.restore(state.songs, state.currentPlayingIndex, state.currentPlayingPosition); - - // Work-around: Serialize again, as the restore() method creates a serialization without current playing info. - serializeDownloadQueue(); - } - - private void handleKeyEvent(KeyEvent event) { - if (event.getAction() != KeyEvent.ACTION_DOWN || event.getRepeatCount() > 0) { - return; - } - - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_HEADSETHOOK: - downloadService.togglePlayPause(); - break; - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - downloadService.previous(); - break; - case KeyEvent.KEYCODE_MEDIA_NEXT: - if (downloadService.getCurrentPlayingIndex() < downloadService.size() - 1) { - downloadService.next(); - } - break; - case KeyEvent.KEYCODE_MEDIA_STOP: - downloadService.reset(); - break; - case KeyEvent.KEYCODE_MEDIA_PLAY: - downloadService.start(); - break; - case KeyEvent.KEYCODE_MEDIA_PAUSE: - downloadService.pause(); - default: - break; - } - } - - /** - * Logic taken from packages/apps/Music. Will pause when an incoming - * call rings or if a call (incoming or outgoing) is connected. - */ - private class MyPhoneStateListener extends PhoneStateListener { - private boolean resumeAfterCall; - - @Override - public void onCallStateChanged(int state, String incomingNumber) { - switch (state) { - case TelephonyManager.CALL_STATE_RINGING: - case TelephonyManager.CALL_STATE_OFFHOOK: - if (downloadService.getPlayerState() == PlayerState.STARTED) { - resumeAfterCall = true; - downloadService.pause(); - } - break; - case TelephonyManager.CALL_STATE_IDLE: - if (resumeAfterCall) { - resumeAfterCall = false; - downloadService.start(); - } - break; - default: - break; - } - } - } - - private static class State implements Serializable { - private static final long serialVersionUID = -6346438781062572270L; - - private List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(); - private int currentPlayingIndex; - private int currentPlayingPosition; - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/JukeboxService.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/JukeboxService.java deleted file mode 100644 index e3145f4e..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/JukeboxService.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import android.content.Context; -import android.os.Handler; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.Toast; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.PlayerState; -import net.sourceforge.subsonic.androidapp.service.parser.SubsonicRESTException; -import net.sourceforge.subsonic.androidapp.util.Util; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Provides an asynchronous interface to the remote jukebox on the Subsonic server. - * - * @author Sindre Mehus - * @version $Id$ - */ -public class JukeboxService { - - private static final String TAG = JukeboxService.class.getSimpleName(); - private static final long STATUS_UPDATE_INTERVAL_SECONDS = 5L; - - private final Handler handler = new Handler(); - private final TaskQueue tasks = new TaskQueue(); - private final DownloadServiceImpl downloadService; - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); - private ScheduledFuture<?> statusUpdateFuture; - private final AtomicLong timeOfLastUpdate = new AtomicLong(); - private JukeboxStatus jukeboxStatus; - private float gain = 0.5f; - private VolumeToast volumeToast; - - // TODO: Report warning if queue fills up. - // TODO: Create shutdown method? - // TODO: Disable repeat. - // TODO: Persist RC state? - // TODO: Minimize status updates. - - public JukeboxService(DownloadServiceImpl downloadService) { - this.downloadService = downloadService; - new Thread() { - @Override - public void run() { - processTasks(); - } - }.start(); - } - - private synchronized void startStatusUpdate() { - stopStatusUpdate(); - Runnable updateTask = new Runnable() { - @Override - public void run() { - tasks.remove(GetStatus.class); - tasks.add(new GetStatus()); - } - }; - statusUpdateFuture = executorService.scheduleWithFixedDelay(updateTask, STATUS_UPDATE_INTERVAL_SECONDS, - STATUS_UPDATE_INTERVAL_SECONDS, TimeUnit.SECONDS); - } - - private synchronized void stopStatusUpdate() { - if (statusUpdateFuture != null) { - statusUpdateFuture.cancel(false); - statusUpdateFuture = null; - } - } - - private void processTasks() { - while (true) { - JukeboxTask task = null; - try { - task = tasks.take(); - JukeboxStatus status = task.execute(); - onStatusUpdate(status); - } catch (Throwable x) { - onError(task, x); - } - } - } - - private void onStatusUpdate(JukeboxStatus jukeboxStatus) { - timeOfLastUpdate.set(System.currentTimeMillis()); - this.jukeboxStatus = jukeboxStatus; - - // Track change? - Integer index = jukeboxStatus.getCurrentPlayingIndex(); - if (index != null && index != -1 && index != downloadService.getCurrentPlayingIndex()) { - downloadService.setCurrentPlaying(index, true); - } - } - - private void onError(JukeboxTask task, Throwable x) { - if (x instanceof ServerTooOldException && !(task instanceof Stop)) { - disableJukeboxOnError(x, R.string.download_jukebox_server_too_old); - } else if (x instanceof OfflineException && !(task instanceof Stop)) { - disableJukeboxOnError(x, R.string.download_jukebox_offline); - } else if (x instanceof SubsonicRESTException && ((SubsonicRESTException) x).getCode() == 50 && !(task instanceof Stop)) { - disableJukeboxOnError(x, R.string.download_jukebox_not_authorized); - } else { - Log.e(TAG, "Failed to process jukebox task: " + x, x); - } - } - - private void disableJukeboxOnError(Throwable x, final int resourceId) { - Log.w(TAG, x.toString()); - handler.post(new Runnable() { - @Override - public void run() { - Util.toast(downloadService, resourceId, false); - } - }); - downloadService.setJukeboxEnabled(false); - } - - public void updatePlaylist() { - tasks.remove(Skip.class); - tasks.remove(Stop.class); - tasks.remove(Start.class); - - List<String> ids = new ArrayList<String>(); - for (DownloadFile file : downloadService.getDownloads()) { - ids.add(file.getSong().getId()); - } - tasks.add(new SetPlaylist(ids)); - } - - public void skip(final int index, final int offsetSeconds) { - tasks.remove(Skip.class); - tasks.remove(Stop.class); - tasks.remove(Start.class); - - startStatusUpdate(); - if (jukeboxStatus != null) { - jukeboxStatus.setPositionSeconds(offsetSeconds); - } - tasks.add(new Skip(index, offsetSeconds)); - downloadService.setPlayerState(PlayerState.STARTED); - } - - public void stop() { - tasks.remove(Stop.class); - tasks.remove(Start.class); - - stopStatusUpdate(); - tasks.add(new Stop()); - } - - public void start() { - tasks.remove(Stop.class); - tasks.remove(Start.class); - - startStatusUpdate(); - tasks.add(new Start()); - } - - public synchronized void adjustVolume(boolean up) { - float delta = up ? 0.1f : -0.1f; - gain += delta; - gain = Math.max(gain, 0.0f); - gain = Math.min(gain, 1.0f); - - tasks.remove(SetGain.class); - tasks.add(new SetGain(gain)); - - if (volumeToast == null) { - volumeToast = new VolumeToast(downloadService); - } - volumeToast.setVolume(gain); - } - - private MusicService getMusicService() { - return MusicServiceFactory.getMusicService(downloadService); - } - - public int getPositionSeconds() { - if (jukeboxStatus == null || jukeboxStatus.getPositionSeconds() == null || timeOfLastUpdate.get() == 0) { - return 0; - } - - if (jukeboxStatus.isPlaying()) { - int secondsSinceLastUpdate = (int) ((System.currentTimeMillis() - timeOfLastUpdate.get()) / 1000L); - return jukeboxStatus.getPositionSeconds() + secondsSinceLastUpdate; - } - - return jukeboxStatus.getPositionSeconds(); - } - - public void setEnabled(boolean enabled) { - tasks.clear(); - if (enabled) { - updatePlaylist(); - } - stop(); - downloadService.setPlayerState(PlayerState.IDLE); - } - - private static class TaskQueue { - - private final LinkedBlockingQueue<JukeboxTask> queue = new LinkedBlockingQueue<JukeboxTask>(); - - void add(JukeboxTask jukeboxTask) { - queue.add(jukeboxTask); - } - - JukeboxTask take() throws InterruptedException { - return queue.take(); - } - - void remove(Class<? extends JukeboxTask> clazz) { - try { - Iterator<JukeboxTask> iterator = queue.iterator(); - while (iterator.hasNext()) { - JukeboxTask task = iterator.next(); - if (clazz.equals(task.getClass())) { - iterator.remove(); - } - } - } catch (Throwable x) { - Log.w(TAG, "Failed to clean-up task queue.", x); - } - } - - void clear() { - queue.clear(); - } - } - - private abstract class JukeboxTask { - - abstract JukeboxStatus execute() throws Exception; - - @Override - public String toString() { - return getClass().getSimpleName(); - } - } - - private class GetStatus extends JukeboxTask { - @Override - JukeboxStatus execute() throws Exception { - return getMusicService().getJukeboxStatus(downloadService, null); - } - } - - private class SetPlaylist extends JukeboxTask { - - private final List<String> ids; - - SetPlaylist(List<String> ids) { - this.ids = ids; - } - - @Override - JukeboxStatus execute() throws Exception { - return getMusicService().updateJukeboxPlaylist(ids, downloadService, null); - } - } - - private class Skip extends JukeboxTask { - private final int index; - private final int offsetSeconds; - - Skip(int index, int offsetSeconds) { - this.index = index; - this.offsetSeconds = offsetSeconds; - } - - @Override - JukeboxStatus execute() throws Exception { - return getMusicService().skipJukebox(index, offsetSeconds, downloadService, null); - } - } - - private class Stop extends JukeboxTask { - @Override - JukeboxStatus execute() throws Exception { - return getMusicService().stopJukebox(downloadService, null); - } - } - - private class Start extends JukeboxTask { - @Override - JukeboxStatus execute() throws Exception { - return getMusicService().startJukebox(downloadService, null); - } - } - - private class SetGain extends JukeboxTask { - - private final float gain; - - private SetGain(float gain) { - this.gain = gain; - } - - @Override - JukeboxStatus execute() throws Exception { - return getMusicService().setJukeboxGain(gain, downloadService, null); - } - } - - private static class VolumeToast extends Toast { - - private final ProgressBar progressBar; - - public VolumeToast(Context context) { - super(context); - setDuration(Toast.LENGTH_SHORT); - LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.jukebox_volume, null); - progressBar = (ProgressBar) view.findViewById(R.id.jukebox_volume_progress_bar); - - setView(view); - setGravity(Gravity.TOP, 0, 0); - } - - public void setVolume(float volume) { - progressBar.setProgress(Math.round(100 * volume)); - show(); - } - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MediaStoreService.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MediaStoreService.java deleted file mode 100644 index 775fa3f5..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MediaStoreService.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.io.File; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore; -import android.util.Log; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.util.FileUtil; - -/** - * @author Sindre Mehus - */ -public class MediaStoreService { - - private static final String TAG = MediaStoreService.class.getSimpleName(); - private static final Uri ALBUM_ART_URI = Uri.parse("content://media/external/audio/albumart"); - - private final Context context; - - public MediaStoreService(Context context) { - this.context = context; - } - - public void saveInMediaStore(DownloadFile downloadFile) { - MusicDirectory.Entry song = downloadFile.getSong(); - File songFile = downloadFile.getCompleteFile(); - - // Delete existing row in case the song has been downloaded before. - deleteFromMediaStore(downloadFile); - - ContentResolver contentResolver = context.getContentResolver(); - ContentValues values = new ContentValues(); - values.put(MediaStore.MediaColumns.TITLE, song.getTitle()); - values.put(MediaStore.Audio.AudioColumns.ARTIST, song.getArtist()); - values.put(MediaStore.Audio.AudioColumns.ALBUM, song.getAlbum()); - values.put(MediaStore.Audio.AudioColumns.TRACK, song.getTrack()); - values.put(MediaStore.Audio.AudioColumns.YEAR, song.getYear()); - values.put(MediaStore.MediaColumns.DATA, songFile.getAbsolutePath()); - values.put(MediaStore.MediaColumns.MIME_TYPE, song.getContentType()); - values.put(MediaStore.Audio.AudioColumns.IS_MUSIC, 1); - - Uri uri = contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values); - - // Look up album, and add cover art if found. - Cursor cursor = contentResolver.query(uri, new String[]{MediaStore.Audio.AudioColumns.ALBUM_ID}, null, null, null); - if (cursor.moveToFirst()) { - int albumId = cursor.getInt(0); - insertAlbumArt(albumId, downloadFile); - } - cursor.close(); - } - - public void deleteFromMediaStore(DownloadFile downloadFile) { - ContentResolver contentResolver = context.getContentResolver(); - MusicDirectory.Entry song = downloadFile.getSong(); - File file = downloadFile.getCompleteFile(); - - int n = contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - MediaStore.Audio.AudioColumns.TITLE_KEY + "=? AND " + - MediaStore.MediaColumns.DATA + "=?", - new String[]{MediaStore.Audio.keyFor(song.getTitle()), file.getAbsolutePath()}); - if (n > 0) { - Log.i(TAG, "Deleting media store row for " + song); - } - } - - private void insertAlbumArt(int albumId, DownloadFile downloadFile) { - ContentResolver contentResolver = context.getContentResolver(); - - Cursor cursor = contentResolver.query(Uri.withAppendedPath(ALBUM_ART_URI, String.valueOf(albumId)), null, null, null, null); - if (!cursor.moveToFirst()) { - - // No album art found, add it. - File albumArtFile = FileUtil.getAlbumArtFile(context, downloadFile.getSong()); - if (albumArtFile.exists()) { - ContentValues values = new ContentValues(); - values.put(MediaStore.Audio.AlbumColumns.ALBUM_ID, albumId); - values.put(MediaStore.MediaColumns.DATA, albumArtFile.getPath()); - contentResolver.insert(ALBUM_ART_URI, values); - Log.i(TAG, "Added album art: " + albumArtFile); - } - } - cursor.close(); - } - -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MusicService.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MusicService.java deleted file mode 100644 index cb0c5709..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MusicService.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.util.List; - -import org.apache.http.HttpResponse; - -import android.content.Context; -import android.graphics.Bitmap; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.domain.Version; -import net.sourceforge.subsonic.androidapp.util.CancellableTask; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; - -/** - * @author Sindre Mehus - */ -public interface MusicService { - - void ping(Context context, ProgressListener progressListener) throws Exception; - - boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception; - - List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception; - - Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception; - - SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception; - - List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; - - void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception; - - Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception; - - void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception; - - MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception; - - Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception; - - HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception; - - Version getLocalVersion(Context context) throws Exception; - - Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception; - - String getVideoUrl(Context context, String id); - - JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception; - - JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception; - - JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception; - - JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception; - - JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception; - - JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception; -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MusicServiceFactory.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MusicServiceFactory.java deleted file mode 100644 index 552d1d32..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/MusicServiceFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - * @version $Id$ - */ -public class MusicServiceFactory { - - private static final MusicService REST_MUSIC_SERVICE = new CachedMusicService(new RESTMusicService()); - private static final MusicService OFFLINE_MUSIC_SERVICE = new OfflineMusicService(); - - public static MusicService getMusicService(Context context) { - return Util.isOffline(context) ? OFFLINE_MUSIC_SERVICE : REST_MUSIC_SERVICE; - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/OfflineException.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/OfflineException.java deleted file mode 100644 index 49c000bf..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/OfflineException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -/** - * Thrown by service methods that are not available in offline mode. - * - * @author Sindre Mehus - * @version $Id$ - */ -public class OfflineException extends Exception { - - public OfflineException(String message) { - super(message); - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java deleted file mode 100644 index 79fee6d2..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/OfflineMusicService.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.io.Reader; -import java.io.FileReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Random; -import java.util.Set; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import net.sourceforge.subsonic.androidapp.domain.Artist; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.service.parser.PlaylistParser; -import net.sourceforge.subsonic.androidapp.util.Constants; -import net.sourceforge.subsonic.androidapp.util.FileUtil; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public class OfflineMusicService extends RESTMusicService { - - @Override - public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { - return true; - } - - @Override - public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - List<Artist> artists = new ArrayList<Artist>(); - File root = FileUtil.getMusicDirectory(context); - for (File file : FileUtil.listFiles(root)) { - if (file.isDirectory()) { - Artist artist = new Artist(); - artist.setId(file.getPath()); - artist.setIndex(file.getName().substring(0, 1)); - artist.setName(file.getName()); - artists.add(artist); - } - } - return new Indexes(0L, Collections.<Artist>emptyList(), artists); - } - - @Override - public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - File dir = new File(id); - MusicDirectory result = new MusicDirectory(); - result.setName(dir.getName()); - - Set<String> names = new HashSet<String>(); - - for (File file : FileUtil.listMusicFiles(dir)) { - String name = getName(file); - if (name != null & !names.contains(name)) { - names.add(name); - result.addChild(createEntry(context, file, name)); - } - } - return result; - } - - private String getName(File file) { - String name = file.getName(); - if (file.isDirectory()) { - return name; - } - - if (name.endsWith(".partial") || name.contains(".partial.") || name.equals(Constants.ALBUM_ART_FILE)) { - return null; - } - - name = name.replace(".complete", ""); - return FileUtil.getBaseName(name); - } - - private MusicDirectory.Entry createEntry(Context context, File file, String name) { - MusicDirectory.Entry entry = new MusicDirectory.Entry(); - entry.setDirectory(file.isDirectory()); - entry.setId(file.getPath()); - entry.setParent(file.getParent()); - entry.setSize(file.length()); - String root = FileUtil.getMusicDirectory(context).getPath(); - entry.setPath(file.getPath().replaceFirst("^" + root + "/" , "")); - if (file.isFile()) { - entry.setArtist(file.getParentFile().getParentFile().getName()); - entry.setAlbum(file.getParentFile().getName()); - } - entry.setTitle(name); - entry.setSuffix(FileUtil.getExtension(file.getName().replace(".complete", ""))); - - File albumArt = FileUtil.getAlbumArtFile(context, entry); - if (albumArt.exists()) { - entry.setCoverArt(albumArt.getPath()); - } - return entry; - } - - @Override - public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { - InputStream in = new FileInputStream(entry.getCoverArt()); - try { - byte[] bytes = Util.toByteArray(in); - Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - return Bitmap.createScaledBitmap(bitmap, size, size, true); - } finally { - Util.close(in); - } - } - - @Override - public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Music folders not available in offline mode"); - } - - @Override - public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Search not available in offline mode"); - } - - @Override - public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - List<Playlist> playlists = new ArrayList<Playlist>(); - File root = FileUtil.getPlaylistDirectory(); - for (File file : FileUtil.listFiles(root)) { - Playlist playlist = new Playlist(file.getName(), file.getName()); - playlists.add(playlist); - } - return playlists; - } - - @Override - public MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception { - DownloadService downloadService = DownloadServiceImpl.getInstance(); - if (downloadService == null) { - return new MusicDirectory(); - } - - Reader reader = null; - try { - reader = new FileReader(FileUtil.getPlaylistFile(name)); - MusicDirectory fullList = new PlaylistParser(context).parse(reader, progressListener); - MusicDirectory playlist = new MusicDirectory(); - for(MusicDirectory.Entry song: fullList.getChildren()) { - DownloadFile downloadFile = downloadService.forSong(song); - File completeFile = downloadFile.getCompleteFile(); - if(completeFile.exists()) { - playlist.addChild(song); - } - } - return playlist; - } finally { - Util.close(reader); - } - } - - @Override - public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Playlists not available in offline mode"); - } - - @Override - public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Lyrics not available in offline mode"); - } - - @Override - public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Scrobbling not available in offline mode"); - } - - @Override - public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Album lists not available in offline mode"); - } - - @Override - public String getVideoUrl(Context context, String id) { - return null; - } - - @Override - public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { - throw new OfflineException("Jukebox not available in offline mode"); - } - - @Override - public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { - File root = FileUtil.getMusicDirectory(context); - List<File> children = new LinkedList<File>(); - listFilesRecursively(root, children); - MusicDirectory result = new MusicDirectory(); - - if (children.isEmpty()) { - return result; - } - Random random = new Random(); - for (int i = 0; i < size; i++) { - File file = children.get(random.nextInt(children.size())); - result.addChild(createEntry(context, file, getName(file))); - } - - return result; - } - - private void listFilesRecursively(File parent, List<File> children) { - for (File file : FileUtil.listMusicFiles(parent)) { - if (file.isFile()) { - children.add(file); - } else { - listFilesRecursively(file, children); - } - } - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java deleted file mode 100644 index a939feef..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/RESTMusicService.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.FileReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.conn.params.ConnManagerParams; -import org.apache.http.conn.params.ConnPerRouteBean; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.scheme.SocketFactory; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.util.Log; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.domain.SearchCritera; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.domain.ServerInfo; -import net.sourceforge.subsonic.androidapp.domain.Version; -import net.sourceforge.subsonic.androidapp.service.parser.AlbumListParser; -import net.sourceforge.subsonic.androidapp.service.parser.ErrorParser; -import net.sourceforge.subsonic.androidapp.service.parser.IndexesParser; -import net.sourceforge.subsonic.androidapp.service.parser.JukeboxStatusParser; -import net.sourceforge.subsonic.androidapp.service.parser.LicenseParser; -import net.sourceforge.subsonic.androidapp.service.parser.LyricsParser; -import net.sourceforge.subsonic.androidapp.service.parser.MusicDirectoryParser; -import net.sourceforge.subsonic.androidapp.service.parser.MusicFoldersParser; -import net.sourceforge.subsonic.androidapp.service.parser.PlaylistParser; -import net.sourceforge.subsonic.androidapp.service.parser.PlaylistsParser; -import net.sourceforge.subsonic.androidapp.service.parser.RandomSongsParser; -import net.sourceforge.subsonic.androidapp.service.parser.SearchResult2Parser; -import net.sourceforge.subsonic.androidapp.service.parser.SearchResultParser; -import net.sourceforge.subsonic.androidapp.service.parser.VersionParser; -import net.sourceforge.subsonic.androidapp.service.ssl.SSLSocketFactory; -import net.sourceforge.subsonic.androidapp.service.ssl.TrustSelfSignedStrategy; -import net.sourceforge.subsonic.androidapp.util.CancellableTask; -import net.sourceforge.subsonic.androidapp.util.Constants; -import net.sourceforge.subsonic.androidapp.util.FileUtil; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public class RESTMusicService implements MusicService { - - private static final String TAG = RESTMusicService.class.getSimpleName(); - - private static final int SOCKET_CONNECT_TIMEOUT = 10 * 1000; - private static final int SOCKET_READ_TIMEOUT_DEFAULT = 10 * 1000; - private static final int SOCKET_READ_TIMEOUT_DOWNLOAD = 30 * 1000; - private static final int SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS = 60 * 1000; - private static final int SOCKET_READ_TIMEOUT_GET_PLAYLIST = 60 * 1000; - - // Allow 20 seconds extra timeout per MB offset. - private static final double TIMEOUT_MILLIS_PER_OFFSET_BYTE = 20000.0 / 1000000.0; - - /** - * URL from which to fetch latest versions. - */ - private static final String VERSION_URL = "http://subsonic.org/backend/version.view"; - - private static final int HTTP_REQUEST_MAX_ATTEMPTS = 5; - private static final long REDIRECTION_CHECK_INTERVAL_MILLIS = 60L * 60L * 1000L; - - private final DefaultHttpClient httpClient; - private long redirectionLastChecked; - private int redirectionNetworkType = -1; - private String redirectFrom; - private String redirectTo; - private final ThreadSafeClientConnManager connManager; - - public RESTMusicService() { - - // Create and initialize default HTTP parameters - HttpParams params = new BasicHttpParams(); - ConnManagerParams.setMaxTotalConnections(params, 20); - ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(20)); - HttpConnectionParams.setConnectionTimeout(params, SOCKET_CONNECT_TIMEOUT); - HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_DEFAULT); - - // Turn off stale checking. Our connections break all the time anyway, - // and it's not worth it to pay the penalty of checking every time. - HttpConnectionParams.setStaleCheckingEnabled(params, false); - - // Create and initialize scheme registry - SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); - schemeRegistry.register(new Scheme("https", createSSLSocketFactory(), 443)); - - // Create an HttpClient with the ThreadSafeClientConnManager. - // This connection manager must be used if more than one thread will - // be using the HttpClient. - connManager = new ThreadSafeClientConnManager(params, schemeRegistry); - httpClient = new DefaultHttpClient(connManager, params); - } - - private SocketFactory createSSLSocketFactory() { - try { - return new SSLSocketFactory(new TrustSelfSignedStrategy(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); - } catch (Throwable x) { - Log.e(TAG, "Failed to create custom SSL socket factory, using default.", x); - return org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory(); - } - } - - @Override - public void ping(Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "ping", null); - try { - new ErrorParser(context).parse(reader); - } finally { - Util.close(reader); - } - } - - @Override - public boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getLicense", null); - try { - ServerInfo serverInfo = new LicenseParser(context).parse(reader); - return serverInfo.isLicenseValid(); - } finally { - Util.close(reader); - } - } - - public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - List<MusicFolder> cachedMusicFolders = readCachedMusicFolders(context); - if (cachedMusicFolders != null && !refresh) { - return cachedMusicFolders; - } - - Reader reader = getReader(context, progressListener, "getMusicFolders", null); - try { - List<MusicFolder> musicFolders = new MusicFoldersParser(context).parse(reader, progressListener); - writeCachedMusicFolders(context, musicFolders); - return musicFolders; - } finally { - Util.close(reader); - } - } - - @Override - public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Indexes cachedIndexes = readCachedIndexes(context, musicFolderId); - if (cachedIndexes != null && !refresh) { - return cachedIndexes; - } - - long lastModified = cachedIndexes == null ? 0L : cachedIndexes.getLastModified(); - - List<String> parameterNames = new ArrayList<String>(); - List<Object> parameterValues = new ArrayList<Object>(); - - parameterNames.add("ifModifiedSince"); - parameterValues.add(lastModified); - - if (musicFolderId != null) { - parameterNames.add("musicFolderId"); - parameterValues.add(musicFolderId); - } - - Reader reader = getReader(context, progressListener, "getIndexes", null, parameterNames, parameterValues); - try { - Indexes indexes = new IndexesParser(context).parse(reader, progressListener); - if (indexes != null) { - writeCachedIndexes(context, indexes, musicFolderId); - return indexes; - } - return cachedIndexes; - } finally { - Util.close(reader); - } - } - - private Indexes readCachedIndexes(Context context, String musicFolderId) { - String filename = getCachedIndexesFilename(context, musicFolderId); - return FileUtil.deserialize(context, filename); - } - - private void writeCachedIndexes(Context context, Indexes indexes, String musicFolderId) { - String filename = getCachedIndexesFilename(context, musicFolderId); - FileUtil.serialize(context, indexes, filename); - } - - private String getCachedIndexesFilename(Context context, String musicFolderId) { - String s = Util.getRestUrl(context, null) + musicFolderId; - return "indexes-" + Math.abs(s.hashCode()) + ".ser"; - } - - private ArrayList<MusicFolder> readCachedMusicFolders(Context context) { - String filename = getCachedMusicFoldersFilename(context); - return FileUtil.deserialize(context, filename); - } - - private void writeCachedMusicFolders(Context context, List<MusicFolder> musicFolders) { - String filename = getCachedMusicFoldersFilename(context); - FileUtil.serialize(context, new ArrayList<MusicFolder>(musicFolders), filename); - } - - private String getCachedMusicFoldersFilename(Context context) { - String s = Util.getRestUrl(context, null); - return "musicFolders-" + Math.abs(s.hashCode()) + ".ser"; - } - - @Override - public MusicDirectory getMusicDirectory(String id, boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getMusicDirectory", null, "id", id); - try { - return new MusicDirectoryParser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - @Override - public SearchResult search(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception { - try { - return searchNew(critera, context, progressListener); - } catch (ServerTooOldException x) { - // Ensure backward compatibility with REST 1.3. - return searchOld(critera, context, progressListener); - } - } - - /** - * Search using the "search" REST method. - */ - private SearchResult searchOld(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception { - List<String> parameterNames = Arrays.asList("any", "songCount"); - List<Object> parameterValues = Arrays.<Object>asList(critera.getQuery(), critera.getSongCount()); - Reader reader = getReader(context, progressListener, "search", null, parameterNames, parameterValues); - try { - return new SearchResultParser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - /** - * Search using the "search2" REST method, available in 1.4.0 and later. - */ - private SearchResult searchNew(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception { - checkServerVersion(context, "1.4", null); - - List<String> parameterNames = Arrays.asList("query", "artistCount", "albumCount", "songCount"); - List<Object> parameterValues = Arrays.<Object>asList(critera.getQuery(), critera.getArtistCount(), - critera.getAlbumCount(), critera.getSongCount()); - Reader reader = getReader(context, progressListener, "search2", null, parameterNames, parameterValues); - try { - return new SearchResult2Parser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - @Override - public MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception { - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_PLAYLIST); - - Reader reader = getReader(context, progressListener, "getPlaylist", params, "id", id); - OutputStreamWriter out = null; - try { - out = new OutputStreamWriter(new FileOutputStream(FileUtil.getPlaylistFile(name))); - - char[] buff = new char[256]; - int n; - while((n = reader.read(buff)) >= 0) { - out.write(buff, 0, n); - } - } finally { - Util.close(out); - Util.close(reader); - } - - try { - reader = new FileReader(FileUtil.getPlaylistFile(name)); - return new PlaylistParser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - @Override - public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getPlaylists", null); - try { - return new PlaylistsParser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - @Override - public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception { - List<String> parameterNames = new LinkedList<String>(); - List<Object> parameterValues = new LinkedList<Object>(); - - if (id != null) { - parameterNames.add("playlistId"); - parameterValues.add(id); - } - if (name != null) { - parameterNames.add("name"); - parameterValues.add(name); - } - for (MusicDirectory.Entry entry : entries) { - parameterNames.add("songId"); - parameterValues.add(entry.getId()); - } - - Reader reader = getReader(context, progressListener, "createPlaylist", null, parameterNames, parameterValues); - try { - new ErrorParser(context).parse(reader); - } finally { - Util.close(reader); - } - } - - @Override - public Lyrics getLyrics(String artist, String title, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getLyrics", null, Arrays.asList("artist", "title"), Arrays.<Object>asList(artist, title)); - try { - return new LyricsParser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - @Override - public void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception { - checkServerVersion(context, "1.5", "Scrobbling not supported."); - Reader reader = getReader(context, progressListener, "scrobble", null, Arrays.asList("id", "submission"), Arrays.<Object>asList(id, submission)); - try { - new ErrorParser(context).parse(reader); - } finally { - Util.close(reader); - } - } - - @Override - public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReader(context, progressListener, "getAlbumList", - null, Arrays.asList("type", "size", "offset"), Arrays.<Object>asList(type, size, offset)); - try { - return new AlbumListParser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - @Override - public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS); - - Reader reader = getReader(context, progressListener, "getRandomSongs", params, "size", size); - try { - return new RandomSongsParser(context).parse(reader, progressListener); - } finally { - Util.close(reader); - } - } - - @Override - public Version getLocalVersion(Context context) throws Exception { - PackageInfo packageInfo = context.getPackageManager().getPackageInfo("net.sourceforge.subsonic.androidapp", 0); - return new Version(packageInfo.versionName); - } - - @Override - public Version getLatestVersion(Context context, ProgressListener progressListener) throws Exception { - Reader reader = getReaderForURL(context, VERSION_URL, null, null, null, progressListener); - try { - return new VersionParser().parse(reader); - } finally { - Util.close(reader); - } - } - - private void checkServerVersion(Context context, String version, String text) throws ServerTooOldException { - Version serverVersion = Util.getServerRestVersion(context); - Version requiredVersion = new Version(version); - boolean ok = serverVersion == null || serverVersion.compareTo(requiredVersion) >= 0; - - if (!ok) { - throw new ServerTooOldException(text, serverVersion, requiredVersion); - } - } - - @Override - public Bitmap getCoverArt(Context context, MusicDirectory.Entry entry, int size, boolean saveToFile, ProgressListener progressListener) throws Exception { - - // Synchronize on the entry so that we don't download concurrently for the same song. - synchronized (entry) { - - // Use cached file, if existing. - Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, entry, size); - if (bitmap != null) { - return bitmap; - } - - String url = Util.getRestUrl(context, "getCoverArt"); - - InputStream in = null; - try { - List<String> parameterNames = Arrays.asList("id", "size"); - List<Object> parameterValues = Arrays.<Object>asList(entry.getCoverArt(), size); - HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener); - in = entity.getContent(); - - // If content type is XML, an error occured. Get it. - String contentType = Util.getContentType(entity); - if (contentType != null && contentType.startsWith("text/xml")) { - new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8)); - return null; // Never reached. - } - - byte[] bytes = Util.toByteArray(in); - - if (saveToFile) { - OutputStream out = null; - try { - out = new FileOutputStream(FileUtil.getAlbumArtFile(context, entry)); - out.write(bytes); - } finally { - Util.close(out); - } - } - - return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - - } finally { - Util.close(in); - } - } - } - - @Override - public HttpResponse getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, CancellableTask task) throws Exception { - - String url = Util.getRestUrl(context, "stream"); - - // Set socket read timeout. Note: The timeout increases as the offset gets larger. This is - // to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server. - // In that case, the server uses a long time before sending any data, causing the client to time out. - HttpParams params = new BasicHttpParams(); - int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE); - HttpConnectionParams.setSoTimeout(params, timeout); - - // Add "Range" header if offset is given. - List<Header> headers = new ArrayList<Header>(); - if (offset > 0) { - headers.add(new BasicHeader("Range", "bytes=" + offset + "-")); - } - List<String> parameterNames = Arrays.asList("id", "maxBitRate"); - List<Object> parameterValues = Arrays.<Object>asList(song.getId(), maxBitrate); - HttpResponse response = getResponseForURL(context, url, params, parameterNames, parameterValues, headers, null, task); - - // If content type is XML, an error occurred. Get it. - String contentType = Util.getContentType(response.getEntity()); - if (contentType != null && contentType.startsWith("text/xml")) { - InputStream in = response.getEntity().getContent(); - try { - new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8)); - } finally { - Util.close(in); - } - } - - return response; - } - - @Override - public String getVideoUrl(Context context, String id) { - StringBuilder builder = new StringBuilder(Util.getRestUrl(context, "videoPlayer")); - builder.append("&id=").append(id); - builder.append("&maxBitRate=500"); - builder.append("&autoplay=true"); - - String url = rewriteUrlWithRedirect(context, builder.toString()); - Log.i(TAG, "Using video URL: " + url); - return url; - } - - @Override - public JukeboxStatus updateJukeboxPlaylist(List<String> ids, Context context, ProgressListener progressListener) throws Exception { - int n = ids.size(); - List<String> parameterNames = new ArrayList<String>(n + 1); - parameterNames.add("action"); - for (int i = 0; i < n; i++) { - parameterNames.add("id"); - } - List<Object> parameterValues = new ArrayList<Object>(); - parameterValues.add("set"); - parameterValues.addAll(ids); - - return executeJukeboxCommand(context, progressListener, parameterNames, parameterValues); - } - - @Override - public JukeboxStatus skipJukebox(int index, int offsetSeconds, Context context, ProgressListener progressListener) throws Exception { - List<String> parameterNames = Arrays.asList("action", "index", "offset"); - List<Object> parameterValues = Arrays.<Object>asList("skip", index, offsetSeconds); - return executeJukeboxCommand(context, progressListener, parameterNames, parameterValues); - } - - @Override - public JukeboxStatus stopJukebox(Context context, ProgressListener progressListener) throws Exception { - return executeJukeboxCommand(context, progressListener, Arrays.asList("action"), Arrays.<Object>asList("stop")); - } - - @Override - public JukeboxStatus startJukebox(Context context, ProgressListener progressListener) throws Exception { - return executeJukeboxCommand(context, progressListener, Arrays.asList("action"), Arrays.<Object>asList("start")); - } - - @Override - public JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception { - return executeJukeboxCommand(context, progressListener, Arrays.asList("action"), Arrays.<Object>asList("status")); - } - - @Override - public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { - List<String> parameterNames = Arrays.asList("action", "gain"); - List<Object> parameterValues = Arrays.<Object>asList("setGain", gain); - return executeJukeboxCommand(context, progressListener, parameterNames, parameterValues); - - } - - private JukeboxStatus executeJukeboxCommand(Context context, ProgressListener progressListener, List<String> parameterNames, List<Object> parameterValues) throws Exception { - checkServerVersion(context, "1.7", "Jukebox not supported."); - Reader reader = getReader(context, progressListener, "jukeboxControl", null, parameterNames, parameterValues); - try { - return new JukeboxStatusParser(context).parse(reader); - } finally { - Util.close(reader); - } - } - - private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams) throws Exception { - return getReader(context, progressListener, method, requestParams, Collections.<String>emptyList(), Collections.emptyList()); - } - - private Reader getReader(Context context, ProgressListener progressListener, String method, - HttpParams requestParams, String parameterName, Object parameterValue) throws Exception { - return getReader(context, progressListener, method, requestParams, Arrays.asList(parameterName), Arrays.<Object>asList(parameterValue)); - } - - private Reader getReader(Context context, ProgressListener progressListener, String method, - HttpParams requestParams, List<String> parameterNames, List<Object> parameterValues) throws Exception { - - if (progressListener != null) { - progressListener.updateProgress(R.string.service_connecting); - } - - String url = Util.getRestUrl(context, method); - return getReaderForURL(context, url, requestParams, parameterNames, parameterValues, progressListener); - } - - private Reader getReaderForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames, - List<Object> parameterValues, ProgressListener progressListener) throws Exception { - HttpEntity entity = getEntityForURL(context, url, requestParams, parameterNames, parameterValues, progressListener); - if (entity == null) { - throw new RuntimeException("No entity received for URL " + url); - } - - InputStream in = entity.getContent(); - return new InputStreamReader(in, Constants.UTF_8); - } - - private HttpEntity getEntityForURL(Context context, String url, HttpParams requestParams, List<String> parameterNames, - List<Object> parameterValues, ProgressListener progressListener) throws Exception { - return getResponseForURL(context, url, requestParams, parameterNames, parameterValues, null, progressListener, null).getEntity(); - } - - private HttpResponse getResponseForURL(Context context, String url, HttpParams requestParams, - List<String> parameterNames, List<Object> parameterValues, - List<Header> headers, ProgressListener progressListener, CancellableTask task) throws Exception { - Log.d(TAG, "Connections in pool: " + connManager.getConnectionsInPool()); - - // If not too many parameters, extract them to the URL rather than relying on the HTTP POST request being - // received intact. Remember, HTTP POST requests are converted to GET requests during HTTP redirects, thus - // loosing its entity. - if (parameterNames != null && parameterNames.size() < 10) { - StringBuilder builder = new StringBuilder(url); - for (int i = 0; i < parameterNames.size(); i++) { - builder.append("&").append(parameterNames.get(i)).append("="); - builder.append(URLEncoder.encode(String.valueOf(parameterValues.get(i)), "UTF-8")); - } - url = builder.toString(); - parameterNames = null; - parameterValues = null; - } - - String rewrittenUrl = rewriteUrlWithRedirect(context, url); - return executeWithRetry(context, rewrittenUrl, url, requestParams, parameterNames, parameterValues, headers, progressListener, task); - } - - private HttpResponse executeWithRetry(Context context, String url, String originalUrl, HttpParams requestParams, - List<String> parameterNames, List<Object> parameterValues, - List<Header> headers, ProgressListener progressListener, CancellableTask task) throws IOException { - Log.i(TAG, "Using URL " + url); - - final AtomicReference<Boolean> cancelled = new AtomicReference<Boolean>(false); - int attempts = 0; - while (true) { - attempts++; - HttpContext httpContext = new BasicHttpContext(); - final HttpPost request = new HttpPost(url); - - if (task != null) { - // Attempt to abort the HTTP request if the task is cancelled. - task.setOnCancelListener(new CancellableTask.OnCancelListener() { - @Override - public void onCancel() { - cancelled.set(true); - request.abort(); - } - }); - } - - if (parameterNames != null) { - List<NameValuePair> params = new ArrayList<NameValuePair>(); - for (int i = 0; i < parameterNames.size(); i++) { - params.add(new BasicNameValuePair(parameterNames.get(i), String.valueOf(parameterValues.get(i)))); - } - request.setEntity(new UrlEncodedFormEntity(params, Constants.UTF_8)); - } - - if (requestParams != null) { - request.setParams(requestParams); - Log.d(TAG, "Socket read timeout: " + HttpConnectionParams.getSoTimeout(requestParams) + " ms."); - } - - if (headers != null) { - for (Header header : headers) { - request.addHeader(header); - } - } - - // Set credentials to get through apache proxies that require authentication. - SharedPreferences prefs = Util.getPreferences(context); - int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1); - String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null); - String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null); - httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), - new UsernamePasswordCredentials(username, password)); - - try { - HttpResponse response = httpClient.execute(request, httpContext); - detectRedirect(originalUrl, context, httpContext); - return response; - } catch (IOException x) { - request.abort(); - if (attempts >= HTTP_REQUEST_MAX_ATTEMPTS || cancelled.get()) { - throw x; - } - if (progressListener != null) { - String msg = context.getResources().getString(R.string.music_service_retry, attempts, HTTP_REQUEST_MAX_ATTEMPTS - 1); - progressListener.updateProgress(msg); - } - Log.w(TAG, "Got IOException (" + attempts + "), will retry", x); - increaseTimeouts(requestParams); - Util.sleepQuietly(2000L); - } - } - } - - private void increaseTimeouts(HttpParams requestParams) { - if (requestParams != null) { - int connectTimeout = HttpConnectionParams.getConnectionTimeout(requestParams); - if (connectTimeout != 0) { - HttpConnectionParams.setConnectionTimeout(requestParams, (int) (connectTimeout * 1.3F)); - } - int readTimeout = HttpConnectionParams.getSoTimeout(requestParams); - if (readTimeout != 0) { - HttpConnectionParams.setSoTimeout(requestParams, (int) (readTimeout * 1.5F)); - } - } - } - - private void detectRedirect(String originalUrl, Context context, HttpContext httpContext) { - HttpUriRequest request = (HttpUriRequest) httpContext.getAttribute(ExecutionContext.HTTP_REQUEST); - HttpHost host = (HttpHost) httpContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST); - String redirectedUrl = host.toURI() + request.getURI(); - - redirectFrom = originalUrl.substring(0, originalUrl.indexOf("/rest/")); - redirectTo = redirectedUrl.substring(0, redirectedUrl.indexOf("/rest/")); - - Log.i(TAG, redirectFrom + " redirects to " + redirectTo); - redirectionLastChecked = System.currentTimeMillis(); - redirectionNetworkType = getCurrentNetworkType(context); - } - - private String rewriteUrlWithRedirect(Context context, String url) { - - // Only cache for a certain time. - if (System.currentTimeMillis() - redirectionLastChecked > REDIRECTION_CHECK_INTERVAL_MILLIS) { - return url; - } - - // Ignore cache if network type has changed. - if (redirectionNetworkType != getCurrentNetworkType(context)) { - return url; - } - - if (redirectFrom == null || redirectTo == null) { - return url; - } - - return url.replace(redirectFrom, redirectTo); - } - - private int getCurrentNetworkType(Context context) { - ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = manager.getActiveNetworkInfo(); - return networkInfo == null ? -1 : networkInfo.getType(); - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/Scrobbler.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/Scrobbler.java deleted file mode 100644 index ce121a4b..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/Scrobbler.java +++ /dev/null @@ -1,52 +0,0 @@ -package net.sourceforge.subsonic.androidapp.service; - -import android.content.Context; -import android.util.Log; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * Scrobbles played songs to Last.fm. - * - * @author Sindre Mehus - * @version $Id$ - */ -public class Scrobbler { - - private static final String TAG = Scrobbler.class.getSimpleName(); - - private String lastSubmission; - private String lastNowPlaying; - - public void scrobble(final Context context, final DownloadFile song, final boolean submission) { - if (song == null || !Util.isScrobblingEnabled(context)) { - return; - } - final String id = song.getSong().getId(); - - // Avoid duplicate registrations. - if (submission && id.equals(lastSubmission)) { - return; - } - if (!submission && id.equals(lastNowPlaying)) { - return; - } - if (submission) { - lastSubmission = id; - } else { - lastNowPlaying = id; - } - - new Thread("Scrobble " + song) { - @Override - public void run() { - MusicService service = MusicServiceFactory.getMusicService(context); - try { - service.scrobble(id, submission, context, null); - Log.i(TAG, "Scrobbled '" + (submission ? "submission" : "now playing") + "' for " + song); - } catch (Exception x) { - Log.i(TAG, "Failed to scrobble'" + (submission ? "submission" : "now playing") + "' for " + song, x); - } - } - }.start(); - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ServerTooOldException.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ServerTooOldException.java deleted file mode 100644 index 9d433385..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ServerTooOldException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service; - -import net.sourceforge.subsonic.androidapp.domain.Version; - -/** - * Thrown if the REST API version implemented by the server is too old. - * - * @author Sindre Mehus - * @version $Id$ - */ -public class ServerTooOldException extends Exception { - - private final String text; - private final Version serverVersion; - private final Version requiredVersion; - - public ServerTooOldException(String text, Version serverVersion, Version requiredVersion) { - this.text = text; - this.serverVersion = serverVersion; - this.requiredVersion = requiredVersion; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (text != null) { - builder.append(text).append(" "); - } - builder.append("Server API version too old. "); - builder.append("Requires ").append(requiredVersion).append(" but is ").append(serverVersion).append("."); - return builder.toString(); - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java deleted file mode 100644 index 4ddff7e9..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/AbstractParser.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import java.io.Reader; - -import org.xmlpull.v1.XmlPullParser; - -import android.content.Context; -import android.util.Xml; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.Version; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import net.sourceforge.subsonic.androidapp.util.Util; - -/** - * @author Sindre Mehus - */ -public abstract class AbstractParser { - - private final Context context; - private XmlPullParser parser; - private boolean rootElementFound; - - public AbstractParser(Context context) { - this.context = context; - } - - protected Context getContext() { - return context; - } - - protected void handleError() throws Exception { - int code = getInteger("code"); - String message; - switch (code) { - case 20: - message = context.getResources().getString(R.string.parser_upgrade_client); - break; - case 30: - message = context.getResources().getString(R.string.parser_upgrade_server); - break; - case 40: - message = context.getResources().getString(R.string.parser_not_authenticated); - break; - case 50: - message = context.getResources().getString(R.string.parser_not_authorized); - break; - default: - message = get("message"); - break; - } - throw new SubsonicRESTException(code, message); - } - - protected void updateProgress(ProgressListener progressListener, int messageId) { - if (progressListener != null) { - progressListener.updateProgress(messageId); - } - } - - protected void updateProgress(ProgressListener progressListener, String message) { - if (progressListener != null) { - progressListener.updateProgress(message); - } - } - - protected String getText() { - return parser.getText(); - } - - protected String get(String name) { - return parser.getAttributeValue(null, name); - } - - protected boolean getBoolean(String name) { - return "true".equals(get(name)); - } - - protected Integer getInteger(String name) { - String s = get(name); - return s == null ? null : Integer.valueOf(s); - } - - protected Long getLong(String name) { - String s = get(name); - return s == null ? null : Long.valueOf(s); - } - - protected Float getFloat(String name) { - String s = get(name); - return s == null ? null : Float.valueOf(s); - } - - protected void init(Reader reader) throws Exception { - parser = Xml.newPullParser(); - parser.setInput(reader); - rootElementFound = false; - } - - protected int nextParseEvent() throws Exception { - return parser.next(); - } - - protected String getElementName() { - String name = parser.getName(); - if ("subsonic-response".equals(name)) { - rootElementFound = true; - String version = get("version"); - if (version != null) { - Util.setServerRestVersion(context, new Version(version)); - } - } - return name; - } - - protected void validate() throws Exception { - if (!rootElementFound) { - throw new Exception(context.getResources().getString(R.string.background_task_parse_error)); - } - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/AlbumListParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/AlbumListParser.java deleted file mode 100644 index 298ef114..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/AlbumListParser.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; - -/** - * @author Sindre Mehus - */ -public class AlbumListParser extends MusicDirectoryEntryParser { - - public AlbumListParser(Context context) { - super(context); - } - - public MusicDirectory parse(Reader reader, ProgressListener progressListener) throws Exception { - - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - MusicDirectory dir = new MusicDirectory(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("album".equals(name)) { - dir.addChild(parseEntry()); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - return dir; - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/ErrorParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/ErrorParser.java deleted file mode 100644 index b2c61c5b..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/ErrorParser.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; - -/** - * @author Sindre Mehus - */ -public class ErrorParser extends AbstractParser { - - public ErrorParser(Context context) { - super(context); - } - - public void parse(Reader reader) throws Exception { - - init(reader); - - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG && "error".equals(getElementName())) { - handleError(); - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/IndexesParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/IndexesParser.java deleted file mode 100644 index 83ef3e77..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/IndexesParser.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import java.io.Reader; -import java.util.List; -import java.util.ArrayList; - -import org.xmlpull.v1.XmlPullParser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.Artist; -import net.sourceforge.subsonic.androidapp.domain.Indexes; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import android.util.Log; - -/** - * @author Sindre Mehus - */ -public class IndexesParser extends AbstractParser { - private static final String TAG = IndexesParser.class.getSimpleName(); - - public IndexesParser(Context context) { - super(context); - } - - public Indexes parse(Reader reader, ProgressListener progressListener) throws Exception { - - long t0 = System.currentTimeMillis(); - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - List<Artist> artists = new ArrayList<Artist>(); - List<Artist> shortcuts = new ArrayList<Artist>(); - Long lastModified = null; - int eventType; - String index = "#"; - boolean changed = false; - - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("indexes".equals(name)) { - changed = true; - lastModified = getLong("lastModified"); - } else if ("index".equals(name)) { - index = get("name"); - - } else if ("artist".equals(name)) { - Artist artist = new Artist(); - artist.setId(get("id")); - artist.setName(get("name")); - artist.setIndex(index); - artists.add(artist); - - if (artists.size() % 10 == 0) { - String msg = getContext().getResources().getString(R.string.parser_artist_count, artists.size()); - updateProgress(progressListener, msg); - } - } else if ("shortcut".equals(name)) { - Artist shortcut = new Artist(); - shortcut.setId(get("id")); - shortcut.setName(get("name")); - shortcut.setIndex("*"); - shortcuts.add(shortcut); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - - if (!changed) { - return null; - } - - long t1 = System.currentTimeMillis(); - Log.d(TAG, "Got " + artists.size() + " artist(s) in " + (t1 - t0) + "ms."); - - String msg = getContext().getResources().getString(R.string.parser_artist_count, artists.size()); - updateProgress(progressListener, msg); - - return new Indexes(lastModified == null ? 0L : lastModified, shortcuts, artists); - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/JukeboxStatusParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/JukeboxStatusParser.java deleted file mode 100644 index 2a61508d..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/JukeboxStatusParser.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import java.io.Reader; - -import org.xmlpull.v1.XmlPullParser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.domain.JukeboxStatus; - -/** - * @author Sindre Mehus - */ -public class JukeboxStatusParser extends AbstractParser { - - public JukeboxStatusParser(Context context) { - super(context); - } - - public JukeboxStatus parse(Reader reader) throws Exception { - - init(reader); - - JukeboxStatus jukeboxStatus = new JukeboxStatus(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("jukeboxPlaylist".equals(name) || "jukeboxStatus".equals(name)) { - jukeboxStatus.setPositionSeconds(getInteger("position")); - jukeboxStatus.setCurrentIndex(getInteger("currentIndex")); - jukeboxStatus.setPlaying(getBoolean("playing")); - jukeboxStatus.setGain(getFloat("gain")); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - - return jukeboxStatus; - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/LicenseParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/LicenseParser.java deleted file mode 100644 index 636c3e6e..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/LicenseParser.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; - -import net.sourceforge.subsonic.androidapp.domain.ServerInfo; -import net.sourceforge.subsonic.androidapp.domain.Version; - -/** - * @author Sindre Mehus - */ -public class LicenseParser extends AbstractParser { - - public LicenseParser(Context context) { - super(context); - } - - public ServerInfo parse(Reader reader) throws Exception { - - init(reader); - - ServerInfo serverInfo = new ServerInfo(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("subsonic-response".equals(name)) { - serverInfo.setRestVersion(new Version(get("version"))); - } else if ("license".equals(name)) { - serverInfo.setLicenseValid(getBoolean("valid")); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - - return serverInfo; - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/LyricsParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/LyricsParser.java deleted file mode 100644 index 698fb4b8..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/LyricsParser.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2010 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.Lyrics; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; - -/** - * @author Sindre Mehus - */ -public class LyricsParser extends AbstractParser { - - public LyricsParser(Context context) { - super(context); - } - - public Lyrics parse(Reader reader, ProgressListener progressListener) throws Exception { - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - Lyrics lyrics = null; - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("lyrics".equals(name)) { - lyrics = new Lyrics(); - lyrics.setArtist(get("artist")); - lyrics.setTitle(get("title")); - } else if ("error".equals(name)) { - handleError(); - } - } else if (eventType == XmlPullParser.TEXT) { - if (lyrics != null && lyrics.getText() == null) { - lyrics.setText(getText()); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - return lyrics; - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java deleted file mode 100644 index 3da90613..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; - -/** - * @author Sindre Mehus - */ -public class MusicDirectoryEntryParser extends AbstractParser { - - public MusicDirectoryEntryParser(Context context) { - super(context); - } - - protected MusicDirectory.Entry parseEntry() { - MusicDirectory.Entry entry = new MusicDirectory.Entry(); - entry.setId(get("id")); - entry.setParent(get("parent")); - entry.setTitle(get("title")); - entry.setDirectory(getBoolean("isDir")); - entry.setCoverArt(get("coverArt")); - entry.setArtist(get("artist")); - - if (!entry.isDirectory()) { - entry.setAlbum(get("album")); - entry.setTrack(getInteger("track")); - entry.setYear(getInteger("year")); - entry.setGenre(get("genre")); - entry.setContentType(get("contentType")); - entry.setSuffix(get("suffix")); - entry.setTranscodedContentType(get("transcodedContentType")); - entry.setTranscodedSuffix(get("transcodedSuffix")); - entry.setSize(getLong("size")); - entry.setDuration(getInteger("duration")); - entry.setBitRate(getInteger("bitRate")); - entry.setPath(get("path")); - entry.setVideo(getBoolean("isVideo")); - } - return entry; - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryParser.java deleted file mode 100644 index b818fc3d..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryParser.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import android.util.Log; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; - -/** - * @author Sindre Mehus - */ -public class MusicDirectoryParser extends MusicDirectoryEntryParser { - - private static final String TAG = MusicDirectoryParser.class.getSimpleName(); - - public MusicDirectoryParser(Context context) { - super(context); - } - - public MusicDirectory parse(Reader reader, ProgressListener progressListener) throws Exception { - - long t0 = System.currentTimeMillis(); - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - MusicDirectory dir = new MusicDirectory(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("child".equals(name)) { - dir.addChild(parseEntry()); - } else if ("directory".equals(name)) { - dir.setName(get("name")); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - long t1 = System.currentTimeMillis(); - Log.d(TAG, "Got music directory in " + (t1 - t0) + "ms."); - - return dir; - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicFoldersParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicFoldersParser.java deleted file mode 100644 index 35057bd9..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/MusicFoldersParser.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; - -import org.xmlpull.v1.XmlPullParser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicFolder; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; - -/** - * @author Sindre Mehus - */ -public class MusicFoldersParser extends AbstractParser { - - public MusicFoldersParser(Context context) { - super(context); - } - - public List<MusicFolder> parse(Reader reader, ProgressListener progressListener) throws Exception { - - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - List<MusicFolder> result = new ArrayList<MusicFolder>(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String tag = getElementName(); - if ("musicFolder".equals(tag)) { - String id = get("id"); - String name = get("name"); - result.add(new MusicFolder(id, name)); - } else if ("error".equals(tag)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - return result; - } - -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/PlaylistParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/PlaylistParser.java deleted file mode 100644 index ee829639..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/PlaylistParser.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; - -/** - * @author Sindre Mehus - */ -public class PlaylistParser extends MusicDirectoryEntryParser { - - public PlaylistParser(Context context) { - super(context); - } - - public MusicDirectory parse(Reader reader, ProgressListener progressListener) throws Exception { - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - MusicDirectory dir = new MusicDirectory(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("entry".equals(name)) { - dir.addChild(parseEntry()); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - return dir; - } - -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/PlaylistsParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/PlaylistsParser.java deleted file mode 100644 index c1b88b8c..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/PlaylistsParser.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.Playlist; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Sindre Mehus - */ -public class PlaylistsParser extends AbstractParser { - - public PlaylistsParser(Context context) { - super(context); - } - - public List<Playlist> parse(Reader reader, ProgressListener progressListener) throws Exception { - - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - List<Playlist> result = new ArrayList<Playlist>(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String tag = getElementName(); - if ("playlist".equals(tag)) { - String id = get("id"); - String name = get("name"); - result.add(new Playlist(id, name)); - } else if ("error".equals(tag)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - return result; - } - -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/RandomSongsParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/RandomSongsParser.java deleted file mode 100644 index 0bf422b7..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/RandomSongsParser.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; - -/** - * @author Sindre Mehus - */ -public class RandomSongsParser extends MusicDirectoryEntryParser { - - public RandomSongsParser(Context context) { - super(context); - } - - public MusicDirectory parse(Reader reader, ProgressListener progressListener) throws Exception { - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - MusicDirectory dir = new MusicDirectory(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("song".equals(name)) { - dir.addChild(parseEntry()); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - return dir; - } - -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SearchResult2Parser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SearchResult2Parser.java deleted file mode 100644 index 01052f25..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SearchResult2Parser.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.domain.Artist; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; -import java.util.List; -import java.util.ArrayList; - -/** - * @author Sindre Mehus - */ -public class SearchResult2Parser extends MusicDirectoryEntryParser { - - public SearchResult2Parser(Context context) { - super(context); - } - - public SearchResult parse(Reader reader, ProgressListener progressListener) throws Exception { - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - List<Artist> artists = new ArrayList<Artist>(); - List<MusicDirectory.Entry> albums = new ArrayList<MusicDirectory.Entry>(); - List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("artist".equals(name)) { - Artist artist = new Artist(); - artist.setId(get("id")); - artist.setName(get("name")); - artists.add(artist); - } else if ("album".equals(name)) { - albums.add(parseEntry()); - } else if ("song".equals(name)) { - songs.add(parseEntry()); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - return new SearchResult(artists, albums, songs); - } - -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SearchResultParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SearchResultParser.java deleted file mode 100644 index c38b077f..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SearchResultParser.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import android.content.Context; -import net.sourceforge.subsonic.androidapp.R; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; -import net.sourceforge.subsonic.androidapp.domain.SearchResult; -import net.sourceforge.subsonic.androidapp.domain.Artist; -import net.sourceforge.subsonic.androidapp.util.ProgressListener; -import org.xmlpull.v1.XmlPullParser; - -import java.io.Reader; -import java.util.Collections; -import java.util.List; -import java.util.ArrayList; - -/** - * @author Sindre Mehus - */ -public class SearchResultParser extends MusicDirectoryEntryParser { - - public SearchResultParser(Context context) { - super(context); - } - - public SearchResult parse(Reader reader, ProgressListener progressListener) throws Exception { - updateProgress(progressListener, R.string.parser_reading); - init(reader); - - List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(); - int eventType; - do { - eventType = nextParseEvent(); - if (eventType == XmlPullParser.START_TAG) { - String name = getElementName(); - if ("match".equals(name)) { - songs.add(parseEntry()); - } else if ("error".equals(name)) { - handleError(); - } - } - } while (eventType != XmlPullParser.END_DOCUMENT); - - validate(); - updateProgress(progressListener, R.string.parser_reading_done); - - return new SearchResult(Collections.<Artist>emptyList(), Collections.<MusicDirectory.Entry>emptyList(), songs); - } - -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SubsonicRESTException.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SubsonicRESTException.java deleted file mode 100644 index b46b6f22..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/SubsonicRESTException.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.sourceforge.subsonic.androidapp.service.parser; - -/** - * @author Sindre Mehus - * @version $Id$ - */ -public class SubsonicRESTException extends Exception { - - private final int code; - - public SubsonicRESTException(int code, String message) { - super(message); - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/VersionParser.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/VersionParser.java deleted file mode 100644 index b8a05531..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/parser/VersionParser.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.service.parser; - -import net.sourceforge.subsonic.androidapp.domain.Version; - -import java.io.BufferedReader; -import java.io.Reader; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @author Sindre Mehus - */ -public class VersionParser { - - public Version parse(Reader reader) throws Exception { - - BufferedReader bufferedReader = new BufferedReader(reader); - Pattern pattern = Pattern.compile("SUBSONIC_ANDROID_VERSION_BEGIN(.*)SUBSONIC_ANDROID_VERSION_END"); - String line = bufferedReader.readLine(); - while (line != null) { - Matcher finalMatcher = pattern.matcher(line); - if (finalMatcher.find()) { - return new Version(finalMatcher.group(1)); - } - line = bufferedReader.readLine(); - } - return null; - } -}
\ No newline at end of file diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/SSLSocketFactory.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/SSLSocketFactory.java deleted file mode 100644 index 0e146650..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/SSLSocketFactory.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package net.sourceforge.subsonic.androidapp.service.ssl; - -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.scheme.HostNameResolver; -import org.apache.http.conn.scheme.LayeredSocketFactory; -import org.apache.http.conn.ssl.AllowAllHostnameVerifier; -import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; -import org.apache.http.conn.ssl.StrictHostnameVerifier; -import org.apache.http.conn.ssl.X509HostnameVerifier; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.UnrecoverableKeyException; - -/** - * Layered socket factory for TLS/SSL connections. - * <p> - * SSLSocketFactory can be used to validate the identity of the HTTPS server against a list of - * trusted certificates and to authenticate to the HTTPS server using a private key. - * <p> - * SSLSocketFactory will enable server authentication when supplied with - * a {@link KeyStore trust-store} file containing one or several trusted certificates. The client - * secure socket will reject the connection during the SSL session handshake if the target HTTPS - * server attempts to authenticate itself with a non-trusted certificate. - * <p> - * Use JDK keytool utility to import a trusted certificate and generate a trust-store file: - * <pre> - * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore - * </pre> - * <p> - * In special cases the standard trust verification process can be bypassed by using a custom - * {@link TrustStrategy}. This interface is primarily intended for allowing self-signed - * certificates to be accepted as trusted without having to add them to the trust-store file. - * <p> - * The following parameters can be used to customize the behavior of this - * class: - * <ul> - * <li>{@link org.apache.http.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li> - * <li>{@link org.apache.http.params.CoreConnectionPNames#SO_TIMEOUT}</li> - * </ul> - * <p> - * SSLSocketFactory will enable client authentication when supplied with - * a {@link KeyStore key-store} file containing a private key/public certificate - * pair. The client secure socket will use the private key to authenticate - * itself to the target HTTPS server during the SSL session handshake if - * requested to do so by the server. - * The target HTTPS server will in its turn verify the certificate presented - * by the client in order to establish client's authenticity - * <p> - * Use the following sequence of actions to generate a key-store file - * </p> - * <ul> - * <li> - * <p> - * Use JDK keytool utility to generate a new key - * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre> - * For simplicity use the same password for the key as that of the key-store - * </p> - * </li> - * <li> - * <p> - * Issue a certificate signing request (CSR) - * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre> - * </p> - * </li> - * <li> - * <p> - * Send the certificate request to the trusted Certificate Authority for signature. - * One may choose to act as her own CA and sign the certificate request using a PKI - * tool, such as OpenSSL. - * </p> - * </li> - * <li> - * <p> - * Import the trusted CA root certificate - * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> - * </p> - * </li> - * <li> - * <p> - * Import the PKCS#7 file containg the complete certificate chain - * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> - * </p> - * </li> - * <li> - * <p> - * Verify the content the resultant keystore file - * <pre>keytool -list -v -keystore my.keystore</pre> - * </p> - * </li> - * </ul> - * - * @since 4.0 - */ -public class SSLSocketFactory implements LayeredSocketFactory { - - public static final String TLS = "TLS"; - public static final String SSL = "SSL"; - public static final String SSLV2 = "SSLv2"; - - public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER - = new AllowAllHostnameVerifier(); - - public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER - = new BrowserCompatHostnameVerifier(); - - public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER - = new StrictHostnameVerifier(); - - /** - * The default factory using the default JVM settings for secure connections. - */ - private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory(); - - /** - * Gets the default factory, which uses the default JVM settings for secure - * connections. - * - * @return the default factory - */ - public static SSLSocketFactory getSocketFactory() { - return DEFAULT_FACTORY; - } - - private final javax.net.ssl.SSLSocketFactory socketfactory; - private final HostNameResolver nameResolver; - // TODO: make final - private volatile X509HostnameVerifier hostnameVerifier; - - private static SSLContext createSSLContext( - String algorithm, - final KeyStore keystore, - final String keystorePassword, - final KeyStore truststore, - final SecureRandom random, - final TrustStrategy trustStrategy) - throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException { - if (algorithm == null) { - algorithm = TLS; - } - KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( - KeyManagerFactory.getDefaultAlgorithm()); - kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray(): null); - KeyManager[] keymanagers = kmfactory.getKeyManagers(); - TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); - tmfactory.init(keystore); - TrustManager[] trustmanagers = tmfactory.getTrustManagers(); - if (trustmanagers != null && trustStrategy != null) { - for (int i = 0; i < trustmanagers.length; i++) { - TrustManager tm = trustmanagers[i]; - if (tm instanceof X509TrustManager) { - trustmanagers[i] = new TrustManagerDecorator( - (X509TrustManager) tm, trustStrategy); - } - } - } - - SSLContext sslcontext = SSLContext.getInstance(algorithm); - sslcontext.init(keymanagers, trustmanagers, random); - return sslcontext; - } - - /** - * @deprecated Use {@link #SSLSocketFactory(String, KeyStore, String, KeyStore, SecureRandom, X509HostnameVerifier)} - */ - @Deprecated - public SSLSocketFactory( - final String algorithm, - final KeyStore keystore, - final String keystorePassword, - final KeyStore truststore, - final SecureRandom random, - final HostNameResolver nameResolver) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - this(createSSLContext( - algorithm, keystore, keystorePassword, truststore, random, null), - nameResolver); - } - - /** - * @since 4.1 - */ - public SSLSocketFactory( - String algorithm, - final KeyStore keystore, - final String keystorePassword, - final KeyStore truststore, - final SecureRandom random, - final X509HostnameVerifier hostnameVerifier) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - this(createSSLContext( - algorithm, keystore, keystorePassword, truststore, random, null), - hostnameVerifier); - } - - /** - * @since 4.1 - */ - public SSLSocketFactory( - String algorithm, - final KeyStore keystore, - final String keystorePassword, - final KeyStore truststore, - final SecureRandom random, - final TrustStrategy trustStrategy, - final X509HostnameVerifier hostnameVerifier) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - this(createSSLContext( - algorithm, keystore, keystorePassword, truststore, random, trustStrategy), - hostnameVerifier); - } - - public SSLSocketFactory( - final KeyStore keystore, - final String keystorePassword, - final KeyStore truststore) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - this(TLS, keystore, keystorePassword, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - } - - public SSLSocketFactory( - final KeyStore keystore, - final String keystorePassword) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{ - this(TLS, keystore, keystorePassword, null, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - } - - public SSLSocketFactory( - final KeyStore truststore) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - this(TLS, null, null, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - } - - /** - * @since 4.1 - */ - public SSLSocketFactory( - final TrustStrategy trustStrategy, - final X509HostnameVerifier hostnameVerifier) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - this(TLS, null, null, null, null, trustStrategy, hostnameVerifier); - } - - /** - * @since 4.1 - */ - public SSLSocketFactory( - final TrustStrategy trustStrategy) - throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - this(TLS, null, null, null, null, trustStrategy, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - } - - public SSLSocketFactory(final SSLContext sslContext) { - this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - } - - /** - * @deprecated Use {@link #SSLSocketFactory(SSLContext)} - */ - @Deprecated - public SSLSocketFactory( - final SSLContext sslContext, final HostNameResolver nameResolver) { - super(); - this.socketfactory = sslContext.getSocketFactory(); - this.hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; - this.nameResolver = nameResolver; - } - - /** - * @since 4.1 - */ - public SSLSocketFactory( - final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) { - super(); - this.socketfactory = sslContext.getSocketFactory(); - this.hostnameVerifier = hostnameVerifier; - this.nameResolver = null; - } - - private SSLSocketFactory() { - super(); - this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory(); - this.hostnameVerifier = null; - this.nameResolver = null; - } - - /** - * @param params Optional parameters. Parameters passed to this method will have no effect. - * This method will create a unconnected instance of {@link Socket} class - * using {@link javax.net.ssl.SSLSocketFactory#createSocket()} method. - * @since 4.1 - */ - @SuppressWarnings("cast") - public Socket createSocket(final HttpParams params) throws IOException { - // the cast makes sure that the factory is working as expected - return (SSLSocket) this.socketfactory.createSocket(); - } - - @SuppressWarnings("cast") - public Socket createSocket() throws IOException { - // the cast makes sure that the factory is working as expected - return (SSLSocket) this.socketfactory.createSocket(); - } - - /** - * @since 4.1 - */ - public Socket connectSocket( - final Socket sock, - final InetSocketAddress remoteAddress, - final InetSocketAddress localAddress, - final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { - if (remoteAddress == null) { - throw new IllegalArgumentException("Remote address may not be null"); - } - if (params == null) { - throw new IllegalArgumentException("HTTP parameters may not be null"); - } - SSLSocket sslsock = (SSLSocket) (sock != null ? sock : createSocket()); - if (localAddress != null) { -// sslsock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params)); - sslsock.bind(localAddress); - } - - int connTimeout = HttpConnectionParams.getConnectionTimeout(params); - int soTimeout = HttpConnectionParams.getSoTimeout(params); - - try { - sslsock.connect(remoteAddress, connTimeout); - } catch (SocketTimeoutException ex) { - throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/" - + remoteAddress.getAddress() + " timed out"); - } - sslsock.setSoTimeout(soTimeout); - if (this.hostnameVerifier != null) { - try { - this.hostnameVerifier.verify(remoteAddress.getHostName(), sslsock); - // verifyHostName() didn't blowup - good! - } catch (IOException iox) { - // close the socket before re-throwing the exception - try { sslsock.close(); } catch (Exception x) { /*ignore*/ } - throw iox; - } - } - return sslsock; - } - - - /** - * Checks whether a socket connection is secure. - * This factory creates TLS/SSL socket connections - * which, by default, are considered secure. - * <br/> - * Derived classes may override this method to perform - * runtime checks, for example based on the cypher suite. - * - * @param sock the connected socket - * - * @return <code>true</code> - * - * @throws IllegalArgumentException if the argument is invalid - */ - public boolean isSecure(final Socket sock) throws IllegalArgumentException { - if (sock == null) { - throw new IllegalArgumentException("Socket may not be null"); - } - // This instanceof check is in line with createSocket() above. - if (!(sock instanceof SSLSocket)) { - throw new IllegalArgumentException("Socket not created by this factory"); - } - // This check is performed last since it calls the argument object. - if (sock.isClosed()) { - throw new IllegalArgumentException("Socket is closed"); - } - return true; - } - - /** - * @since 4.1 - */ - public Socket createLayeredSocket( - final Socket socket, - final String host, - final int port, - final boolean autoClose) throws IOException, UnknownHostException { - SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket( - socket, - host, - port, - autoClose - ); - if (this.hostnameVerifier != null) { - this.hostnameVerifier.verify(host, sslSocket); - } - // verifyHostName() didn't blowup - good! - return sslSocket; - } - - @Deprecated - public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) { - if ( hostnameVerifier == null ) { - throw new IllegalArgumentException("Hostname verifier may not be null"); - } - this.hostnameVerifier = hostnameVerifier; - } - - public X509HostnameVerifier getHostnameVerifier() { - return this.hostnameVerifier; - } - - /** - * @deprecated Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)} - */ - @Deprecated - public Socket connectSocket( - final Socket socket, - final String host, int port, - final InetAddress localAddress, int localPort, - final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { - InetSocketAddress local = null; - if (localAddress != null || localPort > 0) { - // we need to bind explicitly - if (localPort < 0) { - localPort = 0; // indicates "any" - } - local = new InetSocketAddress(localAddress, localPort); - } - InetAddress remoteAddress; - if (this.nameResolver != null) { - remoteAddress = this.nameResolver.resolve(host); - } else { - remoteAddress = InetAddress.getByName(host); - } - InetSocketAddress remote = new InetSocketAddress(remoteAddress, port); - return connectSocket(socket, remote, local, params); - } - - /** - * @deprecated Use {@link #createLayeredSocket(Socket, String, int, boolean)} - */ - @Deprecated - public Socket createSocket( - final Socket socket, - final String host, int port, - boolean autoClose) throws IOException, UnknownHostException { - return createLayeredSocket(socket, host, port, autoClose); - } - -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustManagerDecorator.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustManagerDecorator.java deleted file mode 100644 index 41d98249..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustManagerDecorator.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ -package net.sourceforge.subsonic.androidapp.service.ssl; - -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.X509TrustManager; - - -/** - * @since 4.1 - */ -class TrustManagerDecorator implements X509TrustManager { - - private final X509TrustManager trustManager; - private final TrustStrategy trustStrategy; - - TrustManagerDecorator(final X509TrustManager trustManager, final TrustStrategy trustStrategy) { - super(); - this.trustManager = trustManager; - this.trustStrategy = trustStrategy; - } - - public void checkClientTrusted( - final X509Certificate[] chain, final String authType) throws CertificateException { - this.trustManager.checkClientTrusted(chain, authType); - } - - public void checkServerTrusted( - final X509Certificate[] chain, final String authType) throws CertificateException { - if (!this.trustStrategy.isTrusted(chain, authType)) { - this.trustManager.checkServerTrusted(chain, authType); - } - } - - public X509Certificate[] getAcceptedIssuers() { - return this.trustManager.getAcceptedIssuers(); - } - -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustSelfSignedStrategy.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustSelfSignedStrategy.java deleted file mode 100644 index 4fdaaba2..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustSelfSignedStrategy.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ -package net.sourceforge.subsonic.androidapp.service.ssl; - -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -/** - * A trust strategy that accepts self-signed certificates as trusted. Verification of all other - * certificates is done by the trust manager configured in the SSL context. - * - * @since 4.1 - */ -public class TrustSelfSignedStrategy implements TrustStrategy { - - public boolean isTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { - return true; - } - -} diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustStrategy.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustStrategy.java deleted file mode 100644 index 3cf75b68..00000000 --- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/service/ssl/TrustStrategy.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ -package net.sourceforge.subsonic.androidapp.service.ssl; - -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -/** - * A strategy to establish trustworthiness of certificates without consulting the trust manager - * configured in the actual SSL context. This interface can be used to override the standard - * JSSE certificate verification process. - * - * @since 4.1 - */ -public interface TrustStrategy { - - /** - * Determines whether the certificate chain can be trusted without consulting the trust manager - * configured in the actual SSL context. This method can be used to override the standard JSSE - * certificate verification process. - * <p> - * Please note that, if this method returns <code>false</code>, the trust manager configured - * in the actual SSL context can still clear the certificate as trusted. - * - * @param chain the peer certificate chain - * @param authType the authentication type based on the client certificate - * @return <code>true</code> if the certificate can be trusted without verification by - * the trust manager, <code>false</code> otherwise. - * @throws CertificateException thrown if the certificate is not trusted or invalid. - */ - boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException; - -} |