/*
* 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.httpapi.client;
import java.util.HashMap;
import org.xbmc.api.business.INotifiableManager;
import org.xbmc.api.data.IControlClient;
import org.xbmc.api.info.GuiActions;
import org.xbmc.api.info.GuiSettings;
import org.xbmc.api.info.PlayStatus;
import org.xbmc.api.object.Host;
import org.xbmc.api.type.MediaType;
import org.xbmc.api.type.SeekType;
import org.xbmc.httpapi.Connection;
import org.xbmc.httpapi.WrongDataFormatException;
/**
* The ControlClient class takes care of everything related to controlling
* XBMC. These are essentially play controls, navigation controls other actions
* the user may wants to execute. It equally reads the information instead of
* setting it.
*
* @author Team XBMC
*/
public class ControlClient implements IControlClient {
private final Connection mConnection;
/**
* Class constructor needs reference to HTTP client connection
* @param connection
*/
public ControlClient(Connection connection) {
mConnection = connection;
}
/**
* Updates host info on the connection.
* @param host
*/
public void setHost(Host host) {
mConnection.setHost(host);
}
/**
* Adds a file or folder (<code>fileOrFolder</code> is either a file or a folder) to the current playlist.
* @param manager Manager reference
* @param fileOrFolder
* @return true on success, false otherwise.
*/
public boolean addToPlaylist(INotifiableManager manager, String fileOrFolder, int playlistId) {
return mConnection.getBoolean(manager, "AddToPlayList", fileOrFolder);
}
/**
* Starts playing the media file <code>filename</code> .
* @param manager Manager reference
* @param filename File to play
* @return true on success, false otherwise.
*/
public boolean playFile(INotifiableManager manager, String filename, int playlistId) {
return mConnection.getBoolean(manager, "PlayFile", filename);
}
/**
* Starts playing/showing the next media/image in the current playlist or,
* if currently showing a slideshow, the slideshow playlist.
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean playNext(INotifiableManager manager) {
return mConnection.getBoolean(manager, "PlayNext");
}
/**
* Starts playing/showing the previous media/image in the current playlist
* or, if currently showing a slidshow, the slideshow playlist.
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean playPrevious(INotifiableManager manager) {
return mConnection.getBoolean(manager, "PlayPrev");
}
/**
* Pauses the currently playing media.
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean pause(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Pause");
}
/**
* Stops the currently playing media.
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean stop(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Stop");
}
/**
* Start playing the media file at the given URL
* @param manager Manager reference
* @param url An URL pointing to a supported media file
* @return true on success, false otherwise.
*/
public boolean playUrl(INotifiableManager manager, String url) {
return mConnection.getBoolean(manager, "ExecBuiltin", "PlayMedia(" + url + ")");
}
/**
* Show the picture file <code>filename</code> .
* @param manager Manager reference
* @param filename File to show
* @return true on success, false otherwise.
*/
public boolean showPicture(INotifiableManager manager, String filename) {
mConnection.getBoolean(manager, "ClearSlideshow");
mConnection.getBoolean(manager, "PlaySlideshow", filename.substring(0, filename.replaceAll("\\\\", "/").lastIndexOf("/") ) + ";false");
mConnection.getBoolean(manager, "SlideshowSelect", filename );
return playNext(manager);
}
/**
* Send the string <code>text</code> via keys on the virtual keyboard.
* @param manager Manager reference
* @param text The text string to send.
* @return true on success, false otherwise.
*/
public boolean sendText(INotifiableManager manager, String text) {
final int codeOffset = 0xf100;
for (char c : text.toCharArray()) {
int code = (int)c+codeOffset;
if (! mConnection.getBoolean(manager, "SendKey", Integer.toString(code))) {
return false;
}
}
return true;
}
/**
* Sets the volume as a percentage of the maximum possible.
* @param manager Manager reference
* @param volume New volume (0-100)
* @return true on success, false otherwise.
*/
public boolean setVolume(INotifiableManager manager, int volume) {
return mConnection.getBoolean(manager, "SetVolume", String.valueOf(volume));
}
/**
* Seeks to a position. If type is
* <ul>
* <li><code>absolute</code> - Sets the playing position of the currently
* playing media as a percentage of the media�s length.</li>
* <li><code>relative</code> - Adds/Subtracts the current percentage on to
* the current position in the song</li>
* </ul>
*
* @param manager Manager reference
* @param type Seek type, relative or absolute
* @param progress Progress
* @return true on success, false otherwise.
*/
public boolean seek(INotifiableManager manager, SeekType type, int progress) {
if (type.compareTo(SeekType.absolute) == 0)
return mConnection.getBoolean(manager, "SeekPercentage", String.valueOf(progress));
else
return mConnection.getBoolean(manager, "SeekPercentageRelative", String.valueOf(progress));
}
/**
* Toggles the sound on/off.
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean mute(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Mute");
}
/**
* Retrieves the current playing position of the currently playing media as
* a percentage of the media's length.
* @param manager Manager reference
* @return Percentage (0-100)
*/
public int getPercentage(INotifiableManager manager) {
return mConnection.getInt(manager, "GetPercentage");
}
/**
* Retrieves the current volume setting as a percentage of the maximum
* possible value.
* @param manager Manager reference
* @return Volume (0-100)
*/
public int getVolume(INotifiableManager manager) {
return mConnection.getInt(manager, "GetVolume");
}
/**
* Navigates... UP!
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean navUp(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Action", String.valueOf(GuiActions.ACTION_MOVE_UP));
}
/**
* Navigates... DOWN!
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean navDown(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Action", String.valueOf(GuiActions.ACTION_MOVE_DOWN));
}
/**
* Navigates... LEFT!
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean navLeft(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Action", String.valueOf(GuiActions.ACTION_MOVE_LEFT));
}
/**
* Navigates... RIGHT!
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean navRight(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Action", String.valueOf(GuiActions.ACTION_MOVE_RIGHT));
}
/**
* Selects current item.
* @param manager Manager reference
* @return true on success, false otherwise.
*/
public boolean navSelect(INotifiableManager manager) {
return mConnection.getBoolean(manager, "Action", String.valueOf(GuiActions.ACTION_SELECT_ITEM));
}
/**
* Takes either "video" or "music" as a parameter to begin updating the
* corresponding database.
*
* TODO For "video" you can additionally specify a specific path to be scanned.
*
* @param manager Manager reference
* @param mediaType Either <code>video</code> or <code>music</code>.
* @return True on success, false otherwise.
*/
public boolean updateLibrary(INotifiableManager manager, String mediaType) {
return mConnection.getBoolean(manager, "ExecBuiltin", "UpdateLibrary(" + mediaType + ")");
}
/**
* Broadcast a message. Used to test broadcasting feature.
* @param manager Manager reference
* @param message
* @return True on success, false otherwise.
*/
public boolean broadcast(INotifiableManager manager, String message) {
return mConnection.getBoolean(manager, "Broadcast", message);
}
/**
* Returns the current broadcast port number, or 0 if deactivated.
* @param manager Manager reference
* @return Current broadcast port number.
*/
public int getBroadcast(INotifiableManager manager) {
final String ret[] = mConnection.getString(manager, "GetBroadcast").split(";");
try {
final int port = Integer.parseInt(ret[1]);
return port > 1 && !ret[0].equals("0") ? port : 0;
} catch (NumberFormatException e) {
return 0;
}
}
/**
* Sets the brodcast level and port. Level currently only takes three values:
* <ul>
* <li><code>0</code> - No broadcasts</li>
* <li><code>1</code> - Media playback and startup & shutdown events
* <li><code>2</code> - "OnAction" events (e.g. buttons) as well as level 1 events.
* </ul>
*
* @param manager Manager reference
* @param port Broadcast port
* @param level Broadcast level
* @return True on success, false otherwise.
*/
public boolean setBroadcast(INotifiableManager manager, int port, int level) {
return mConnection.getBoolean(manager, "SetBroadcast", level + ";" + port);
}
/**
* Returns current play state
* @param manager Manager reference
* @return
*/
public int getPlayState(INotifiableManager manager) {
return PlayStatus.parse(mConnection.getString(manager, "GetCurrentlyPlaying"));
}
/**
* Returns the current playlist identifier
* @param manager Manager reference
*/
public int getPlaylistId(INotifiableManager manager) {
return mConnection.getInt(manager, "GetCurrentPlaylist");
}
/**
* Sets the current playlist identifier
* @param manager Manager reference
* @param id Playlist identifier
* @return True on success, false otherwise.
*/
public boolean setPlaylistId(INotifiableManager manager, int id) {
return mConnection.getBoolean(manager, "SetCurrentPlaylist", String.valueOf(id));
}
/**
* Sets the current playlist position
* @param manager Manager reference
* @param position New playlist position
* @return True on success, false otherwise.
*/
public boolean setPlaylistPos(INotifiableManager manager, int playlistId, int position) {
return mConnection.getBoolean(manager, "SetPlaylistSong", String.valueOf(position));
}
/**
* Clears a playlist.
* @param manager Manager reference
* @param int Playlist to clear (0 = music, 1 = video)
* @return True on success, false otherwise.
*/
public boolean clearPlaylist(INotifiableManager manager, int playlistId) {
return mConnection.getBoolean(manager, "ClearPlayList", String.valueOf(playlistId));
}
/**
* Sets current playlist
* @param manager Manager reference
* @param playlistId Playlist ID ("0" = music, "1" = video)
* @return True on success, false otherwise.
*/
public boolean setCurrentPlaylist(INotifiableManager manager, int playlistId) {
return mConnection.getBoolean(manager, "SetCurrentPlaylist", String.valueOf(playlistId));
}
/**
* Sets the correct response format to default values
* @param manager Manager reference
* @return True on success, false otherwise.
*/
public boolean setResponseFormat(INotifiableManager manager) {
try {
StringBuilder sb = new StringBuilder();
sb.append("WebHeader;true;");
sb.append("WebFooter;true;");
sb.append("Header; ;");
sb.append("Footer; ;");
sb.append("OpenTag;");sb.append(Connection.LINE_SEP);sb.append(";");
sb.append("CloseTag;\n;");
sb.append("CloseFinalTag;false");
mConnection.assertBoolean(manager, "SetResponseFormat", sb.toString());
sb = new StringBuilder();
sb.append("OpenRecordSet; ;");
sb.append("CloseRecordSet; ;");
sb.append("OpenRecord; ;");
sb.append("CloseRecord; ;");
sb.append("OpenField;<field>;");
sb.append("CloseField;</field>");
mConnection.assertBoolean(manager, "SetResponseFormat", sb.toString());
return true;
} catch (WrongDataFormatException e) {
return false;
}
}
/**
* Sets the gui setting of XBMC to value
* @param manager
* @param setting see {@link org.xbmc.api.info.GuiSettings} for the available settings
* @param value the value to set
* @return {@code true} if the value was set successfully
*/
public boolean setGuiSetting(INotifiableManager manager, final int setting, final String value) {
return mConnection.getBoolean(manager, "SetGUISetting", GuiSettings.getType(setting) +
";" + GuiSettings.getName(setting) + ";" + value);
}
/**
* Returns state and type of the media currently playing.
* @return
*/
public ICurrentlyPlaying getCurrentlyPlaying(INotifiableManager manager) {
final HashMap<String, String> map = mConnection.getPairs(manager, "GetCurrentlyPlaying", " ; ; ;true");
final IControlClient.ICurrentlyPlaying nothingPlaying = new IControlClient.ICurrentlyPlaying() {
private static final long serialVersionUID = -1554068775915058884L;
public boolean isPlaying() { return false; }
public int getMediaType() { return 0; }
public int getPlaylistPosition() { return -1; }
public String getTitle() { return ""; }
public int getTime() { return 0; }
public int getPlayStatus() { return PlayStatus.STOPPED; }
public float getPercentage() { return 0; }
public String getFilename() { return ""; }
public int getDuration() { return 0; }
public String getArtist() { return ""; }
public String getAlbum() { return ""; }
public int getHeight() { return 0; }
public int getWidth() { return 0; }
};
if (map == null)
return nothingPlaying;
if (map.get("Filename") != null && map.get("Filename").contains("Nothing Playing")) {
return nothingPlaying;
} else {
//final int type = map.get("Type").contains("Audio") ? MediaType.MUSIC : (map.get("Type").contains("Video") ? MediaType.VIDEO : MediaType.PICTURES );
final int type;
if (map.containsKey("Type")) {
if (map.get("Type").contains("Audio"))
type = MediaType.MUSIC;
else if (map.get("Type").contains("Video")) {
if (map.get("Show Title")!= null && map.get("Episode") != null)
type = MediaType.VIDEO_TVEPISODE;
else
type = MediaType.VIDEO;
}
else
type = MediaType.PICTURES;
} else {
return nothingPlaying;
}
switch (type) {
case MediaType.MUSIC:
return MusicClient.getCurrentlyPlaying(map);
case MediaType.VIDEO:
return VideoClient.getCurrentlyPlaying(map);
case MediaType.VIDEO_TVEPISODE:
return TvShowClient.getCurrentlyPlaying(map);
case MediaType.PICTURES:
return PictureClient.getCurrentlyPlaying(map);
default:
return nothingPlaying;
}
}
}
}