/* * Copyright (C) 2005-2009 Team XBMC * http://xbmc.org * * This Program 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 2, or (at your option) * any later version. * * This Program 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 XBMC Remote; see the file license. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ package org.xbmc.android.remote.business; import java.util.ArrayList; import org.xbmc.api.business.DataResponse; import org.xbmc.api.business.IMusicManager; import org.xbmc.api.business.INotifiableManager; import org.xbmc.api.business.ISortableManager; import org.xbmc.api.data.IControlClient; import org.xbmc.api.data.IMusicClient; import org.xbmc.api.info.GuiSettings; import org.xbmc.api.info.PlayStatus; import org.xbmc.api.object.Album; import org.xbmc.api.object.Artist; import org.xbmc.api.object.Genre; import org.xbmc.api.object.Song; import org.xbmc.api.type.SortType; import org.xbmc.httpapi.WifiStateException; import org.xbmc.jsonrpc.client.MusicClient; import android.content.Context; /** * Asynchronously wraps the {@link org.xbmc.httpapi.client.InfoClient} class. * * @author Team XBMC */ public class MusicManager extends AbstractManager implements IMusicManager, ISortableManager, INotifiableManager { /** * Gets all albums from database * @param response Response object */ public void getCompilations(final DataResponse<ArrayList<Album>> response, final Context context) { mHandler.post(new Command<ArrayList<Album>>(response, this){ @Override public void doRun() throws Exception { final IMusicClient mc = music(context); ArrayList<Integer> compilationArtistIDs = mc.getCompilationArtistIDs(MusicManager.this); response.value = mc.getAlbums(MusicManager.this, compilationArtistIDs); } }); } /** * Gets all albums from database * @param response Response object */ public void getAlbums(final DataResponse<ArrayList<Album>> response, final Context context) { mHandler.post(new Command<ArrayList<Album>>(response, this) { @Override public void doRun() throws Exception { response.value = music(context).getAlbums(MusicManager.this, getSortBy(SortType.ALBUM), getSortOrder()); } }); } /** * SYNCHRONOUSLY gets all albums from database * @return All albums in database */ public ArrayList<Album> getAlbums(final Context context) { try { //TODO fix this to throw return music(context).getAlbums(MusicManager.this, getSortBy(SortType.ALBUM), getSortOrder()); } catch (WifiStateException e) { e.printStackTrace(); } return null; } /** * Gets all albums of an artist from database * @param response Response object * @param artist Artist of the albums */ public void getAlbums(final DataResponse<ArrayList<Album>> response, final Artist artist, final Context context) { // mHandler.post(new Runnable() { // public void run() { // response.value = music(context).getAlbums(MusicManager.this, artist, getSortBy(SortType.ALBUM), getSortOrder()); // onFinish(response); // } // }); mHandler.post(new Command<ArrayList<Album>>(response, this){ @Override public void doRun() throws Exception { response.value = music(context).getAlbums(MusicManager.this, artist, getSortBy(SortType.ALBUM), getSortOrder()); } }); } /** * Gets all albums of a genre from database * @param response Response object * @param artist Genre of the albums */ public void getAlbums(final DataResponse<ArrayList<Album>> response, final Genre genre, final Context context) { // mHandler.post(new Runnable() { // public void run() { // response.value = music(context).getAlbums(MusicManager.this, genre, getSortBy(SortType.ALBUM), getSortOrder()); // onFinish(response); // } // }); mHandler.post(new Command<ArrayList<Album>>(response, this) { public void doRun() throws Exception{ response.value = music(context).getAlbums(MusicManager.this, genre, getSortBy(SortType.ALBUM), getSortOrder()); } }); } /** * Gets all songs of an album from database * @param response Response object * @param album Album */ public void getSongs(final DataResponse<ArrayList<Song>> response, final Album album, final Context context) { // mHandler.post(new Runnable() { // public void run() { // response.value = music(context).getSongs(MusicManager.this, album, getSortBy(SortType.ARTIST), getSortOrder()); // onFinish(response); // } // }); mHandler.post(new Command<ArrayList<Song>>(response, this) { public void doRun() throws Exception{ response.value = music(context).getSongs(MusicManager.this, album, getSortBy(SortType.TRACK), getSortOrder()); } }); } /** * Gets all songs from an artist from database * @param response Response object * @param album Artist */ public void getSongs(final DataResponse<ArrayList<Song>> response, final Artist artist, final Context context) { // mHandler.post(new Runnable() { // public void run() { // response.value = music(context).getSongs(MusicManager.this, artist, getSortBy(SortType.ARTIST), getSortOrder()); // onFinish(response); // } // }); mHandler.post(new Command<ArrayList<Song>>(response, this) { public void doRun() throws Exception{ response.value = music(context).getSongs(MusicManager.this, artist, getSortBy(SortType.ARTIST), getSortOrder()); } }); } /** * Gets all songs of a genre from database * @param response Response object * @param album Genre */ public void getSongs(final DataResponse<ArrayList<Song>> response, final Genre genre, final Context context) { // mHandler.post(new Runnable() { // public void run() { // response.value = music(context).getSongs(MusicManager.this, genre, getSortBy(SortType.ARTIST), getSortOrder()); // onFinish(response); // } // }); mHandler.post(new Command<ArrayList<Song>>(response, this) { public void doRun() throws Exception{ response.value = music(context).getSongs(MusicManager.this, genre, getSortBy(SortType.ARTIST), getSortOrder()); } }); } /** * Gets all artists from database * @param response Response object */ public void getArtists(final DataResponse<ArrayList<Artist>> response, final Context context) { // mHandler.post(new Runnable() { // public void run() { // boolean albumArtistsOnly; // try { // albumArtistsOnly = info(context).getGuiSettingBool(MusicManager.this, GuiSettings.MusicLibrary.ALBUM_ARTISTS_ONLY); // response.value = music(context).getArtists(MusicManager.this, albumArtistsOnly); // onFinish(response); // } catch (WifiStateException e) { // onWrongConnectionState(e.getState()); // } // } // }); mHandler.post(new Command<ArrayList<Artist>>(response, this) { public void doRun() throws Exception{ final boolean albumArtistsOnly = !info(context).getGuiSettingBool(MusicManager.this, GuiSettings.MusicLibrary.SHOW_COMPLATION_ARTISTS); response.value = music(context).getArtists(MusicManager.this, albumArtistsOnly); } }); } /** * Gets all artists with at least one song of a genre. * @param response Response object * @param genre Genre */ public void getArtists(final DataResponse<ArrayList<Artist>> response, final Genre genre, final Context context) { // mHandler.post(new Runnable() { // public void run() { // boolean albumArtistsOnly; // try { // albumArtistsOnly = info(context).getGuiSettingBool(MusicManager.this, GuiSettings.MusicLibrary.ALBUM_ARTISTS_ONLY); // response.value = music(context).getArtists(MusicManager.this, genre, albumArtistsOnly); // onFinish(response); // } catch (WifiStateException e) { // onWrongConnectionState(e.getState()); // } // } // }); mHandler.post(new Command<ArrayList<Artist>>(response, this) { public void doRun() throws Exception{ final boolean albumArtistsOnly = !info(context).getGuiSettingBool(MusicManager.this, GuiSettings.MusicLibrary.SHOW_COMPLATION_ARTISTS); response.value = music(context).getArtists(MusicManager.this, genre, albumArtistsOnly); } }); } /** * Gets all artists from database * @param response Response object */ public void getGenres(final DataResponse<ArrayList<Genre>> response, final Context context) { // mHandler.post(new Runnable() { // public void run() { // response.value = music(context).getGenres(MusicManager.this); // onFinish(response); // } // }); mHandler.post(new Command<ArrayList<Genre>>(response, this) { public void doRun() throws Exception{ response.value = music(context).getGenres(MusicManager.this); } }); } /** * Adds an album to the current playlist. If current playlist is stopped, * the album is added to playlist and the first song is selected to play. * If something is playing already, the album is only queued. * * @param response Response object * @param album Album to add */ public void addToPlaylist(final DataResponse<Boolean> response, final Album album, final Context context) { // mHandler.post(new Runnable() { // public void run() { // final IMusicClient mc = music(context); // final IControlClient cc = control(context); // final int numAlreadyQueued = mc.getPlaylistSize(MusicManager.this); // response.value = mc.addToPlaylist(MusicManager.this, album); // checkForPlayAfterQueue(mc, cc, numAlreadyQueued); // onFinish(response); // } // }); mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ final IMusicClient mc = music(context); final IControlClient cc = control(context); final int numAlreadyQueued = mc.getPlaylistSize(MusicManager.this); response.value = mc.addToPlaylist(MusicManager.this, album, getSortBy(SortType.TRACK), getSortOrder()); checkForPlayAfterQueue(mc, cc, numAlreadyQueued); } }); } /** * Adds all songs of a genre to the current playlist. If current playlist is stopped, * play is executed. Value is the first song of the added album. * @param response Response object * @param genre Genre of songs to add */ public void addToPlaylist(final DataResponse<Boolean> response, final Genre genre, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ final IMusicClient mc = music(context); final IControlClient cc = control(context); final int numAlreadyQueued = mc.getPlaylistSize(MusicManager.this); response.value = mc.addToPlaylist(MusicManager.this, genre, getSortBy(SortType.ARTIST), getSortOrder()); checkForPlayAfterQueue(mc, cc, numAlreadyQueued); } }); } /** * Adds a song to the current playlist. Even if the playlist is empty, only this song will be added. * @param response Response object * @param album Song to add */ public void addToPlaylist(final DataResponse<Boolean> response, final Song song, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ response.value = music(context).addToPlaylist(MusicManager.this, song); } }); } /** * Adds a song to the current playlist. If the playlist is empty, the whole * album will be added with this song playing, otherwise only this song is * added. * * <b>Attention</b>, the response.value result is different as usual: True * means the whole album was added, false means ony the song. * * @param response Response object * @param album Album to add * @param song Song to play */ public void addToPlaylist(final DataResponse<Boolean> response, final Album album, final Song song, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ final IMusicClient mc = music(context); final IControlClient cc = control(context); final int playStatus = cc.getPlayState(MusicManager.this); //cc.setCurrentPlaylist(MusicManager.this, MusicClient.PLAYLIST_ID); final int playlistSize = mc.getPlaylistSize(MusicManager.this); int playPos = -1; if (playlistSize == 0) { // if playlist is empty, add the whole album int n = 0; for (Song albumSong : mc.getSongs(MusicManager.this, album, getSortBy(PREF_SORT_KEY_ALBUM), getSortOrder())) { if (albumSong.id == song.id) { playPos = n; break; } n++; } mc.addToPlaylist(MusicManager.this, album, getSortBy(PREF_SORT_KEY_ALBUM), getSortOrder()); response.value = true; } else { // otherwise, only add the song mc.addToPlaylist(MusicManager.this, song); response.value = false; } if (playStatus == PlayStatus.STOPPED) { // if nothing is playing, play the song if (playPos == 0) { mc.playlistSetSong(MusicManager.this, playPos + 1); mc.playPrev(MusicManager.this); } else if (playPos > 0) { mc.playlistSetSong(MusicManager.this, playPos - 1); mc.playNext(MusicManager.this); } else { mc.playlistSetSong(MusicManager.this, playlistSize - 1); mc.playNext(MusicManager.this); } } } }); } /** * Adds all songs from an artist to the playlist. If current playlist is * stopped, the all songs of the artist are added to playlist and the first * song is selected to play. If something is playing already, the songs are * only queued. * @param response Response object * @param artist Artist */ public void addToPlaylist(final DataResponse<Boolean> response, final Artist artist, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ final IMusicClient mc = music(context); final IControlClient cc = control(context); final int numAlreadyQueued = mc.getPlaylistSize(MusicManager.this); response.value = mc.addToPlaylist(MusicManager.this, artist, getSortBy(SortType.ALBUM), getSortOrder()); checkForPlayAfterQueue(mc, cc, numAlreadyQueued); } }); } /** * Adds all songs of a genre from an artist to the playlist. If nothing is playing, * the first song will be played, otherwise songs are just added to the playlist. * @param response Response object * @param artist Artist * @param genre Genre */ public void addToPlaylist(final DataResponse<Boolean> response, final Artist artist, final Genre genre, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ final IMusicClient mc = music(context); final IControlClient cc = control(context); final int numAlreadyQueued = mc.getPlaylistSize(MusicManager.this); response.value = mc.addToPlaylist(MusicManager.this, artist, genre, getSortBy(SortType.ARTIST), getSortOrder()); checkForPlayAfterQueue(mc, cc, numAlreadyQueued); } }); } /** * Sets the media at playlist position position to be the next item to be played. * @param response Response object * @param position Position, starting with 0. */ public void setPlaylistSong(final DataResponse<Boolean> response, final int position, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ response.value = music(context).setPlaylistPosition(MusicManager.this, position); } }); } /** * Removes media from the current playlist. It is not possible to remove the media if it is currently being played. * @param position Position to remove, starting with 0. * @return True on success, false otherwise. */ public void removeFromPlaylist(final DataResponse<Boolean> response, final int position, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ response.value = music(context).removeFromPlaylist(MusicManager.this, position); } }); } /** * Removes media from the current playlist. It is not possible to remove the media if it is currently being played. * @param position Complete path (including filename) of the media to be removed. * @return True on success, false otherwise. */ public void removeFromPlaylist(final DataResponse<Boolean> response, final String path, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ response.value = music(context).removeFromPlaylist(MusicManager.this, path); } }); } /** * Plays an album * @param response Response object * @param album Album to play */ public void play(final DataResponse<Boolean> response, final Album album, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ control(context).stop(MusicManager.this); response.value = music(context).play(MusicManager.this, album, getSortBy(SortType.TRACK), getSortOrder()); } }); } /** * Plays all songs of a genre * @param response Response object * @param genre Genre of songs to play */ public void play(final DataResponse<Boolean> response, final Genre genre, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ control(context).stop(MusicManager.this); response.value = music(context).play(MusicManager.this, genre, getSortBy(SortType.ARTIST), getSortOrder()); } }); } /** * Plays a song * @param response Response object * @param song Song to play */ public void play(final DataResponse<Boolean> response, final Song song, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ control(context).stop(MusicManager.this); response.value = music(context).play(MusicManager.this, song); } }); } /** * Plays a song, but the whole album is added to the playlist. * @param response Response object * @param album Album to queue * @param song Song to play */ public void play(final DataResponse<Boolean> response, final Album album, final Song song, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ final IMusicClient mc = music(context); final IControlClient cc = control(context); int n = 0; int playPos = 0; mc.clearPlaylist(MusicManager.this); for (Song albumSong : mc.getSongs(MusicManager.this, album, getSortBy(SortType.TRACK), getSortOrder())) { if (albumSong.id == song.id) { playPos = n; break; } n++; } cc.stop(MusicManager.this); mc.addToPlaylist(MusicManager.this, album, getSortBy(SortType.TRACK), getSortOrder()); if (playPos > 0) { response.value = mc.playlistSetSong(MusicManager.this, playPos); } else{ response.value = cc.setCurrentPlaylist(MusicManager.this, MusicClient.PLAYLIST_ID); } } }); } /** * Plays all songs from an artist * @param response Response object * @param artist Artist whose songs to play */ public void play(final DataResponse<Boolean> response, final Artist artist, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ control(context).stop(MusicManager.this); response.value = music(context).play(MusicManager.this, artist, getSortBy(SortType.ALBUM), getSortOrder()); } }); } /** * Plays songs of a genre from an artist * @param response Response object * @param artist Artist whose songs to play * @param genre Genre filter */ public void play(final DataResponse<Boolean> response, final Artist artist, final Genre genre, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ control(context).stop(MusicManager.this); response.value = music(context).play(MusicManager.this, artist, genre); } }); } /** * Starts playing the next media in the current playlist. * @param response Response object */ public void playlistNext(final DataResponse<Boolean> response, final Context context) { mHandler.post(new Command<Boolean>(response, this) { public void doRun() throws Exception{ response.value = music(context).playNext(MusicManager.this); } }); } /** * Returns an array of songs on the playlist. Empty array if nothing is playing. * @param response Response object */ public void getPlaylist(final DataResponse<ArrayList<String>> response, final Context context) { mHandler.post(new Command<ArrayList<String>>(response, this) { public void doRun() throws Exception{ response.value = music(context).getPlaylist(MusicManager.this); final String firstEntry = response.value.get(0); if (firstEntry != null && firstEntry.equals("[Empty]")) { response.value = new ArrayList<String>(); } } }); } /** * Returns the position of the currently playing song in the playlist. First position is 0. * @param response Response object */ public void getPlaylistPosition(final DataResponse<Integer> response, final Context context) { mHandler.post(new Command<Integer>(response, this) { public void doRun() throws Exception{ response.value = music(context).getPlaylistPosition(MusicManager.this); } }); } /** * Updates the album object with additional data from the albuminfo table * @param response Response object * @param album Album to update */ public void updateAlbumInfo(final DataResponse<Album> response, final Album album, final Context context) { mHandler.post(new Command<Album>(response, this) { public void doRun() throws Exception{ response.value = music(context).updateAlbumInfo(MusicManager.this, album); } }); } /** * Updates the artist object with additional data from the artistinfo table * @param response Response object * @param artist Artist to update */ public void updateArtistInfo(final DataResponse<Artist> response, final Artist artist, final Context context) { mHandler.post(new Command<Artist>(response, this) { public void doRun() throws Exception{ response.value = music(context).updateArtistInfo(MusicManager.this, artist); } }); } /** * Checks if something's playing. If that's not the case, set the * playlist's play position either to the start if there were no items * before, or to the first position of the newly added files. * @param mc Music client * @param cc Control client * @param numAlreadyQueued Number of previously queued items */ private void checkForPlayAfterQueue(final IMusicClient mc, final IControlClient cc, int numAlreadyQueued) { final int ps = cc.getPlayState(MusicManager.this); if (ps == PlayStatus.STOPPED) { // if nothing is playing, play the song cc.setCurrentPlaylist(MusicManager.this, MusicClient.PLAYLIST_ID); if (numAlreadyQueued == 0) { mc.playNext(MusicManager.this); } else { mc.playlistSetSong(MusicManager.this, numAlreadyQueued); } } } public void onWrongConnectionState(int state) { // TODO Auto-generated method stub } }