/*
* 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.android.util.ClientFactory;
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.ICoverArt;
import org.xbmc.api.object.Song;
import org.xbmc.api.type.MediaType;
import org.xbmc.api.type.SortType;
import org.xbmc.httpapi.WifiStateException;
import android.content.Context;
import android.graphics.Bitmap;
/**
* Asynchronously wraps the {@link org.xbmc.httpapi.client.InfoClient} class.
*
* @author Team XBMC
*/
public class MusicManager extends AbstractManager implements IMusicManager,
ISortableManager, INotifiableManager {
public static final String EMPTY_PLAYLIST_ITEM = "[Empty]";
/**
* 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, getSort(SortType.ALBUM));
}
});
}
/**
* 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,
getSort(SortType.ALBUM));
}
});
}
/**
* 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,
getSort(SortType.ALBUM));
} 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 Command<ArrayList<Album>>(response, this) {
@Override
public void doRun() throws Exception {
mResponse.value = music(context).getAlbums(MusicManager.this,
artist, getSort(SortType.ALBUM));
}
});
}
/**
* 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 Command<ArrayList<Album>>(response, this) {
public void doRun() throws Exception {
response.value = music(context).getAlbums(MusicManager.this,
genre, getSort(SortType.ALBUM));
}
});
}
/**
* 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 Command<ArrayList<Song>>(response, this) {
public void doRun() throws Exception {
response.value = music(context).getSongs(MusicManager.this,
album, getSort(SortType.TRACK));
}
});
}
/**
* 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 Command<ArrayList<Song>>(response, this) {
public void doRun() throws Exception {
response.value = music(context).getSongs(MusicManager.this,
artist, getSort(SortType.ARTIST));
}
});
}
/**
* 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 Command<ArrayList<Song>>(response, this) {
public void doRun() throws Exception {
response.value = music(context).getSongs(MusicManager.this,
genre, getSort(SortType.ARTIST));
}
});
}
/**
* Gets all artists from database
*
* @param response
* Response object
*/
public void getArtists(final DataResponse<ArrayList<Artist>> response,
final Context context) {
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, getSort(SortType.ARTIST));
}
});
}
/**
* 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 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, getSort(SortType.ARTIST));
}
});
}
/**
* 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,
getSort(SortType.GENRE));
}
});
}
/**
* 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 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,
getSort(SortType.TRACK));
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,
getSort(SortType.ARTIST));
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,
MediaType.getPlaylistType(MediaType.MUSIC));
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,
getSort(PREF_SORT_KEY_ALBUM))) {
if (albumSong.id == song.id) {
playPos = n;
break;
}
n++;
}
mc.addToPlaylist(MusicManager.this, album,
getSort(PREF_SORT_KEY_ALBUM));
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,
getSort(SortType.ALBUM));
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, getSort(SortType.ARTIST));
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);
}
});
}
/**
* 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,
getSort(SortType.TRACK));
}
});
}
/**
* 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,
getSort(SortType.ARTIST));
}
});
}
/**
* 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,
getSort(SortType.TRACK))) {
if (albumSong.id == song.id) {
playPos = n;
break;
}
n++;
}
cc.stop(MusicManager.this);
mc.addToPlaylist(MusicManager.this, album,
getSort(SortType.TRACK));
cc.setCurrentPlaylist(MusicManager.this,
MediaType.getPlaylistType(MediaType.MUSIC));
if (playPos > 0) {
mc.playlistSetSong(MusicManager.this, playPos - 1);
}
response.value = mc.playNext(MusicManager.this);
}
});
}
/**
* 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,
getSort(SortType.ALBUM));
}
});
}
/**
* 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, getSort(SortType.ARTIST));
}
});
}
/**
* 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);
if (response.value.size() == 0) {
}
final String firstEntry = response.value.get(0);
if (firstEntry != null
&& firstEntry.equals(EMPTY_PLAYLIST_ITEM)) {
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,
MediaType.getPlaylistType(MediaType.MUSIC));
if (numAlreadyQueued == 0) {
mc.playNext(MusicManager.this);
} else {
mc.playlistSetSong(MusicManager.this, numAlreadyQueued);
}
}
}
public void onWrongConnectionState(int state) {
// TODO Auto-generated method stub
}
public void downloadCover(DataResponse<Bitmap> response, ICoverArt cover,
int thumbSize, Context context) throws WifiStateException {
response.value = ClientFactory.getMusicClient(this, context).getCover(
this, cover, thumbSize);
}
}