Add support for using EventServer in remote d-pad

This commit is contained in:
Synced Synapse 2015-06-21 15:11:22 +01:00
commit 0563e80fe9
25 changed files with 2161 additions and 290 deletions

View File

@ -91,4 +91,8 @@ public interface Settings {
// Current host id
public static final String KEY_PREF_CURRENT_HOST_ID = "current_host_id";
public static final int DEFAULT_PREF_CURRENT_HOST_ID = -1;
public static final String KEY_PREF_CHECKED_EVENT_SERVER_CONNECTION = "checked_event_server_connection";
public static final boolean DEFAULT_PREF_CHECKED_EVENT_SERVER_CONNECTION = false;
}

View File

@ -0,0 +1,191 @@
/*
* 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.kore.eventclient;
/**
* Remote control and keyboard strings, taken from xbmc/ButtonTranslator.cpp
*
* @author Team XBMC
*/
public final class ButtonCodes {
/**
* "KB" => standard keyboard map ( <keyboard> section )
* "XG" => xbox gamepad map ( <gamepad> section )
* "R1" => xbox remote map ( <remote> section )
* "R2" => xbox universal remote map ( <universalremote> section )
* "LI:devicename" => LIRC remote map where 'devicename' is the actual device's name
*/
public static final String MAP_KEYBOARD = "KB";
public static final String MAP_GAMEPAD = "XG";
public static final String MAP_REMOTE = "R1";
public static final String MAP_UNIVERSAL_REMOTE = "R2";
public static final String REMOTE_LEFT = "left";
public static final String REMOTE_RIGHT = "right";
public static final String REMOTE_UP = "up";
public static final String REMOTE_DOWN = "down";
public static final String REMOTE_SELECT = "select";
public static final String REMOTE_BACK = "back";
public static final String REMOTE_MENU = "menu";
public static final String REMOTE_INFO = "info";
public static final String REMOTE_DISPLAY = "display";
public static final String REMOTE_TITLE = "title";
public static final String REMOTE_PLAY = "play";
public static final String REMOTE_PAUSE = "pause";
public static final String REMOTE_REVERSE = "reverse";
public static final String REMOTE_FORWARD = "forward";
public static final String REMOTE_SKIP_PLUS = "skipplus";
public static final String REMOTE_SKIP_MINUS = "skipminus";
public static final String REMOTE_STOP = "stop";
public static final String REMOTE_0 = "zero";
public static final String REMOTE_1 = "one";
public static final String REMOTE_2 = "two";
public static final String REMOTE_3 = "three";
public static final String REMOTE_4 = "four";
public static final String REMOTE_5 = "five";
public static final String REMOTE_6 = "six";
public static final String REMOTE_7 = "seven";
public static final String REMOTE_8 = "eight";
public static final String REMOTE_9 = "nine";
// additional keys from the media center extender for xbox remote
public static final String REMOTE_POWER = "power";
public static final String REMOTE_MY_TV = "mytv";
public static final String REMOTE_MY_MUSIC = "mymusic";
public static final String REMOTE_MY_PICTURES = "mypictures";
public static final String REMOTE_MY_VIDEOS = "myvideo";
public static final String REMOTE_RECORD = "record";
public static final String REMOTE_START = "start";
public static final String REMOTE_VOLUME_PLUS = "volumeplus";
public static final String REMOTE_VOLUME_MINUS = "volumeminus";
public static final String REMOTE_CHANNEL_PLUS = "channelplus";
public static final String REMOTE_CHANNEL_MINUS = "channelminus";
public static final String REMOTE_PAGE_PLUS = "pageplus";
public static final String REMOTE_PAGE_MINUS = "pageminus";
public static final String REMOTE_MUTE = "mute";
public static final String REMOTE_RECORDED_TV = "recordedtv";
public static final String REMOTE_GUIDE = "guide";
public static final String REMOTE_LIVE_TV = "livetv";
public static final String REMOTE_STAR = "star";
public static final String REMOTE_HASH = "hash";
public static final String REMOTE_CLEAR = "clear";
public static final String REMOTE_ENTER = "enter";
public static final String REMOTE_XBOX = "xbox";
public static final String KEYBOARD_RETURN = "return";
public static final String KEYBOARD_ENTER = "enter";
public static final String KEYBOARD_ESCAPE = "escape";
public static final String KEYBOARD_ESC = "esc";
public static final String KEYBOARD_TAB = "tab";
public static final String KEYBOARD_SPACE = "space";
public static final String KEYBOARD_LEFT = "left";
public static final String KEYBOARD_RIGHT = "right";
public static final String KEYBOARD_UP = "up";
public static final String KEYBOARD_DOWN = "down";
public static final String KEYBOARD_INSERT = "insert";
public static final String KEYBOARD_DELETE = "delete";
public static final String KEYBOARD_HOME = "home";
public static final String KEYBOARD_END = "end";
public static final String KEYBOARD_F1 = "f1";
public static final String KEYBOARD_F2 = "f2";
public static final String KEYBOARD_F3 = "f3";
public static final String KEYBOARD_F4 = "f4";
public static final String KEYBOARD_F5 = "f5";
public static final String KEYBOARD_F6 = "f6";
public static final String KEYBOARD_F7 = "f7";
public static final String KEYBOARD_F8 = "f8";
public static final String KEYBOARD_F9 = "f9";
public static final String KEYBOARD_F10 = "f10";
public static final String KEYBOARD_F11 = "f11";
public static final String KEYBOARD_F12 = "f12";
public static final String KEYBOARD_NUMPAD_ZERO = "numpadzero";
public static final String KEYBOARD_NUMPAD_1 = "numpadone";
public static final String KEYBOARD_NUMPAD_2 = "numpadtwo";
public static final String KEYBOARD_NUMPAD_3 = "numpadthree";
public static final String KEYBOARD_NUMPAD_4 = "numpadfour";
public static final String KEYBOARD_NUMPAD_5 = "numpadfive";
public static final String KEYBOARD_NUMPAD_6 = "numpadsix";
public static final String KEYBOARD_NUMPAD_7 = "numpadseven";
public static final String KEYBOARD_NUMPAD_8 = "numpadeight";
public static final String KEYBOARD_NUMPAD_9 = "numpadnine";
public static final String KEYBOARD_NUMPAD_TIMES = "numpadtimes";
public static final String KEYBOARD_NUMPAD_PLUS = "numpadplus";
public static final String KEYBOARD_NUMPAD_MINUS = "numpadminus";
public static final String KEYBOARD_NUMPAD_PERIOD = "numpadperiod";
public static final String KEYBOARD_NUMPAD_DIVIDE = "numpaddivide";
public static final String KEYBOARD_PAGEUP = "pageup";
public static final String KEYBOARD_PAGEDOWN = "pagedown";
public static final String KEYBOARD_PRINTSCREEN = "printscreen";
public static final String KEYBOARD_BACKSPACE = "backspace";
public static final String KEYBOARD_MENU = "menu";
public static final String KEYBOARD_PAUSE = "pause";
public static final String KEYBOARD_LEFTSHIFT = "leftshift";
public static final String KEYBOARD_RIGHTSHIFT = "rightshift";
public static final String KEYBOARD_LEFTCTRL = "leftctrl";
public static final String KEYBOARD_RIGHTCTRL = "rightctrl";
public static final String KEYBOARD_LEFTALT = "leftalt";
public static final String KEYBOARD_RIGHTALT = "rightalt";
public static final String KEYBOARD_LEFTWINDOWS = "leftwindows";
public static final String KEYBOARD_RIGHTWINDOWS = "rightwindows";
public static final String KEYBOARD_CAPSLOCK = "capslock";
public static final String KEYBOARD_NUMLOCK = "numlock";
public static final String KEYBOARD_SCROLLLOCK = "scrolllock";
public static final String KEYBOARD_SEMICOLON = "semicolon";
public static final String KEYBOARD_COLON = "colon";
public static final String KEYBOARD_EQUALS = "equals";
public static final String KEYBOARD_PLUS = "plus";
public static final String KEYBOARD_COMMA = "comma";
public static final String KEYBOARD_LESSTHAN = "lessthan";
public static final String KEYBOARD_MINUS = "minus";
public static final String KEYBOARD_UNDERLINE = "underline";
public static final String KEYBOARD_PERIOD = "period";
public static final String KEYBOARD_GREATERTHAN = "greaterthan";
public static final String KEYBOARD_FORWARDSLASH = "forwardslash";
public static final String KEYBOARD_QUESTIONMARK = "questionmark";
public static final String KEYBOARD_LEFTQUOTE = "leftquote";
public static final String KEYBOARD_TILDE = "tilde";
public static final String KEYBOARD_OPENSQUAREBRACKET = "opensquarebracket";
public static final String KEYBOARD_OPENBRACE = "openbrace";
public static final String KEYBOARD_BACKSLASH = "backslash";
public static final String KEYBOARD_PIPE = "pipe";
public static final String KEYBOARD_CLOSESQUAREBRACKET = "closesquarebracket";
public static final String KEYBOARD_CLOSEBRACE = "closebrace";
public static final String KEYBOARD_QUOTE = "quote";
public static final String KEYBOARD_DOUBLEQUOTE = "doublequote";
public static final String KEYBOARD_LAUNCH_MAIL = "launch_mail";
public static final String KEYBOARD_BROWSER_HOME = "browser_home";
public static final String KEYBOARD_BROWSER_FAVORITES = "browser_favorites";
public static final String KEYBOARD_BROWSER_REFRESH = "browser_refresh";
public static final String KEYBOARD_BROWSER_SEARCH = "browser_search";
public static final String KEYBOARD_LAUNCH_APP1_PC_ICON = "launch_app1_pc_icon";
public static final String KEYBOARD_LAUNCH_MEDIA_SELECT = "launch_media_select";
public static final String KEYBOARD_PLAY_PAUSE = "play_pause";
public static final String KEYBOARD_STOP = "stop";
public static final String KEYBOARD_VOLUME_UP = "volume_up";
public static final String KEYBOARD_VOLUME_MUTE = "volume_mute";
public static final String KEYBOARD_VOLUME_DOWN = "volume_down";
public static final String KEYBOARD_PREV_TRACK = "prev_track";
public static final String KEYBOARD_NEXT_TRACK = "next_track";
public static final String GAMEPAD_LEFT_ANALOG_TRIGGER = "leftanalogtrigger";
public static final String GAMEPAD_RIGHT_ANALOG_TRIGGER = "rightanalogtrigger";
}

View File

@ -0,0 +1,317 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
/**
* Event Client Class
*
* Implements an XBMC-Client. This class can be used to implement your own application which
* should act as a Input device for XBMC. Also starts a Ping-Thread, which tells the XBMC EventServer
* that the client is alive. Therefore if you close your application you SHOULD call stopClient()!
* @author Stefan Agner
*
*/
public class EventClient
{
private boolean hasIcon = false;
private String deviceName;
private PingThread oPingThread;
private byte iconType = Packet.ICON_PNG;
private byte[] iconData;
private InetAddress hostAddress;
private int hostPort;
/**
* Starts a XBMC EventClient.
* @param hostAddress Address of the Host running XBMC
* @param hostPort Port of the Host running XBMC (default 9777)
* @param deviceName Name of the Device
* @param iconFile Path to the Iconfile (PNG, JPEG or GIF)
* @throws IOException
*/
public EventClient(InetAddress hostAddress, int hostPort, String deviceName, String iconFile) throws IOException
{
byte iconType = Packet.ICON_PNG;
// Assume png as icon type
if(iconFile.toLowerCase().endsWith(".jpeg"))
iconType = Packet.ICON_JPEG;
if(iconFile.toLowerCase().endsWith(".jpg"))
iconType = Packet.ICON_JPEG;
if(iconFile.toLowerCase().endsWith(".gif"))
iconType = Packet.ICON_GIF;
// Read the icon file to the byte array...
FileInputStream iconFileStream = new FileInputStream(iconFile);
byte[] iconData = new byte[iconFileStream.available()];
iconFileStream.read(iconData);
hasIcon = true;
// Call start-Method...
startClient(hostAddress, hostPort, deviceName, iconType, iconData);
}
/**
* Starts a XBMC EventClient.
* @param hostAddress Address of the Host running XBMC
* @param hostPort Port of the Host running XBMC (default 9777)
* @param deviceName Name of the Device
* @param iconType Type of the icon file (see Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
* @param iconData The icon itself as a Byte-Array
* @throws IOException
*/
public EventClient(InetAddress hostAddress, int hostPort, String deviceName, byte iconType, byte[] iconData) throws IOException
{
hasIcon = true;
startClient(hostAddress, hostPort, deviceName, iconType, iconData);
}
/**
* Starts a XBMC EventClient without an icon.
* @param hostAddress Address of the Host running XBMC
* @param hostPort Port of the Host running XBMC (default 9777)
* @param deviceName Name of the Device
* @throws IOException
*/
public EventClient(InetAddress hostAddress, int hostPort, String deviceName) throws IOException
{
hasIcon = false;
byte iconType = Packet.ICON_NONE;
byte[] iconData = null;
startClient(hostAddress, hostPort, deviceName, iconType, iconData);
}
/**
* Starts a XBMC EventClient.
* @param hostAddress Address of the Host running XBMC
* @param hostPort Port of the Host running XBMC (default 9777)
* @param deviceName Name of the Device
* @param iconType Type of the icon file (see Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
* @param iconData The icon itself as a Byte-Array
* @throws IOException
*/
private void startClient(InetAddress hostAddress, int hostPort, String deviceName, byte iconType, byte[] iconData) throws IOException
{
// Save host address and port
this.hostAddress = hostAddress;
this.hostPort = hostPort;
this.deviceName = deviceName;
this.iconType = iconType;
this.iconData = iconData;
// Send Hello Packet...
PacketHELO p;
if(hasIcon)
p = new PacketHELO(deviceName, iconType, iconData);
else
p = new PacketHELO(deviceName);
p.send(hostAddress, hostPort);
// Start Thread (for Ping packets...)
oPingThread = new PingThread(hostAddress, hostPort, 20000);
oPingThread.start();
}
/**
* Stops the XBMC EventClient (especially the Ping-Thread)
* @throws IOException
*/
public void stopClient() throws IOException
{
// Stop Ping-Thread...
oPingThread.giveup();
oPingThread.interrupt();
PacketBYE p = new PacketBYE();
p.send(hostAddress, hostPort);
}
/**
* Displays a notification window in XBMC.
* @param title Message title
* @param message The actual message
*/
public void sendNotification(String title, String message) throws IOException
{
PacketNOTIFICATION p;
if(hasIcon)
p = new PacketNOTIFICATION(title, message, iconType, iconData);
else
p = new PacketNOTIFICATION(title, message);
p.send(hostAddress, hostPort);
}
/**
* Sends a Button event
* @param code raw button code (default: 0)
* @param repeat this key press should repeat until released (default: 1)
* Note that queued pressed cannot repeat.
* @param down if this is 1, it implies a press event, 0 implies a release
* event. (default: 1)
* @param queue a queued key press means that the button event is
* executed just once after which the next key press is processed.
* It can be used for macros. Currently there is no support for
* time delays between queued presses. (default: 0)
* @param amount unimplemented for now; in the future it will be used for
* specifying magnitude of analog key press events
* @param axis
*/
public void sendButton(short code, boolean repeat, boolean down, boolean queue, short amount, byte axis) throws IOException
{
PacketBUTTON p = new PacketBUTTON(code, repeat, down, queue, amount, axis);
p.send(hostAddress, hostPort);
}
/**
* Sends a Button event
* @param map_name a combination of map_name and button_name refers to a
* mapping in the user's Keymap.xml or Lircmap.xml.
* map_name can be one of the following:
* <ul>
* <li>"KB" => standard keyboard map ( <keyboard> section )</li>
* <li>"XG" => xbox gamepad map ( <gamepad> section )</li>
* <li>"R1" => xbox remote map ( <remote> section )</li>
* <li>"R2" => xbox universal remote map ( <universalremote> section )</li>
* <li>"LI:devicename" => LIRC remote map where 'devicename' is the
* actual device's name</li></ul>
* @param button_name a button name defined in the map specified in map_name.
* For example, if map_name is "KB" refering to the <keyboard> section in Keymap.xml
* then, valid button_names include "printscreen", "minus", "x", etc.
* @param repeat this key press should repeat until released (default: 1)
* Note that queued pressed cannot repeat.
* @param down if this is 1, it implies a press event, 0 implies a release
* event. (default: 1)
* @param queue a queued key press means that the button event is
* executed just once after which the next key press is processed.
* It can be used for macros. Currently there is no support for
* time delays between queued presses. (default: 0)
* @param amount unimplemented for now; in the future it will be used for
* specifying magnitude of analog key press events
* @param axis
*/
public void sendButton(String map_name, String button_name, boolean repeat, boolean down, boolean queue, short amount, byte axis) throws IOException
{
PacketBUTTON p = new PacketBUTTON(map_name, button_name, repeat, down, queue, amount, axis);
p.send(hostAddress, hostPort);
}
/**
* Sets the mouse position in XBMC
* @param x horitontal position ranging from 0 to 65535
* @param y vertical position ranging from 0 to 65535
*/
public void sendMouse(int x, int y) throws IOException
{
PacketMOUSE p = new PacketMOUSE(x, y);
p.send(hostAddress, hostPort);
}
/**
* Sends a ping to the XBMC EventServer
* @throws IOException
*/
public void ping() throws IOException
{
PacketPING p = new PacketPING();
p.send(hostAddress, hostPort);
}
/**
* Tells XBMC to log the message to xbmc.log with the loglevel as specified.
* @param loglevel the loglevel, follows XBMC standard.
* <ul>
* <li>0 = DEBUG</li>
* <li>1 = INFO</li>
* <li>2 = NOTICE</li>
* <li>3 = WARNING</li>
* <li>4 = ERROR</li>
* <li>5 = SEVERE</li>
* </ul>
* @param logmessage the message to log
*/
public void sendLog(byte loglevel, String logmessage) throws IOException
{
PacketLOG p = new PacketLOG(loglevel, logmessage);
p.send(hostAddress, hostPort);
}
/**
* Tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
* @param actionmessage Actionmessage (as in scripting/skinning)
*/
public void sendAction(String actionmessage) throws IOException
{
PacketACTION p = new PacketACTION(actionmessage);
p.send(hostAddress, hostPort);
}
/**
* Implements a PingThread which tells XBMC EventServer that the Client is alive (this should
* be done at least every 60 seconds!
* @author Stefan Agner
*
*/
class PingThread extends Thread
{
private InetAddress hostAddress;
private int hostPort;
private int sleepTime;
private boolean giveup = false;
public PingThread(InetAddress hostAddress, int hostPort, int sleepTime)
{
super("XBMC EventClient Ping-Thread");
this.hostAddress = hostAddress;
this.hostPort = hostPort;
this.sleepTime = sleepTime;
}
public void giveup()
{
giveup = true;
}
public void run()
{
while(!giveup)
{
try {
PacketPING p = new PacketPING();
p.send(hostAddress, hostPort);
} catch (IOException e) {
e.printStackTrace();
}
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
}
}
}

View File

@ -0,0 +1,278 @@
/*
* Copyright 2015 Synced Synapse. All rights reserved.
*
* Licensed 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.
*/
package org.xbmc.kore.eventclient;
import android.annotation.SuppressLint;
import android.os.*;
import android.os.Process;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.HostConnection;
import org.xbmc.kore.jsonrpc.method.Application;
import org.xbmc.kore.jsonrpc.type.ApplicationType;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.Utils;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Class that establishes and maintains a connection to Kodi's EventServer
* This class keeps pinging Kodi to keep the connection alive and contains
* auxiliary methods that allow the sending of packets to Kodi.
* Make sure to call quit() when done with it, so that it gracefully shuts down
*/
public class EventServerConnection {
private static final String TAG = LogUtils.makeLogTag(EventServerConnection.class);
private static final int PING_INTERVAL = 45000; // ms
private static final String DEVICE_NAME = "Kore Remote";
/**
* Host to connect too
*/
private final HostInfo hostInfo;
private InetAddress hostInetAddress = null;
// Handler on which packets will be posted, to send them asynchronously
private Handler commHandler = null;
private HandlerThread handlerThread = null;
private PacketPING packetPING = new PacketPING();
private Runnable pingRunnable = new Runnable() {
@Override
public void run() {
LogUtils.LOGD(TAG, "Pinging EventServer");
if (hostInetAddress != null) {
try {
packetPING.send(hostInetAddress, hostInfo.getEventServerPort());
} catch (IOException exc) {
LogUtils.LOGD(TAG, "Got an IOException when sending a PING Packet to Kodi's EventServer");
}
}
commHandler.postDelayed(this, PING_INTERVAL);
}
};
/**
* Interface to notify users if the connection was successful
*/
public interface EventServerConnectionCallback {
void OnConnectResult(boolean success);
}
/**
* Constructor. Starts the thread that keeps the connection alive. Make sure to call quit() when done.
* @param hostInfo Host to connect to
*/
public EventServerConnection(final HostInfo hostInfo, final EventServerConnectionCallback callback) {
this.hostInfo = hostInfo;
LogUtils.LOGD(TAG, "Starting EventServer Thread");
// Handler thread that will keep pinging and send the requests to Kodi
handlerThread = new HandlerThread("EventServerConnection", Process.THREAD_PRIORITY_DEFAULT);
handlerThread.start();
// Get the HandlerThread's Looper and use it for our Handler
commHandler = new Handler(handlerThread.getLooper());
// Now, get the host InetAddress in the background
commHandler.post(new Runnable() {
@Override
public void run() {
try {
hostInetAddress = InetAddress.getByName(hostInfo.getAddress());
} catch (UnknownHostException exc) {
LogUtils.LOGD(TAG, "Got an UnknownHostException, disabling EventServer");
hostInetAddress = null;
}
callback.OnConnectResult(hostInetAddress != null);
}
});
// Start pinging
commHandler.postDelayed(pingRunnable, PING_INTERVAL);
}
/**
* Stops the HandlerThread that is being used to send packets to Kodi
*/
public void quit() {
LogUtils.LOGD(TAG, "Quiting EventServer handler thread");
quitHandlerThread(handlerThread);
}
/**
* Sends a packet to Kodi's Event Server
* Only sends the packet if connected, i.e. if quit() has not been not called
* @param p Packet to send
*/
public void sendPacket(final Packet p) {
if (!handlerThread.isAlive() || (hostInetAddress == null)) {
return;
}
LogUtils.LOGD(TAG, "Sending Packet");
commHandler.post(new Runnable() {
@Override
public void run() {
try {
p.send(hostInetAddress, hostInfo.getEventServerPort());
} catch (IOException exc) {
LogUtils.LOGD(TAG, "Got an IOException when sending a packet to Kodi's EventServer");
}
}
});
}
/**
* Establishes a connection to the EventServer and reports the result
* @param hostInfo Host to connect to
* @param callerCallback Callback on which to post the result
* @param callerHandler Handler on which to post the callback call
*/
public static void testEventServerConnection(final HostInfo hostInfo,
final EventServerConnectionCallback callerCallback,
final Handler callerHandler) {
final HandlerThread auxThread = new HandlerThread("EventServerConnectionTest", Process.THREAD_PRIORITY_DEFAULT);
auxThread.start();
// Get the HandlerThread's Looper and use it for our Handler
final Handler auxHandler = new Handler(auxThread.getLooper());
auxHandler.post(new Runnable() {
@Override
public void run() {
// Get the InetAddress
final InetAddress hostInetAddress;
try {
hostInetAddress = InetAddress.getByName(hostInfo.getAddress());
} catch (UnknownHostException exc) {
LogUtils.LOGD(TAG, "Couldn't get host InetAddress");
reportTestResult(callerHandler, callerCallback, false);
quitHandlerThread(auxThread);
return;
}
// Send a HELO packet
Packet p = new PacketHELO(DEVICE_NAME);
try {
p.send(hostInetAddress, hostInfo.getEventServerPort());
} catch (IOException exc) {
LogUtils.LOGD(TAG, "Couldn't send HELO packet to host");
reportTestResult(callerHandler, callerCallback, false);
quitHandlerThread(auxThread);
return;
}
// The previous checks don't really test the connection, as this is UDP. Apart from checking if
// any HostUnreachable ICMP message is returned (which may or may not happen), there's no direct way
// to check if the messages were delivered, so the solution is to force something to happen on
// Kodi and them read Kodi's state to check if it was applied.
// We are going to get the mute status of Kodi via jsonrpc, change it via EventServer and check if
// it was changed via jsonrpc, reverting it back afterwards
final HostConnection auxHostConnection = new HostConnection(
new HostInfo(hostInfo.getName(), hostInfo.getAddress(),
HostConnection.PROTOCOL_HTTP, hostInfo.getHttpPort(), hostInfo.getTcpPort(),
hostInfo.getUsername(), hostInfo.getPassword(), false, 0));
final Application.GetProperties action = new Application.GetProperties(Application.GetProperties.MUTED);
final Packet mutePacket = new PacketBUTTON(ButtonCodes.MAP_REMOTE, ButtonCodes.REMOTE_MUTE,
false, true, true, (short) 0, (byte) 0);
// Get the initial mute status
action.execute(auxHostConnection, new ApiCallback<ApplicationType.PropertyValue>() {
@Override
public void onSuccess(ApplicationType.PropertyValue result) {
final boolean initialMuteStatus = result.muted;
// Switch mute status
try {
mutePacket.send(hostInetAddress, hostInfo.getEventServerPort());
} catch (IOException exc) {
LogUtils.LOGD(TAG, "Couldn't send first MUTE packet to host");
reportTestResult(callerHandler, callerCallback, false);
quitHandlerThread(auxThread);
return;
}
// Sleep a while to make sure the previous command was executed
try {
Thread.sleep(2000);
} catch (InterruptedException exc) {
// Ignore
}
// Now get the new status and compare
action.execute(auxHostConnection, new ApiCallback<ApplicationType.PropertyValue>() {
@Override
public void onSuccess(ApplicationType.PropertyValue result) {
// Report result (mute status is different)
reportTestResult(callerHandler, callerCallback, initialMuteStatus != result.muted);
// Revert mute status
try {
mutePacket.send(hostInetAddress, hostInfo.getEventServerPort());
} catch (IOException exc) {
LogUtils.LOGD(TAG, "Couldn't revert MUTE status");
}
quitHandlerThread(auxThread);
}
@Override
public void onError(int errorCode, String description) {
LogUtils.LOGD(TAG, "Got an error on Application.GetProperties: " + description);
reportTestResult(callerHandler, callerCallback, false);
quitHandlerThread(auxThread);
}
}, auxHandler);
}
@Override
public void onError(int errorCode, String description) {
LogUtils.LOGD(TAG, "Got an error on Application.GetProperties: " + description);
reportTestResult(callerHandler, callerCallback, false);
quitHandlerThread(auxThread);
}
}, auxHandler);
}
});
}
private static void reportTestResult(final Handler callerHandler,
final EventServerConnectionCallback callback,
final boolean result) {
callerHandler.post(new Runnable() {
@Override
public void run() {
callback.OnConnectResult(result);
}
});
}
@SuppressLint("NewApi")
private static void quitHandlerThread(HandlerThread handlerThread) {
if (Utils.isJellybeanMR2OrLater()) {
handlerThread.quitSafely();
} else {
handlerThread.quit();
}
}
}

View File

@ -0,0 +1,289 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* XBMC Event Client Class
* <p>
* Implementation of XBMC's UDP based input system.
* A set of classes that abstract the various packets that the event server
* currently supports. In addition, there's also a class, XBMCClient, that
* provides functions that sends the various packets. Use XBMCClient if you
* don't need complete control over packet structure.
* </p>
* <p>
* The basic workflow involves:
* <ol>
* <li>Send a HELO packet</li>
* <li>Send x number of valid packets</li>
* <li>Send a BYE packet</li>
* </ol>
* </p>
* <p>
* IMPORTANT NOTE ABOUT TIMEOUTS:
* A client is considered to be timed out if XBMC doesn't received a packet
* at least once every 60 seconds. To "ping" XBMC with an empty packet use
* PacketPING or XBMCClient.ping(). See the documentation for details.
* </p>
* <p>
* Base class that implements a single event packet.
* - Generic packet structure (maximum 1024 bytes per packet)
* - Header is 32 bytes long, so 992 bytes available for payload
* - large payloads can be split into multiple packets using H4 and H5
* H5 should contain total no. of packets in such a case
* - H6 contains length of P1, which is limited to 992 bytes
* - if H5 is 0 or 1, then H4 will be ignored (single packet msg)
* - H7 must be set to zeros for now
* </p>
* <pre>
* -----------------------------
* | -H1 Signature ("XBMC") | - 4 x CHAR 4B
* | -H2 Version (eg. 2.0) | - 2 x UNSIGNED CHAR 2B
* | -H3 PacketType | - 1 x UNSIGNED SHORT 2B
* | -H4 Sequence number | - 1 x UNSIGNED LONG 4B
* | -H5 No. of packets in msg | - 1 x UNSIGNED LONG 4B
* | -H6 Payloadsize of packet | - 1 x UNSIGNED SHORT 2B
* | -H7 Client's unique token | - 1 x UNSIGNED LONG 4B
* | -H8 Reserved | - 10 x UNSIGNED CHAR 10B
* |---------------------------|
* | -P1 payload | -
* -----------------------------
* </pre>
* @author Stefan Agner
*
*/
public abstract class Packet {
private byte[] sig;
private byte[] payload = new byte[0];
private byte minver;
private byte majver;
private short packettype;
private final static short MAX_PACKET_SIZE = 1024;
private final static short HEADER_SIZE = 32;
private final static short MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - HEADER_SIZE;
protected final static byte PT_HELO = 0x01;
protected final static byte PT_BYE = 0x02;
protected final static byte PT_BUTTON = 0x03;
protected final static byte PT_MOUSE = 0x04;
protected final static byte PT_PING = 0x05;
protected final static byte PT_BROADCAST = 0x06;
protected final static byte PT_NOTIFICATION = 0x07;
protected final static byte PT_BLOB = 0x08;
protected final static byte PT_LOG = 0x09;
protected final static byte PT_ACTION = 0x0A;
protected final static byte PT_DEBUG = (byte)0xFF;
public final static byte ICON_NONE = 0x00;
public final static byte ICON_JPEG = 0x01;
public final static byte ICON_PNG = 0x02;
public final static byte ICON_GIF = 0x03;
private static int uid = (int)(Math.random()*Integer.MAX_VALUE);
/**
* This is an Abstract class and cannot be instanced. Please use one of the Packet implementation Classes
* (PacketXXX).
*
* Implements an XBMC Event Client Packet. Type is to be specified at creation time, Payload can be added
* with the various appendPayload methods. Packet can be sent through UDP-Socket with method "send".
* @param packettype Type of Packet (PT_XXX)
*/
protected Packet(short packettype)
{
sig = new byte[] {'X', 'B', 'M', 'C' };
minver = 0;
majver = 2;
this.packettype = packettype;
}
/**
* Appends a String to the payload (terminated with 0x00)
* @param payload Payload as String
*/
protected void appendPayload(String payload)
{
byte[] payloadarr = payload.getBytes();
int oldpayloadsize = this.payload.length;
byte[] oldpayload = this.payload;
this.payload = new byte[oldpayloadsize+payloadarr.length+1]; // Create new Array with more place (+1 for string terminator)
System.arraycopy(oldpayload, 0, this.payload, 0, oldpayloadsize);
System.arraycopy(payloadarr, 0, this.payload, oldpayloadsize, payloadarr.length);
}
/**
* Appends a single Byte to the payload
* @param payload Payload
*/
protected void appendPayload(byte payload)
{
appendPayload(new byte[] { payload });
}
/**
* Appends a Byte-Array to the payload
* @param payloadarr Payload
*/
protected void appendPayload(byte[] payloadarr)
{
int oldpayloadsize = this.payload.length;
byte[] oldpayload = this.payload;
this.payload = new byte[oldpayloadsize+payloadarr.length];
System.arraycopy(oldpayload, 0, this.payload, 0, oldpayloadsize);
System.arraycopy(payloadarr, 0, this.payload, oldpayloadsize, payloadarr.length);
}
/**
* Appends an integer to the payload
* @param i Payload
*/
protected void appendPayload(int i) {
appendPayload(intToByteArray(i));
}
/**
* Appends a short to the payload
* @param s Payload
*/
protected void appendPayload(short s) {
appendPayload(shortToByteArray(s));
}
/**
* Get Number of Packets which will be sent with current Payload...
* @return Number of Packets
*/
public int getNumPackets()
{
// return (payload.length + (MAX_PAYLOAD_SIZE - 1)) / MAX_PAYLOAD_SIZE;
return 1 + Math.max(payload.length - 1, 0) / MAX_PAYLOAD_SIZE;
}
/**
* Get Header for a specific Packet in this sequence...
* @param seq Current sequence number
* @param maxseq Maximal sequence number
* @param actpayloadsize Payloadsize of this packet
* @return Byte-Array with Header information (currently 32-Byte long, see HEADER_SIZE)
*/
private byte[] getHeader(int seq, int maxseq, short actpayloadsize)
{
byte[] header = new byte[HEADER_SIZE];
System.arraycopy(sig, 0, header, 0, 4);
header[4] = majver;
header[5] = minver;
byte[] packettypearr = shortToByteArray(this.packettype);
System.arraycopy(packettypearr, 0, header, 6, 2);
byte[] seqarr = intToByteArray(seq);
System.arraycopy(seqarr, 0, header, 8, 4);
byte[] maxseqarr = intToByteArray(maxseq);
System.arraycopy(maxseqarr, 0, header, 12, 4);
byte[] payloadsize = shortToByteArray(actpayloadsize);
System.arraycopy(payloadsize, 0, header, 16, 2);
byte[] uid = intToByteArray(Packet.uid);
System.arraycopy(uid, 0, header, 18, 4);
byte[] reserved = new byte[10];
System.arraycopy(reserved, 0, header, 22, 10);
return header;
}
/**
* Generates the whole UDP-Message with Header and Payload of a specific Packet in sequence
* @param seq Current sequence number
* @return Byte-Array with UDP-Message
*/
private byte[] getUDPMessage(int seq)
{
int maxseq = getNumPackets();
if(seq > maxseq)
return null;
short actpayloadsize;
if(seq == maxseq)
actpayloadsize = (short)((payload.length - 1) % MAX_PAYLOAD_SIZE + 1);
else
actpayloadsize = (short)MAX_PAYLOAD_SIZE;
byte[] pack = new byte[HEADER_SIZE+actpayloadsize];
System.arraycopy(getHeader(seq, maxseq, actpayloadsize), 0, pack, 0, HEADER_SIZE);
System.arraycopy(payload, (seq-1)*MAX_PAYLOAD_SIZE, pack, HEADER_SIZE, actpayloadsize);
return pack;
}
/**
* Sends this packet to the EventServer
* @param adr Address of the EventServer
* @param port Port of the EventServer
* @throws IOException
*/
public void send(InetAddress adr, int port) throws IOException
{
int maxseq = getNumPackets();
DatagramSocket s = new DatagramSocket();
// For each Packet in Sequence...
for(int seq=1;seq<=maxseq;seq++)
{
// Get Message and send them...
byte[] pack = getUDPMessage(seq);
DatagramPacket p = new DatagramPacket(pack, pack.length);
p.setAddress(adr);
p.setPort(port);
s.send(p);
}
}
/**
* Helper Method to convert an integer to a Byte array
* @param value
* @return Byte-Array
*/
private static final byte[] intToByteArray(int value) {
return new byte[] {
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value};
}
/**
* Helper Method to convert an short to a Byte array
* @param value
* @return Byte-Array
*/
private static final byte[] shortToByteArray(short value) {
return new byte[] {
(byte)(value >>> 8),
(byte)value};
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
/**
* XBMC Event Client Class
*
* An ACTION packet tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
* The idea is that this will be as in scripting/skining and keymapping, just triggered from afar.
* @author Stefan Agner
*
*/
public class PacketACTION extends Packet {
public final static byte ACTION_EXECBUILTIN = 0x01;
public final static byte ACTION_BUTTON = 0x02;
/**
* An ACTION packet tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
* @param actionmessage Actionmessage (as in scripting/skinning)
*/
public PacketACTION(String actionmessage)
{
super(PT_ACTION);
byte actiontype = ACTION_EXECBUILTIN;
appendPayload(actionmessage, actiontype);
}
/**
* An ACTION packet tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
* @param actionmessage Actionmessage (as in scripting/skinning)
* @param actiontype Actiontype (ACTION_EXECBUILTIN or ACTION_BUTTON)
*/
public PacketACTION(String actionmessage, byte actiontype)
{
super(PT_ACTION);
appendPayload(actionmessage, actiontype);
}
private void appendPayload(String actionmessage, byte actiontype)
{
appendPayload(actiontype);
appendPayload(actionmessage);
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
/**
* XBMC Event Client Class
*
* A button packet send a key press or release event to XBMC
* @author Stefan Agner
*
*/
public class PacketBUTTON extends Packet {
protected final static byte BT_USE_NAME = 0x01;
protected final static byte BT_DOWN = 0x02;
protected final static byte BT_UP = 0x04;
protected final static byte BT_USE_AMOUNT = 0x08;
protected final static byte BT_QUEUE = 0x10;
protected final static byte BT_NO_REPEAT = 0x20;
protected final static byte BT_VKEY = 0x40;
protected final static byte BT_AXIS = (byte)0x80;
protected final static byte BT_AXISSINGLE = (byte)0x100;
/**
* A button packet send a key press or release event to XBMC
* @param code raw button code (default: 0)
* @param repeat this key press should repeat until released (default: 1)
* Note that queued pressed cannot repeat.
* @param down if this is 1, it implies a press event, 0 implies a release
* event. (default: 1)
* @param queue a queued key press means that the button event is
* executed just once after which the next key press is processed.
* It can be used for macros. Currently there is no support for
* time delays between queued presses. (default: 0)
* @param amount unimplemented for now; in the future it will be used for
* specifying magnitude of analog key press events
* @param axis
*/
public PacketBUTTON(short code, boolean repeat, boolean down, boolean queue, short amount, byte axis)
{
super(PT_BUTTON);
String map_name = "";
String button_name = "";
short flags = 0;
appendPayload(code, map_name, button_name, repeat, down, queue, amount, axis, flags);
}
/**
* A button packet send a key press or release event to XBMC
* @param map_name a combination of map_name and button_name refers to a
* mapping in the user's Keymap.xml or Lircmap.xml.
* map_name can be one of the following:
* <ul>
* <li>"KB" => standard keyboard map ( <keyboard> section )</li>
* <li>"XG" => xbox gamepad map ( <gamepad> section )</li>
* <li>"R1" => xbox remote map ( <remote> section )</li>
* <li>"R2" => xbox universal remote map ( <universalremote> section )</li>
* <li>"LI:devicename" => LIRC remote map where 'devicename' is the
* actual device's name</li></ul>
* @param button_name a button name defined in the map specified in map_name.
* For example, if map_name is "KB" refering to the <keyboard> section in Keymap.xml
* then, valid button_names include "printscreen", "minus", "x", etc.
* @param repeat this key press should repeat until released (default: 1)
* Note that queued pressed cannot repeat.
* @param down if this is 1, it implies a press event, 0 implies a release
* event. (default: 1)
* @param queue a queued key press means that the button event is
* executed just once after which the next key press is processed.
* It can be used for macros. Currently there is no support for
* time delays between queued presses. (default: 0)
* @param amount unimplemented for now; in the future it will be used for
* specifying magnitude of analog key press events
* @param axis
*/
public PacketBUTTON(String map_name, String button_name, boolean repeat, boolean down, boolean queue, short amount, byte axis)
{
super(PT_BUTTON);
short code = 0;
short flags = BT_USE_NAME;
appendPayload(code, map_name, button_name, repeat, down, queue, amount, axis, flags);
}
/**
* Appends Payload for a Button Packet (this method is used by the different Constructors of this Packet)
* @param code raw button code (default: 0)
* @param map_name a combination of map_name and button_name refers to a
* mapping in the user's Keymap.xml or Lircmap.xml.
* map_name can be one of the following:
* <ul>
* <li>"KB" => standard keyboard map ( <keyboard> section )</li>
* <li>"XG" => xbox gamepad map ( <gamepad> section )</li>
* <li>"R1" => xbox remote map ( <remote> section )</li>
* <li>"R2" => xbox universal remote map ( <universalremote> section )</li>
* <li>"LI:devicename" => LIRC remote map where 'devicename' is the
* actual device's name</li></ul>
* @param button_name a button name defined in the map specified in map_name.
* For example, if map_name is "KB" refering to the <keyboard> section in Keymap.xml
* then, valid button_names include "printscreen", "minus", "x", etc.
* @param repeat this key press should repeat until released (default: 1)
* Note that queued pressed cannot repeat.
* @param down if this is 1, it implies a press event, 0 implies a release
* event. (default: 1)
* @param queue a queued key press means that the button event is
* executed just once after which the next key press is processed.
* It can be used for macros. Currently there is no support for
* time delays between queued presses. (default: 0)
* @param amount unimplemented for now; in the future it will be used for
* specifying magnitude of analog key press events
* @param axis
* @param flags Packet specific flags
*/
private void appendPayload(short code, String map_name, String button_name, boolean repeat, boolean down, boolean queue, short amount, byte axis, short flags)
{
if(amount>0)
flags |= BT_USE_AMOUNT;
else
amount = 0;
if(down)
flags |= BT_DOWN;
else
flags |= BT_UP;
if(!repeat)
flags |= BT_NO_REPEAT;
if(queue)
flags |= BT_QUEUE;
if(axis == 1)
flags |= BT_AXISSINGLE;
else if (axis == 2)
flags |= BT_AXIS;
appendPayload(code);
appendPayload(flags);
appendPayload(amount);
appendPayload(map_name);
appendPayload(button_name);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
/**
* XBMC Event Client Class
*
* A BYE packet terminates the connection to XBMC.
* @author Stefan Agner
*
*/
public class PacketBYE extends Packet
{
/**
* A BYE packet terminates the connection to XBMC.
*/
public PacketBYE()
{
super(PT_BYE);
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
import java.nio.charset.Charset;
/**
* XBMC Event Client Class
*
* A HELO packet establishes a valid connection to XBMC. It is the
* first packet that should be sent.
* @author Stefan Agner
*
*/
public class PacketHELO extends Packet {
/**
* A HELO packet establishes a valid connection to XBMC.
* @param devicename Name of the device which connects to XBMC
*/
public PacketHELO(String devicename)
{
super(PT_HELO);
this.appendPayload(devicename);
this.appendPayload(ICON_NONE);
this.appendPayload((short)0); // port no
this.appendPayload(0); // reserved1
this.appendPayload(0); // reserved2
}
/**
* A HELO packet establishes a valid connection to XBMC.
* @param devicename Name of the device which connects to XBMC
* @param iconType Type of the icon (Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
* @param iconData The icon as a Byte-Array
*/
public PacketHELO(String devicename, byte iconType, byte[] iconData)
{
super(PT_HELO);
this.appendPayload(devicename);
this.appendPayload(iconType);
this.appendPayload((short)0); // port no
this.appendPayload(0); // reserved1
this.appendPayload(0); // reserved2
this.appendPayload(iconData); // reserved2
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
/**
* XBMC Event Client Class
*
* A LOG packet tells XBMC to log the message to xbmc.log with the loglevel as specified.
* @author Stefan Agner
*
*/
public class PacketLOG extends Packet {
/**
* A LOG packet tells XBMC to log the message to xbmc.log with the loglevel as specified.
* @param loglevel the loglevel, follows XBMC standard.
* <ul>
* <li>0 = DEBUG</li>
* <li>1 = INFO</li>
* <li>2 = NOTICE</li>
* <li>3 = WARNING</li>
* <li>4 = ERROR</li>
* <li>5 = SEVERE</li>
* </ul>
* @param logmessage the message to log
*/
public PacketLOG(byte loglevel, String logmessage)
{
super(PT_LOG);
appendPayload(loglevel);
appendPayload(logmessage);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
/**
* XBMC Event Client Class
*
* A MOUSE packets sets the mouse position in XBMC
* @author Stefan Agner
*
*/
public class PacketMOUSE extends Packet {
protected final static byte MS_ABSOLUTE = 0x01;
/**
* A MOUSE packets sets the mouse position in XBMC
* @param x horitontal position ranging from 0 to 65535
* @param y vertical position ranging from 0 to 65535
*/
public PacketMOUSE(int x, int y)
{
super(PT_MOUSE);
byte flags = 0;
flags |= MS_ABSOLUTE;
appendPayload(flags);
appendPayload((short)x);
appendPayload((short)y);
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
/**
* XBMC Event Client Class
*
* This packet displays a notification window in XBMC. It can contain
* a caption, a message and an icon.
* @author Stefan Agner
*
*/
public class PacketNOTIFICATION extends Packet {
/**
* This packet displays a notification window in XBMC.
* @param title Message title
* @param message The actual message
* @param iconType Type of the icon (Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
* @param iconData The icon as a Byte-Array
*/
public PacketNOTIFICATION(String title, String message, byte iconType, byte[] iconData)
{
super(PT_NOTIFICATION);
appendPayload(title, message, iconType, iconData);
}
/**
* This packet displays a notification window in XBMC.
* @param title Message title
* @param message The actual message
*/
public PacketNOTIFICATION(String title, String message)
{
super(PT_NOTIFICATION);
appendPayload(title, message, Packet.ICON_NONE, null);
}
/**
* Appends the payload to the packet...
* @param title Message title
* @param message The actual message
* @param iconType Type of the icon (Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
* @param iconData The icon as a Byte-Array
*/
private void appendPayload(String title, String message, byte iconType, byte[] iconData)
{
appendPayload(title);
appendPayload(message);
appendPayload(iconType);
appendPayload(0); // reserved
if(iconData!=null)
appendPayload(iconData);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2008-2013 Team XBMC
*
* 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.xbmc.kore.eventclient;
/**
* XBMC Event Client Class
*
* A PING packet tells XBMC that the client is still alive. All valid
* packets act as ping (not just this one). A client needs to ping
* XBMC at least once in 60 seconds or it will time
* @author Stefan Agner
*
*/
public class PacketPING extends Packet {
/**
* A PING packet tells XBMC that the client is still alive.
*/
public PacketPING()
{
super(PT_PING);
}
}

View File

@ -44,7 +44,12 @@ public class HostInfo {
*/
public static final int DEFAULT_WOL_PORT = 9;
/**
/**
* Default EventServer port for Kodi
*/
public static final int DEFAULT_EVENT_SERVER_PORT = 9777;
/**
* Internal id of the host
*/
private int id;
@ -61,6 +66,9 @@ public class HostInfo {
private int httpPort;
private int tcpPort;
private boolean useEventServer;
private int eventServerPort;
/**
* Authentication information
*/
@ -93,7 +101,8 @@ public class HostInfo {
* @param password Password for basic auth
*/
public HostInfo(int id, String name, String address, int protocol, int httpPort, int tcpPort,
String username, String password, String macAddress, int wolPort) {
String username, String password, String macAddress, int wolPort,
boolean useEventServer, int eventServerPort) {
this.id = id;
this.name = name;
this.address = address;
@ -108,6 +117,9 @@ public class HostInfo {
this.macAddress = macAddress;
this.wolPort = wolPort;
this.useEventServer = useEventServer;
this.eventServerPort = eventServerPort;
// For performance reasons
this.auxImageHttpAddress = getHttpURL() + "/image/";
}
@ -124,12 +136,13 @@ public class HostInfo {
* @param password Password for basic auth
*/
public HostInfo(String name, String address, int protocol, int httpPort,
int tcpPort, String username, String password) {
int tcpPort, String username, String password,
boolean useEventServer, int eventServerPort) {
this(-1, name, address, protocol, httpPort, tcpPort, username,
password, null, DEFAULT_WOL_PORT);
password, null, DEFAULT_WOL_PORT, useEventServer, eventServerPort);
}
public int getId() {
public int getId() {
return id;
}
@ -177,7 +190,15 @@ public class HostInfo {
return protocol;
}
/**
public boolean getUseEventServer() {
return useEventServer;
}
public int getEventServerPort() {
return eventServerPort;
}
/**
* Overrides the protocol for this host info
* @param protocol Protocol
*/
@ -188,6 +209,14 @@ public class HostInfo {
this.protocol = protocol;
}
/**
* Overrides the use of EventServer
* @param useEventServer Whether to use EventServer
*/
public void setUseEventServer(boolean useEventServer) {
this.useEventServer = useEventServer;
}
/**
* Returns the URL of the host
* @return HTTP URL eg. http://192.168.1.1:8080

View File

@ -142,9 +142,11 @@ public class HostManager {
String password = cursor.getString(idx++);
String macAddress = cursor.getString(idx++);
int wolPort = cursor.getInt(idx++);
boolean useEventServer = (cursor.getInt(idx++) != 0);
int eventServerPort = cursor.getInt(idx++);
hosts.add(new HostInfo(id, name, address, protocol, httpPort, tcpPort,
username, password, macAddress, wolPort));
username, password, macAddress, wolPort, useEventServer, eventServerPort));
}
}
cursor.close();
@ -307,9 +309,10 @@ public class HostManager {
*/
public HostInfo addHost(HostInfo hostInfo) {
return addHost(hostInfo.getName(), hostInfo.getAddress(), hostInfo.getProtocol(),
hostInfo.getHttpPort(), hostInfo.getTcpPort(),
hostInfo.getUsername(), hostInfo.getPassword(),
hostInfo.getMacAddress(), hostInfo.getWolPort());
hostInfo.getHttpPort(), hostInfo.getTcpPort(),
hostInfo.getUsername(), hostInfo.getPassword(),
hostInfo.getMacAddress(), hostInfo.getWolPort(),
hostInfo.getUseEventServer(), hostInfo.getEventServerPort());
}
@ -325,7 +328,8 @@ public class HostManager {
* @return Newly created {@link org.xbmc.kore.host.HostInfo}
*/
public HostInfo addHost(String name, String address, int protocol, int httpPort, int tcpPort,
String username, String password, String macAddress, int wolPort) {
String username, String password, String macAddress, int wolPort,
boolean useEventServer, int eventServerPort) {
ContentValues values = new ContentValues();
values.put(MediaContract.HostsColumns.NAME, name);
@ -337,6 +341,8 @@ public class HostManager {
values.put(MediaContract.HostsColumns.PASSWORD, password);
values.put(MediaContract.HostsColumns.MAC_ADDRESS, macAddress);
values.put(MediaContract.HostsColumns.WOL_PORT, wolPort);
values.put(MediaContract.HostsColumns.USE_EVENT_SERVER, useEventServer);
values.put(MediaContract.HostsColumns.EVENT_SERVER_PORT, eventServerPort);
Uri newUri = context.getContentResolver()
.insert(MediaContract.Hosts.CONTENT_URI, values);
@ -371,6 +377,8 @@ public class HostManager {
values.put(MediaContract.HostsColumns.PASSWORD, newHostInfo.getPassword());
values.put(MediaContract.HostsColumns.MAC_ADDRESS, newHostInfo.getMacAddress());
values.put(MediaContract.HostsColumns.WOL_PORT, newHostInfo.getWolPort());
values.put(MediaContract.HostsColumns.USE_EVENT_SERVER, newHostInfo.getUseEventServer());
values.put(MediaContract.HostsColumns.EVENT_SERVER_PORT, newHostInfo.getEventServerPort());
context.getContentResolver()
.update(MediaContract.Hosts.buildHostUri(hostId), values, null, null);

View File

@ -54,15 +54,17 @@ public class MediaContract {
* Columns for table HOSTS
*/
public interface HostsColumns {
public final static String NAME = "name";
public final static String ADDRESS = "address";
public final static String PROTOCOL = "protocol";
public final static String HTTP_PORT = "http_port";
public final static String TCP_PORT = "tcp_port";
public final static String USERNAME = "username";
public final static String PASSWORD = "password";
public final static String MAC_ADDRESS = "mac_address";
public final static String WOL_PORT = "wol_port";
String NAME = "name";
String ADDRESS = "address";
String PROTOCOL = "protocol";
String HTTP_PORT = "http_port";
String TCP_PORT = "tcp_port";
String USERNAME = "username";
String PASSWORD = "password";
String MAC_ADDRESS = "mac_address";
String WOL_PORT = "wol_port";
String USE_EVENT_SERVER = "use_event_server";
String EVENT_SERVER_PORT = "event_server_port";
}
public static class Hosts implements BaseColumns, SyncColumns, HostsColumns {
@ -86,7 +88,7 @@ public class MediaContract {
public final static String[] ALL_COLUMNS = {
_ID, UPDATED, NAME, ADDRESS, PROTOCOL, HTTP_PORT, TCP_PORT, USERNAME, PASSWORD,
MAC_ADDRESS, WOL_PORT
MAC_ADDRESS, WOL_PORT, USE_EVENT_SERVER, EVENT_SERVER_PORT
};
}
@ -95,40 +97,40 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + MOVIEID
*/
public interface MoviesColumns {
public final static String HOST_ID = "host_id";
public final static String MOVIEID = "movieid";
String HOST_ID = "host_id";
String MOVIEID = "movieid";
public final static String FANART = "fanart";
public final static String THUMBNAIL = "thumbnail";
public final static String PLAYCOUNT = "playcount";
public final static String TITLE = "title";
public final static String FILE = "file";
public final static String PLOT = "plot";
public final static String DIRECTOR = "director";
public final static String RUNTIME = "runtime";
public final static String AUDIO_CHANNELS = "audio_channels";
public final static String AUDIO_CODEC = "audio_coded";
public final static String AUDIO_LANGUAGE = "audio_language";
public final static String SUBTITLES_LANGUAGES = "subtitles_languages";
public static final String VIDEO_ASPECT = "video_aspect";
public static final String VIDEO_CODEC = "video_codec";
public static final String VIDEO_HEIGHT = "video_height";
public static final String VIDEO_WIDTH = "video_width";
public static final String COUNTRIES = "countries";
public static final String GENRES = "genres";
public static final String IMDBNUMBER = "imdbnumber";
public static final String MPAA = "mpaa";
public static final String RATING = "rating";
public static final String SET = "movie_set";
public static final String SETID = "setid";
public static final String STUDIOS = "studios";
public static final String TAGLINE = "tagline";
public static final String TOP250 = "top250";
public static final String TRAILER = "trailer";
public static final String VOTES = "votes";
public static final String WRITERS = "writers";
public static final String YEAR = "year";
public static final String DATEADDED = "dateadded";
String FANART = "fanart";
String THUMBNAIL = "thumbnail";
String PLAYCOUNT = "playcount";
String TITLE = "title";
String FILE = "file";
String PLOT = "plot";
String DIRECTOR = "director";
String RUNTIME = "runtime";
String AUDIO_CHANNELS = "audio_channels";
String AUDIO_CODEC = "audio_coded";
String AUDIO_LANGUAGE = "audio_language";
String SUBTITLES_LANGUAGES = "subtitles_languages";
String VIDEO_ASPECT = "video_aspect";
String VIDEO_CODEC = "video_codec";
String VIDEO_HEIGHT = "video_height";
String VIDEO_WIDTH = "video_width";
String COUNTRIES = "countries";
String GENRES = "genres";
String IMDBNUMBER = "imdbnumber";
String MPAA = "mpaa";
String RATING = "rating";
String SET = "movie_set";
String SETID = "setid";
String STUDIOS = "studios";
String TAGLINE = "tagline";
String TOP250 = "top250";
String TRAILER = "trailer";
String VOTES = "votes";
String WRITERS = "writers";
String YEAR = "year";
String DATEADDED = "dateadded";
}
public static class Movies implements BaseColumns, SyncColumns, MoviesColumns {
@ -174,13 +176,13 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + MOVIEID + NAME
*/
public interface MovieCastColumns {
public final static String HOST_ID = "host_id";
public final static String MOVIEID = "movieid";
public final static String NAME = "name";
String HOST_ID = "host_id";
String MOVIEID = "movieid";
String NAME = "name";
public final static String ORDER = "cast_order";
public final static String ROLE = "role";
public final static String THUMBNAIL = "thumbnail";
String ORDER = "cast_order";
String ROLE = "role";
String THUMBNAIL = "thumbnail";
}
public static class MovieCast implements BaseColumns, SyncColumns, MovieCastColumns {
@ -205,24 +207,24 @@ public class MediaContract {
* For XBMC reference use HOST_ID + TVSHOWID
*/
public interface TVShowsColumns {
public final static String HOST_ID = "host_id";
public final static String TVSHOWID = "tvshowid";
String HOST_ID = "host_id";
String TVSHOWID = "tvshowid";
public static final String FANART = "fanart";
public static final String THUMBNAIL = "thumbnail";
public static final String PLAYCOUNT = "playcount";
public static final String TITLE = "title";
public static final String DATEADDED = "dateadded";
public static final String FILE = "file";
public static final String PLOT = "plot";
public static final String EPISODE = "episode";
public static final String IMDBNUMBER = "imdbnumber";
public static final String MPAA = "mpaa";
public static final String PREMIERED = "premiered";
public static final String RATING = "rating";
public static final String STUDIO = "studio";
public static final String WATCHEDEPISODES = "watchedepisodes";
public static final String GENRES = "genres";
String FANART = "fanart";
String THUMBNAIL = "thumbnail";
String PLAYCOUNT = "playcount";
String TITLE = "title";
String DATEADDED = "dateadded";
String FILE = "file";
String PLOT = "plot";
String EPISODE = "episode";
String IMDBNUMBER = "imdbnumber";
String MPAA = "mpaa";
String PREMIERED = "premiered";
String RATING = "rating";
String STUDIO = "studio";
String WATCHEDEPISODES = "watchedepisodes";
String GENRES = "genres";
}
public static class TVShows implements BaseColumns, SyncColumns, TVShowsColumns {
@ -266,13 +268,13 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + TVSHOWID + NAME
*/
public interface TVShowCastColumns {
public final static String HOST_ID = "host_id";
public final static String TVSHOWID = "tvshowid";
public final static String NAME = "name";
String HOST_ID = "host_id";
String TVSHOWID = "tvshowid";
String NAME = "name";
public final static String ORDER = "cast_order";
public final static String ROLE = "role";
public final static String THUMBNAIL = "thumbnail";
String ORDER = "cast_order";
String ROLE = "role";
String THUMBNAIL = "thumbnail";
}
public static class TVShowCast implements BaseColumns, SyncColumns, TVShowCastColumns {
@ -297,16 +299,16 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + TVSHOWID + SEASON
*/
public interface SeasonsColumns {
public final static String HOST_ID = "host_id";
public final static String TVSHOWID = "tvshowid";
public static final String SEASON = "season";
String HOST_ID = "host_id";
String TVSHOWID = "tvshowid";
String SEASON = "season";
public static final String LABEL = "label";
public static final String FANART = "fanart";
public static final String THUMBNAIL = "thumbnail";
public static final String EPISODE = "episode";
public static final String SHOWTITLE = "showtitle";
public static final String WATCHEDEPISODES = "watchedepisodes";
String LABEL = "label";
String FANART = "fanart";
String THUMBNAIL = "thumbnail";
String EPISODE = "episode";
String SHOWTITLE = "showtitle";
String WATCHEDEPISODES = "watchedepisodes";
}
public static class Seasons implements BaseColumns, SyncColumns, SeasonsColumns {
@ -350,34 +352,34 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + EPISODEID
*/
public interface EpisodesColumns {
public final static String HOST_ID = "host_id";
public static final String EPISODEID = "episodeid";
String HOST_ID = "host_id";
String EPISODEID = "episodeid";
public final static String TVSHOWID = "tvshowid";
public static final String SEASON = "season";
public static final String EPISODE = "episode";
String TVSHOWID = "tvshowid";
String SEASON = "season";
String EPISODE = "episode";
public static final String FANART = "fanart";
public static final String THUMBNAIL = "thumbnail";
public static final String PLAYCOUNT = "playcount";
public static final String TITLE = "title";
public static final String DATEADDED = "dateadded";
public static final String FILE = "file";
public static final String PLOT = "plot";
public static final String DIRECTOR = "director";
public static final String RUNTIME = "runtime";
public static final String FIRSTAIRED = "firstaired";
public static final String RATING = "rating";
public static final String SHOWTITLE = "showtitle";
public static final String WRITER = "writer";
public final static String AUDIO_CHANNELS = "audio_channels";
public final static String AUDIO_CODEC = "audio_coded";
public final static String AUDIO_LANGUAGE = "audio_language";
public final static String SUBTITLES_LANGUAGES = "subtitles_languages";
public static final String VIDEO_ASPECT = "video_aspect";
public static final String VIDEO_CODEC = "video_codec";
public static final String VIDEO_HEIGHT = "video_height";
public static final String VIDEO_WIDTH = "video_width";
String FANART = "fanart";
String THUMBNAIL = "thumbnail";
String PLAYCOUNT = "playcount";
String TITLE = "title";
String DATEADDED = "dateadded";
String FILE = "file";
String PLOT = "plot";
String DIRECTOR = "director";
String RUNTIME = "runtime";
String FIRSTAIRED = "firstaired";
String RATING = "rating";
String SHOWTITLE = "showtitle";
String WRITER = "writer";
String AUDIO_CHANNELS = "audio_channels";
String AUDIO_CODEC = "audio_coded";
String AUDIO_LANGUAGE = "audio_language";
String SUBTITLES_LANGUAGES = "subtitles_languages";
String VIDEO_ASPECT = "video_aspect";
String VIDEO_CODEC = "video_codec";
String VIDEO_HEIGHT = "video_height";
String VIDEO_WIDTH = "video_width";
}
public static class Episodes implements BaseColumns, SyncColumns, EpisodesColumns {
@ -443,14 +445,14 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + ARTISTID
*/
public interface ArtistsColumns {
public final static String HOST_ID = "host_id";
public static final String ARTISTID = "artistid";
String HOST_ID = "host_id";
String ARTISTID = "artistid";
public static final String ARTIST = "artist";
public final String DESCRIPTION = "description";
public final String GENRE = "genre";
public final String FANART = "fanart";
public final String THUMBNAIL = "thumbnail";
String ARTIST = "artist";
String DESCRIPTION = "description";
String GENRE = "genre";
String FANART = "fanart";
String THUMBNAIL = "thumbnail";
}
public static class Artists implements BaseColumns, SyncColumns, ArtistsColumns {
@ -492,19 +494,19 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + ALBUMID
*/
public interface AlbumsColumns {
public final static String HOST_ID = "host_id";
public static final String ALBUMID = "albumid";
String HOST_ID = "host_id";
String ALBUMID = "albumid";
public static final String FANART = "fanart";
public static final String THUMBNAIL = "thumbnail";
public static final String DISPLAYARTIST = "displayartist";
public static final String RATING = "rating";
public static final String TITLE = "title";
public static final String YEAR = "year";
public static final String ALBUMLABEL = "albumlabel";
public static final String DESCRIPTION = "description";
public static final String PLAYCOUNT = "playcount";
public static final String GENRE = "genre";
String FANART = "fanart";
String THUMBNAIL = "thumbnail";
String DISPLAYARTIST = "displayartist";
String RATING = "rating";
String TITLE = "title";
String YEAR = "year";
String ALBUMLABEL = "albumlabel";
String DESCRIPTION = "description";
String PLAYCOUNT = "playcount";
String GENRE = "genre";
}
public static class Albums implements BaseColumns, SyncColumns, AlbumsColumns {
@ -565,15 +567,15 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + ALBUMID + SONGID
*/
public interface SongsColumns {
public final static String HOST_ID = "host_id";
public static final String ALBUMID = "albumid";
public static final String SONGID = "songid";
String HOST_ID = "host_id";
String ALBUMID = "albumid";
String SONGID = "songid";
public static final String DURATION = "duration";
public static final String THUMBNAIL = "thumbnail";
public static final String FILE = "file";
public static final String TRACK = "track";
public static final String TITLE = "title";
String DURATION = "duration";
String THUMBNAIL = "thumbnail";
String FILE = "file";
String TRACK = "track";
String TITLE = "title";
}
public static class Songs implements BaseColumns, SyncColumns, SongsColumns {
@ -615,11 +617,11 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + GENREID
*/
public interface AudioGenresColumns {
public final static String HOST_ID = "host_id";
public static final String GENREID = "genreid";
String HOST_ID = "host_id";
String GENREID = "genreid";
public static final String THUMBNAIL = "thumbnail";
public static final String TITLE = "title";
String THUMBNAIL = "thumbnail";
String TITLE = "title";
}
public static class AudioGenres implements BaseColumns, SyncColumns, AudioGenresColumns {
@ -661,9 +663,9 @@ public class MediaContract {
* All Other IDs refer to XBMC Ids, not Internal ones
*/
public interface AlbumArtistsColumns {
public final static String HOST_ID = "host_id";
public static final String ALBUMID = "albumid";
public static final String ARTISTID = "artistid";
String HOST_ID = "host_id";
String ALBUMID = "albumid";
String ARTISTID = "artistid";
}
public static class AlbumArtists implements BaseColumns, AlbumArtistsColumns {
@ -691,9 +693,9 @@ public class MediaContract {
* All Other IDs refer to XBMC Ids, not Internal ones
*/
public interface AlbumGenresColumns {
public final static String HOST_ID = "host_id";
public static final String ALBUMID = "albumid";
public static final String GENREID = "genreid";
String HOST_ID = "host_id";
String ALBUMID = "albumid";
String GENREID = "genreid";
}
public static class AlbumGenres implements BaseColumns, AlbumGenresColumns {
@ -721,51 +723,51 @@ public class MediaContract {
* For XBMC reference/unique key use HOST_ID + MUSICVIDEOID
*/
public interface MusicVideosColumns {
public final static String HOST_ID = "host_id";
public final static String MUSICVIDEOID = "musicvideoid";
String HOST_ID = "host_id";
String MUSICVIDEOID = "musicvideoid";
// ItemType.DetailsBase
//public static final String LABEL = "label";
//String LABEL = "label";
// MediaType.DetailsBase
public static final String FANART = "fanart";
public static final String THUMBNAIL = "thumbnail";
String FANART = "fanart";
String THUMBNAIL = "thumbnail";
// DetailsBase
//public static final String ART = "art";
public static final String PLAYCOUNT = "playcount";
//String ART = "art";
String PLAYCOUNT = "playcount";
// DetailsMedia
public static final String TITLE = "title";
String TITLE = "title";
// DetailsItem
//public static final String DATEADDED = "dateadded";
public static final String FILE = "file";
//public static final String LASTPLAYED = "lastplayed";
public static final String PLOT = "plot";
//String DATEADDED = "dateadded";
String FILE = "file";
//String LASTPLAYED = "lastplayed";
String PLOT = "plot";
// DetailsFile
public static final String DIRECTOR = "director";
//public static final String RESUME = "resume";
public static final String RUNTIME = "runtime";
//public static final String STREAMDETAILS = "streamdetails";
public final static String AUDIO_CHANNELS = "audio_channels";
public final static String AUDIO_CODEC = "audio_coded";
public final static String AUDIO_LANGUAGE = "audio_language";
public final static String SUBTITLES_LANGUAGES = "subtitles_languages";
public static final String VIDEO_ASPECT = "video_aspect";
public static final String VIDEO_CODEC = "video_codec";
public static final String VIDEO_HEIGHT = "video_height";
public static final String VIDEO_WIDTH = "video_width";
String DIRECTOR = "director";
//String RESUME = "resume";
String RUNTIME = "runtime";
//String STREAMDETAILS = "streamdetails";
String AUDIO_CHANNELS = "audio_channels";
String AUDIO_CODEC = "audio_coded";
String AUDIO_LANGUAGE = "audio_language";
String SUBTITLES_LANGUAGES = "subtitles_languages";
String VIDEO_ASPECT = "video_aspect";
String VIDEO_CODEC = "video_codec";
String VIDEO_HEIGHT = "video_height";
String VIDEO_WIDTH = "video_width";
// MusicVideo
public static final String ALBUM = "album";
public static final String ARTIST = "artist";
public static final String GENRES = "genre";
public static final String STUDIOS = "studio";
public static final String TAG = "tag";
public static final String TRACK = "track";
public static final String YEAR = "year";
String ALBUM = "album";
String ARTIST = "artist";
String GENRES = "genre";
String STUDIOS = "studio";
String TAG = "tag";
String TRACK = "track";
String YEAR = "year";
}
public static class MusicVideos implements BaseColumns, SyncColumns, MusicVideosColumns {

View File

@ -20,6 +20,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.utils.LogUtils;
@ -30,31 +31,32 @@ public class MediaDatabase extends SQLiteOpenHelper {
private static final String TAG = LogUtils.makeLogTag(MediaDatabase.class);
private static final String DB_NAME = "xbmc.sqlite";
private static final int DB_VERSION = 4;
private static final int DB_VERSION_PRE_EVENT_SERVER = 4,
DB_VERSION = 5;
/**
* Tables exposed
*/
public interface Tables {
public final static String HOSTS = "hosts";
public final static String MOVIES = "movies";
public final static String MOVIE_CAST = "movie_cast";
public final static String TVSHOWS = "tvshows";
public final static String TVSHOWS_CAST = "tvshows_cast";
public final static String SEASONS = "seasons";
public final static String EPISODES = "episodes";
public final static String ARTISTS = "artists";
public final static String ALBUMS = "albums";
public final static String SONGS = "songs";
public final static String AUDIO_GENRES = "audio_genres";
public final static String ALBUM_ARTISTS = "album_artists";
public final static String ALBUM_GENRES = "album_genres";
public final static String MUSIC_VIDEOS = "music_videos";
String HOSTS = "hosts";
String MOVIES = "movies";
String MOVIE_CAST = "movie_cast";
String TVSHOWS = "tvshows";
String TVSHOWS_CAST = "tvshows_cast";
String SEASONS = "seasons";
String EPISODES = "episodes";
String ARTISTS = "artists";
String ALBUMS = "albums";
String SONGS = "songs";
String AUDIO_GENRES = "audio_genres";
String ALBUM_ARTISTS = "album_artists";
String ALBUM_GENRES = "album_genres";
String MUSIC_VIDEOS = "music_videos";
/**
* Join to get Albums for an Artist
*/
public final static String ALBUMS_FOR_ARTIST_JOIN =
String ALBUMS_FOR_ARTIST_JOIN =
ALBUM_ARTISTS + " JOIN " + ALBUMS + " ON " +
ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.HOST_ID + "=" + ALBUMS + "." + MediaContract.Albums.HOST_ID +
" AND " +
@ -63,7 +65,7 @@ public class MediaDatabase extends SQLiteOpenHelper {
/**
* Join to get Artists for an Album
*/
public final static String ARTISTS_FOR_ALBUM_JOIN =
String ARTISTS_FOR_ALBUM_JOIN =
ALBUM_ARTISTS + " JOIN " + ARTISTS + " ON " +
ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.HOST_ID + "=" + ARTISTS + "." + MediaContract.Artists.HOST_ID +
" AND " +
@ -72,7 +74,7 @@ public class MediaDatabase extends SQLiteOpenHelper {
/**
* Join to get Album for a Genre
*/
public final static String ALBUMS_FOR_GENRE_JOIN =
String ALBUMS_FOR_GENRE_JOIN =
ALBUM_GENRES + " JOIN " + ALBUMS + " ON " +
ALBUM_GENRES + "." + MediaContract.AlbumGenres.HOST_ID + "=" + ALBUMS + "." + MediaContract.Albums.HOST_ID +
" AND " +
@ -81,7 +83,7 @@ public class MediaDatabase extends SQLiteOpenHelper {
/**
* Join to get Genres for an Album
*/
public final static String GENRES_FOR_ALBUM_JOIN =
String GENRES_FOR_ALBUM_JOIN =
ALBUM_GENRES + " JOIN " + AUDIO_GENRES + " ON " +
ALBUM_GENRES + "." + MediaContract.AlbumGenres.HOST_ID + "=" + AUDIO_GENRES + "." + MediaContract.AudioGenres.HOST_ID +
" AND " +
@ -89,13 +91,13 @@ public class MediaDatabase extends SQLiteOpenHelper {
}
private interface References {
final static String HOST_ID =
String HOST_ID =
"REFERENCES " + Tables.HOSTS + "(" + BaseColumns._ID + ")";
final static String ALBUMID =
String ALBUMID =
"REFERENCES " + Tables.ALBUMS + "(" + MediaContract.AlbumsColumns.ALBUMID + ")";
final static String ARTISTID =
String ARTISTID =
"REFERENCES " + Tables.ARTISTS + "(" + MediaContract.ArtistsColumns.ARTISTID + ")";
final static String GENREID =
String GENREID =
"REFERENCES " + Tables.AUDIO_GENRES + "(" + MediaContract.AudioGenresColumns.GENREID + ")";
}
@ -108,17 +110,19 @@ public class MediaDatabase extends SQLiteOpenHelper {
// Hosts
db.execSQL("CREATE TABLE " + Tables.HOSTS + "(" +
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
MediaContract.SyncColumns.UPDATED + " INTEGER NOT NULL," +
MediaContract.HostsColumns.NAME + " TEXT, " +
MediaContract.HostsColumns.ADDRESS + " TEXT, " +
MediaContract.HostsColumns.PROTOCOL + " INTEGER, " +
MediaContract.HostsColumns.HTTP_PORT + " INTEGER, " +
MediaContract.HostsColumns.TCP_PORT + " INTEGER, " +
MediaContract.HostsColumns.USERNAME + " TEXT, " +
MediaContract.HostsColumns.PASSWORD + " TEXT, " +
MediaContract.HostsColumns.MAC_ADDRESS + " TEXT, " +
MediaContract.HostsColumns.WOL_PORT + " INTEGER)"
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
MediaContract.SyncColumns.UPDATED + " INTEGER NOT NULL," +
MediaContract.HostsColumns.NAME + " TEXT, " +
MediaContract.HostsColumns.ADDRESS + " TEXT, " +
MediaContract.HostsColumns.PROTOCOL + " INTEGER, " +
MediaContract.HostsColumns.HTTP_PORT + " INTEGER, " +
MediaContract.HostsColumns.TCP_PORT + " INTEGER, " +
MediaContract.HostsColumns.USERNAME + " TEXT, " +
MediaContract.HostsColumns.PASSWORD + " TEXT, " +
MediaContract.HostsColumns.MAC_ADDRESS + " TEXT, " +
MediaContract.HostsColumns.WOL_PORT + " INTEGER, " +
MediaContract.HostsColumns.USE_EVENT_SERVER + " INTEGER, " +
MediaContract.HostsColumns.EVENT_SERVER_PORT + " INTEGER)"
);
// Movies
@ -438,7 +442,20 @@ public class MediaDatabase extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// For now just drop old tables and recreate
switch (oldVersion) {
case DB_VERSION_PRE_EVENT_SERVER:
// Add USE_EVENT_SERVER and EVENT_SERVER_PORT columns to HOSTS table
db.execSQL("ALTER TABLE " + Tables.HOSTS +
" ADD COLUMN " + MediaContract.HostsColumns.USE_EVENT_SERVER +
" INTEGER DEFAULT 1;");
db.execSQL("ALTER TABLE " + Tables.HOSTS +
" ADD COLUMN " + MediaContract.HostsColumns.EVENT_SERVER_PORT +
" INTEGER DEFAULT " + HostInfo.DEFAULT_EVENT_SERVER_PORT + ";");
}
}
private void resetDb(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + Tables.HOSTS);
db.execSQL("DROP TABLE IF EXISTS " + Tables.MOVIES);
db.execSQL("DROP TABLE IF EXISTS " + Tables.MOVIE_CAST);
@ -454,7 +471,7 @@ public class MediaDatabase extends SQLiteOpenHelper {
db.execSQL("DROP TABLE IF EXISTS " + Tables.ALBUM_GENRES);
db.execSQL("DROP TABLE IF EXISTS " + Tables.MUSIC_VIDEOS);
onCreate(db);
}
}
/**
* Tokens to move from prefix to suffix when sorting titles

View File

@ -16,6 +16,7 @@
package org.xbmc.kore.ui;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
@ -34,7 +35,9 @@ import android.widget.Toast;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.eventclient.EventServerConnection;
import org.xbmc.kore.host.HostConnectionObserver;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.HostConnection;
@ -574,6 +577,7 @@ public class RemoteActivity extends BaseActivity
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
PlayerType.PropertyValue getPropertiesResult,
ListType.ItemsAll getItemResult) {
checkEventServerAvailability();
String imageUrl = (TextUtils.isEmpty(getItemResult.fanart)) ?
getItemResult.thumbnail : getItemResult.fanart;
if ((imageUrl != null) && !imageUrl.equals(lastImageUrl)) {
@ -599,6 +603,7 @@ public class RemoteActivity extends BaseActivity
}
public void playerOnStop() {
checkEventServerAvailability();
if (lastImageUrl != null) {
setImageViewBackground(null);
}
@ -633,4 +638,30 @@ public class RemoteActivity extends BaseActivity
viewPager.setCurrentItem(1);
}
// TODO: Remove this method after deployment of version 1.5.0. The only objective of this is to facilitate the
// transition to using EventServer, by checking if it is available, but this needs to be done only once.
public void checkEventServerAvailability() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean checkedEventServerConnection =
preferences.getBoolean(Settings.KEY_PREF_CHECKED_EVENT_SERVER_CONNECTION,
Settings.DEFAULT_PREF_CHECKED_EVENT_SERVER_CONNECTION);
if (!checkedEventServerConnection) {
LogUtils.LOGD(TAG, "Checking EventServer connection implicitely");
// Check if EventServer is available
final HostInfo hostInfo = hostManager.getHostInfo();
EventServerConnection.testEventServerConnection(
hostInfo,
new EventServerConnection.EventServerConnectionCallback() {
@Override
public void OnConnectResult(boolean success) {
hostInfo.setUseEventServer(success);
hostManager.editHost(hostInfo.getId(), hostInfo);
}
},
new Handler());
preferences.edit()
.putBoolean(Settings.KEY_PREF_CHECKED_EVENT_SERVER_CONNECTION, true)
.apply();
}
}
}

View File

@ -20,13 +20,13 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.BitmapFactory;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@ -40,6 +40,10 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import org.xbmc.kore.R;
import org.xbmc.kore.eventclient.ButtonCodes;
import org.xbmc.kore.eventclient.EventServerConnection;
import org.xbmc.kore.eventclient.Packet;
import org.xbmc.kore.eventclient.PacketBUTTON;
import org.xbmc.kore.host.HostConnectionObserver;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.host.HostManager;
@ -56,6 +60,8 @@ import org.xbmc.kore.utils.RepeatListener;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
import java.net.UnknownHostException;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
@ -128,6 +134,9 @@ public class RemoteFragment extends Fragment
// Touch listener that provides touch feedbacl
private View.OnTouchListener feedbackTouckListener;
// EventServer connection
private EventServerConnection eventServerConnection = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -137,6 +146,8 @@ public class RemoteFragment extends Fragment
buttonInAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.button_in);
buttonOutAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.button_out);
createEventServerConnection();
feedbackTouckListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
@ -160,19 +171,30 @@ public class RemoteFragment extends Fragment
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_remote, container, false);
ButterKnife.inject(this, root);
// Setup repeat buttons
setupRepeatButton(leftButton, new Input.Left());
setupRepeatButton(rightButton, new Input.Right());
setupRepeatButton(upButton, new Input.Up());
setupRepeatButton(downButton, new Input.Down());
if (hostManager.getHostInfo().getUseEventServer() &&
(eventServerConnection != null)) {
// Setup d-pad to use EventServer
setupEventServerButton(leftButton, ButtonCodes.REMOTE_LEFT);
setupEventServerButton(rightButton, ButtonCodes.REMOTE_RIGHT);
setupEventServerButton(upButton, ButtonCodes.REMOTE_UP);
setupEventServerButton(downButton, ButtonCodes.REMOTE_DOWN);
//setupEventServerButton(selectButton, ButtonCodes.REMOTE_SELECT);
} else {
// Otherwise, use json-rpc
setupRepeatButton(leftButton, new Input.Left());
setupRepeatButton(rightButton, new Input.Right());
setupRepeatButton(upButton, new Input.Up());
setupRepeatButton(downButton, new Input.Down());
}
setupDefaultButton(selectButton, new Input.Select(), null);
setupNoRepeatButton(selectButton, new Input.Select(), null);
setupNoRepeatButton(backButton, new Input.Back(), null);
setupNoRepeatButton(infoButton,
new Input.ExecuteAction(Input.ExecuteAction.INFO),
new Input.ExecuteAction(Input.ExecuteAction.CODECINFO));
setupNoRepeatButton(osdButton, new Input.ExecuteAction(Input.ExecuteAction.OSD), null);
setupNoRepeatButton(contextButton, new Input.ExecuteAction(Input.ExecuteAction.CONTEXTMENU), null);
// Other buttons
setupDefaultButton(backButton, new Input.Back(), null);
setupDefaultButton(infoButton,
new Input.ExecuteAction(Input.ExecuteAction.INFO),
new Input.ExecuteAction(Input.ExecuteAction.CODECINFO));
setupDefaultButton(osdButton, new Input.ExecuteAction(Input.ExecuteAction.OSD), null);
setupDefaultButton(contextButton, new Input.ExecuteAction(Input.ExecuteAction.CONTEXTMENU), null);
adjustRemoteButtons();
@ -185,17 +207,6 @@ public class RemoteFragment extends Fragment
return root;
}
// /**
// * Select button callback
// * @param v Clicked view
// */
// @OnClick(R.id.select)
// public void onSelectClicked(View v) {
// v.startAnimation(buttonInOutAnim);
// Input.Select action = new Input.Select();
// action.execute(hostManager.getConnection(), defaultActionCallback, callbackHandler);
// }
@TargetApi(21)
private void adjustRemoteButtons() {
Resources.Theme theme = getActivity().getTheme();
@ -244,27 +255,47 @@ public class RemoteFragment extends Fragment
public void onResume() {
super.onResume();
hostConnectionObserver.registerPlayerObserver(this, true);
if (eventServerConnection == null)
createEventServerConnection();
}
@Override
public void onPause() {
super.onPause();
hostConnectionObserver.unregisterPlayerObserver(this);
if (eventServerConnection != null) {
eventServerConnection.quit();
eventServerConnection = null;
}
}
private void createEventServerConnection() {
eventServerConnection = new EventServerConnection(
hostManager.getHostInfo(),
new EventServerConnection.EventServerConnectionCallback() {
@Override
public void OnConnectResult(boolean success) {
if (!success) {
LogUtils.LOGD(TAG, "Couldnt setup EventServer, disabling it");
eventServerConnection = null;
}
}
});
}
private void setupRepeatButton(View button, final ApiMethod<String> action) {
button.setOnTouchListener(new RepeatListener(UIUtils.initialButtonRepeatInterval, UIUtils.buttonRepeatInterval,
new View.OnClickListener() {
@Override
public void onClick(View v) {
action.execute(hostManager.getConnection(), defaultActionCallback, callbackHandler);
}
}, buttonInAnim, buttonOutAnim));
new View.OnClickListener() {
@Override
public void onClick(View v) {
action.execute(hostManager.getConnection(), defaultActionCallback, callbackHandler);
}
}, buttonInAnim, buttonOutAnim));
}
private void setupNoRepeatButton(View button,
final ApiMethod<String> clickAction,
final ApiMethod<String> longClickAction) {
private void setupDefaultButton(View button,
final ApiMethod<String> clickAction,
final ApiMethod<String> longClickAction) {
// Set animation
button.setOnTouchListener(feedbackTouckListener);
if (clickAction != null) {
@ -286,6 +317,54 @@ public class RemoteFragment extends Fragment
}
}
private void setupEventServerButton(View button, final String action) {
final Packet packet =
new PacketBUTTON(ButtonCodes.MAP_REMOTE, action, false, true, true, (short)0, (byte)0);
button.setOnTouchListener(new RepeatListener(UIUtils.initialButtonRepeatInterval, UIUtils.buttonRepeatInterval,
new View.OnClickListener() {
@Override
public void onClick(View v) {
eventServerConnection.sendPacket(packet);
}
}, buttonInAnim, buttonOutAnim));
}
// private void setupEventServerButton(View button, final String action) {
// short amount = 0;
// byte axis = 0;
// final Packet packetDown =
// new PacketBUTTON(ButtonCodes.MAP_REMOTE, action, true, true, false, amount, axis);
// final Packet packetUp =
// new PacketBUTTON(ButtonCodes.MAP_REMOTE, action, false, false, false, amount, axis);
//
// // Set animation and packet
// button.setOnTouchListener(new View.OnTouchListener() {
// @Override
// public boolean onTouch(View v, MotionEvent event) {
// switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// buttonInAnim.setFillAfter(true);
// v.startAnimation(buttonInAnim);
// eventServerConnection.sendPacket(packetDown);
// break;
// case MotionEvent.ACTION_UP:
// case MotionEvent.ACTION_CANCEL:
// v.startAnimation(buttonOutAnim);
// eventServerConnection.sendPacket(packetUp);
// break;
// }
// return false;
// }
// });
// button.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// // Nothing to do
// }
// });
// }
/**
* Default callback for methods that don't return anything
*/

View File

@ -108,21 +108,16 @@ public class AddHostActivity extends BaseActivity
Bundle args = new Bundle();
if (hostInfo != null) {
args.putString(HostFragmentManualConfiguration.HOST_NAME,
hostInfo.getName());
args.putString(HostFragmentManualConfiguration.HOST_ADDRESS,
hostInfo.getAddress());
args.putInt(HostFragmentManualConfiguration.HOST_HTTP_PORT,
hostInfo.getHttpPort());
args.putInt(HostFragmentManualConfiguration.HOST_TCP_PORT,
hostInfo.getTcpPort());
args.putString(HostFragmentManualConfiguration.HOST_USERNAME,
hostInfo.getUsername());
args.putString(HostFragmentManualConfiguration.HOST_PASSWORD,
hostInfo.getPassword());
args.putInt(HostFragmentManualConfiguration.HOST_PROTOCOL,
hostInfo.getProtocol());
args.putString(HostFragmentManualConfiguration.HOST_NAME, hostInfo.getName());
args.putString(HostFragmentManualConfiguration.HOST_ADDRESS, hostInfo.getAddress());
args.putInt(HostFragmentManualConfiguration.HOST_HTTP_PORT, hostInfo.getHttpPort());
args.putInt(HostFragmentManualConfiguration.HOST_TCP_PORT, hostInfo.getTcpPort());
args.putString(HostFragmentManualConfiguration.HOST_USERNAME, hostInfo.getUsername());
args.putString(HostFragmentManualConfiguration.HOST_PASSWORD, hostInfo.getPassword());
args.putInt(HostFragmentManualConfiguration.HOST_PROTOCOL, hostInfo.getProtocol());
// Ignore Mac Address and Wol Port
args.putBoolean(HostFragmentManualConfiguration.HOST_USE_EVENT_SERVER, hostInfo.getUseEventServer());
args.putInt(HostFragmentManualConfiguration.HOST_EVENT_SERVER_PORT, hostInfo.getEventServerPort());
// Send this fragment straight to test
args.putBoolean(HostFragmentManualConfiguration.GO_STRAIGHT_TO_TEST, true);

View File

@ -271,7 +271,7 @@ public class AddHostFragmentZeroconf extends Fragment {
String hostAddress = addresses[0];
int hostHttpPort = selectedServiceInfo.getPort();
HostInfo selectedHostInfo = new HostInfo(hostName, hostAddress, HostConnection.PROTOCOL_TCP,
hostHttpPort, HostInfo.DEFAULT_TCP_PORT, null, null);
hostHttpPort, HostInfo.DEFAULT_TCP_PORT, null, null, true, HostInfo.DEFAULT_EVENT_SERVER_PORT);
listener.onAddHostZeroconfFoundHost(selectedHostInfo);
}

View File

@ -26,6 +26,7 @@ import org.xbmc.kore.R;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.ui.BaseActivity;
import org.xbmc.kore.utils.LogUtils;
/**
* Edits a host
@ -61,21 +62,25 @@ public class EditHostActivity extends BaseActivity implements
args.putString(HostFragmentManualConfiguration.HOST_NAME,
selectedHostInfo.getName());
args.putString(HostFragmentManualConfiguration.HOST_ADDRESS,
selectedHostInfo.getAddress());
selectedHostInfo.getAddress());
args.putInt(HostFragmentManualConfiguration.HOST_HTTP_PORT,
selectedHostInfo.getHttpPort());
selectedHostInfo.getHttpPort());
args.putInt(HostFragmentManualConfiguration.HOST_TCP_PORT,
selectedHostInfo.getTcpPort());
selectedHostInfo.getTcpPort());
args.putString(HostFragmentManualConfiguration.HOST_USERNAME,
selectedHostInfo.getUsername());
selectedHostInfo.getUsername());
args.putString(HostFragmentManualConfiguration.HOST_PASSWORD,
selectedHostInfo.getPassword());
selectedHostInfo.getPassword());
args.putInt(HostFragmentManualConfiguration.HOST_PROTOCOL,
selectedHostInfo.getProtocol());
selectedHostInfo.getProtocol());
args.putString(HostFragmentManualConfiguration.HOST_MAC_ADDRESS,
selectedHostInfo.getMacAddress());
selectedHostInfo.getMacAddress());
args.putInt(HostFragmentManualConfiguration.HOST_WOL_PORT,
selectedHostInfo.getWolPort());
selectedHostInfo.getWolPort());
args.putBoolean(HostFragmentManualConfiguration.HOST_USE_EVENT_SERVER,
selectedHostInfo.getUseEventServer());
args.putInt(HostFragmentManualConfiguration.HOST_EVENT_SERVER_PORT,
selectedHostInfo.getEventServerPort());
editFragment.setArguments(args);
}
}

View File

@ -20,6 +20,7 @@ import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
@ -32,6 +33,8 @@ import android.widget.EditText;
import android.widget.Toast;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.eventclient.EventServerConnection;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.ApiException;
@ -62,7 +65,9 @@ public class HostFragmentManualConfiguration extends Fragment {
HOST_PASSWORD = PREFIX + ".host_password",
HOST_MAC_ADDRESS = PREFIX + ".host_mac_address",
HOST_WOL_PORT = PREFIX + ".host_wol_port",
HOST_PROTOCOL = PREFIX + ".host_protocol";
HOST_PROTOCOL = PREFIX + ".host_protocol",
HOST_USE_EVENT_SERVER = PREFIX + ".host_use_event_server",
HOST_EVENT_SERVER_PORT = PREFIX + ".host_event_server_port";
public static final String GO_STRAIGHT_TO_TEST = PREFIX + ".go_straight_to_test";
/**
@ -86,6 +91,8 @@ public class HostFragmentManualConfiguration extends Fragment {
@InjectView(R.id.xbmc_mac_address) EditText xbmcMacAddressEditText;
@InjectView(R.id.xbmc_wol_port) EditText xbmcWolPortEditText;
@InjectView(R.id.xbmc_use_tcp) CheckBox xbmcUseTcpCheckbox;
@InjectView(R.id.xbmc_use_event_server) CheckBox xbmcUseEventServerCheckbox;
@InjectView(R.id.xbmc_event_server_port) EditText xbmcEventServerPortEditText;
// Handler for callbacks
final Handler handler = new Handler();
@ -104,6 +111,13 @@ public class HostFragmentManualConfiguration extends Fragment {
}
});
xbmcUseEventServerCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
xbmcEventServerPortEditText.setEnabled(isChecked);
}
});
// Check if we were given a host info
String hostName = getArguments().getString(HOST_NAME);
String hostAddress = getArguments().getString(HOST_ADDRESS);
@ -114,6 +128,8 @@ public class HostFragmentManualConfiguration extends Fragment {
int hostProtocol = getArguments().getInt(HOST_PROTOCOL, HostConnection.PROTOCOL_TCP);
String hostMacAddress = getArguments().getString(HOST_MAC_ADDRESS);
int hostWolPort = getArguments().getInt(HOST_WOL_PORT, HostInfo.DEFAULT_WOL_PORT);
boolean hostUseEventServer = getArguments().getBoolean(HOST_USE_EVENT_SERVER, true);
int hostEventServerPort = getArguments().getInt(HOST_EVENT_SERVER_PORT, HostInfo.DEFAULT_EVENT_SERVER_PORT);
if (hostAddress != null) {
xbmcNameEditText.setText(hostName);
@ -133,6 +149,11 @@ public class HostFragmentManualConfiguration extends Fragment {
xbmcMacAddressEditText.setText(hostMacAddress);
if (hostWolPort != HostInfo.DEFAULT_WOL_PORT)
xbmcWolPortEditText.setText(String.valueOf(hostWolPort));
xbmcUseEventServerCheckbox.setChecked(hostUseEventServer);
xbmcEventServerPortEditText.setEnabled(xbmcUseEventServerCheckbox.isChecked());
if (hostEventServerPort != HostInfo.DEFAULT_EVENT_SERVER_PORT)
xbmcEventServerPortEditText.setText(String.valueOf(hostEventServerPort));
}
return root;
@ -230,6 +251,15 @@ public class HostFragmentManualConfiguration extends Fragment {
// Ignoring this exception and keeping WoL port at the default value
}
boolean xbmcUseEventServer = xbmcUseEventServerCheckbox.isChecked();
aux = xbmcEventServerPortEditText.getText().toString();
int xbmcEventServerPort;
try {
xbmcEventServerPort = TextUtils.isEmpty(aux) ? HostInfo.DEFAULT_EVENT_SERVER_PORT : Integer.valueOf(aux);
} catch (NumberFormatException exc) {
xbmcEventServerPort = -1;
}
// Check Xbmc name and address
if (TextUtils.isEmpty(xbmcName)) {
Toast.makeText(getActivity(), R.string.wizard_no_name_specified, Toast.LENGTH_SHORT).show();
@ -247,6 +277,10 @@ public class HostFragmentManualConfiguration extends Fragment {
Toast.makeText(getActivity(), R.string.wizard_invalid_tcp_port_specified, Toast.LENGTH_SHORT).show();
xbmcTcpPortEditText.requestFocus();
return;
} else if (xbmcEventServerPort <= 0) {
Toast.makeText(getActivity(), R.string.wizard_invalid_tcp_port_specified, Toast.LENGTH_SHORT).show();
xbmcEventServerPortEditText.requestFocus();
return;
}
// If username or password empty, set it to null
@ -257,7 +291,9 @@ public class HostFragmentManualConfiguration extends Fragment {
// Ok, let's try to ping the host
final HostInfo checkedHostInfo = new HostInfo(xbmcName, xbmcAddress, xbmcProtocol,
xbmcHttpPort, xbmcTcpPort, xbmcUsername, xbmcPassword);
xbmcHttpPort, xbmcTcpPort,
xbmcUsername, xbmcPassword,
xbmcUseEventServer, xbmcEventServerPort);
checkedHostInfo.setMacAddress(macAddress);
checkedHostInfo.setWolPort(xbmcWolPort);
@ -288,9 +324,9 @@ public class HostFragmentManualConfiguration extends Fragment {
if (hostInfo.getProtocol() == HostConnection.PROTOCOL_TCP) {
chainCallCheckTcpConnection(hostConnection, hostInfo);
} else {
// We're done
// No TCP, check EventServer
hostConnection.disconnect();
hostConnectionChecked(hostInfo);
chainCallCheckEventServerConnection(hostInfo);
}
}
@ -312,8 +348,8 @@ public class HostFragmentManualConfiguration extends Fragment {
// Great, we managed to connect through HTTP and TCP
LogUtils.LOGD(TAG, "Successfully connected to new host through TCP.");
hostConnection.disconnect();
// Notify connection checked through TCP
hostConnectionChecked(hostInfo);
// Check EventServer
chainCallCheckEventServerConnection(hostInfo);
}
@Override
@ -322,11 +358,39 @@ public class HostFragmentManualConfiguration extends Fragment {
LogUtils.LOGD(TAG, "Couldn't connect to host through TCP. Message: " + description);
hostConnection.disconnect();
hostInfo.setProtocol(HostConnection.PROTOCOL_HTTP);
hostConnectionChecked(hostInfo);
// Check EventServer
chainCallCheckEventServerConnection(hostInfo);
}
}, handler);
}
private void chainCallCheckEventServerConnection(final HostInfo hostInfo) {
if (hostInfo.getUseEventServer()) {
EventServerConnection.testEventServerConnection(
hostInfo,
new EventServerConnection.EventServerConnectionCallback() {
@Override
public void OnConnectResult(boolean success) {
LogUtils.LOGD(TAG, "Check ES connection: " + success);
if (success) {
hostConnectionChecked(hostInfo);
} else {
hostInfo.setUseEventServer(false);
hostConnectionChecked(hostInfo);
}
}
},
handler);
} else {
hostConnectionChecked(hostInfo);
}
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit()
.putBoolean(Settings.KEY_PREF_CHECKED_EVENT_SERVER_CONNECTION, true)
.apply();
}
/**
* The connection was checked, and hostInfo has all the correct parameters to communicate
* with it

View File

@ -67,6 +67,7 @@
android:layout_height="wrap_content"
android:layout_below="@+id/xbmc_name"
android:layout_alignRight="@id/xbmc_name"
android:layout_alignEnd="@id/xbmc_name"
android:inputType="number"
android:ems="4"
@ -79,7 +80,9 @@
android:layout_height="wrap_content"
android:layout_alignTop="@id/xbmc_http_port"
android:layout_alignLeft="@id/xbmc_name"
android:layout_alignStart="@id/xbmc_name"
android:layout_toLeftOf="@id/xbmc_http_port"
android:layout_toStartOf="@id/xbmc_http_port"
android:inputType="textNoSuggestions"
android:hint="@string/wizard_xbmc_ip"/>
@ -91,7 +94,9 @@
android:layout_height="wrap_content"
android:layout_below="@+id/xbmc_ip_address"
android:layout_alignLeft="@id/xbmc_name"
android:layout_alignStart="@id/xbmc_name"
android:layout_alignRight="@id/xbmc_name"
android:layout_alignEnd="@id/xbmc_name"
android:inputType="textNoSuggestions"
android:hint="@string/wizard_xbmc_username"/>
@ -103,7 +108,9 @@
android:layout_height="wrap_content"
android:layout_below="@+id/xbmc_username"
android:layout_alignLeft="@id/xbmc_name"
android:layout_alignStart="@id/xbmc_name"
android:layout_alignRight="@id/xbmc_name"
android:layout_alignEnd="@id/xbmc_name"
android:inputType="textPassword"
android:hint="@string/wizard_xbmc_password"/>
@ -124,6 +131,7 @@
android:layout_below="@android:id/text1"
android:layout_marginTop="8dp"
android:layout_alignRight="@id/xbmc_name"
android:layout_alignEnd="@id/xbmc_name"
android:inputType="number"
android:ems="7"
android:hint="@string/wizard_xbmc_tcp_port"/>
@ -134,17 +142,46 @@
android:layout_height="wrap_content"
android:layout_alignTop="@+id/xbmc_tcp_port"
android:layout_alignLeft="@id/xbmc_name"
android:layout_alignStart="@id/xbmc_name"
android:layout_toLeftOf="@id/xbmc_tcp_port"
android:layout_toStartOf="@id/xbmc_tcp_port"
android:text="@string/wizard_xbmc_use_tcp"/>
<EditText
android:id="@+id/xbmc_event_server_port"
style="@style/TextAppearance.EditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/xbmc_tcp_port"
android:layout_alignLeft="@id/xbmc_tcp_port"
android:layout_alignStart="@id/xbmc_tcp_port"
android:layout_alignRight="@id/xbmc_tcp_port"
android:layout_alignEnd="@id/xbmc_tcp_port"
android:inputType="number"
android:ems="7"
android:hint="@string/wizard_xbmc_event_server_port"/>
<CheckBox
android:id="@+id/xbmc_use_event_server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/xbmc_event_server_port"
android:layout_alignLeft="@id/xbmc_name"
android:layout_alignStart="@id/xbmc_name"
android:layout_toLeftOf="@id/xbmc_event_server_port"
android:layout_toStartOf="@id/xbmc_event_server_port"
android:text="@string/wizard_xbmc_use_event_server"/>
<EditText
android:id="@+id/xbmc_wol_port"
style="@style/TextAppearance.EditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/xbmc_tcp_port"
android:layout_alignLeft="@id/xbmc_tcp_port"
android:layout_alignRight="@id/xbmc_tcp_port"
android:layout_below="@+id/xbmc_event_server_port"
android:layout_alignLeft="@id/xbmc_event_server_port"
android:layout_alignStart="@id/xbmc_event_server_port"
android:layout_alignRight="@id/xbmc_event_server_port"
android:layout_alignEnd="@id/xbmc_event_server_port"
android:inputType="number"
android:hint="@string/wizard_xbmc_wol_port"/>
@ -155,7 +192,9 @@
android:layout_height="wrap_content"
android:layout_alignTop="@+id/xbmc_wol_port"
android:layout_alignLeft="@id/xbmc_name"
android:layout_alignStart="@id/xbmc_name"
android:layout_toLeftOf="@id/xbmc_wol_port"
android:layout_toStartOf="@id/xbmc_wol_port"
android:inputType="text"
android:hint="@string/wizard_xbmc_mac_address"/>

View File

@ -98,11 +98,14 @@
<string name="wizard_xbmc_mac_address">MAC address</string>
<string name="wizard_xbmc_wol_port">WoL port (9)</string>
<string name="wizard_xbmc_use_tcp">Use TCP</string>
<string name="wizard_xbmc_event_server_port">ES Port (9777)</string>
<string name="wizard_xbmc_use_event_server">Use EventServer</string>
<string name="wizard_no_name_specified">Please specify a name for this media center, so you can identify it later.</string>
<string name="wizard_no_address_specified">Please specify the address of this media center, so I can locate it.</string>
<string name="wizard_invalid_http_port_specified">Please specify a valid HTTP port for this media center, so I can locate it.</string>
<string name="wizard_invalid_tcp_port_specified">Please specify a valid TCP port for this media center, so I can locate it.</string>
<string name="wizard_invalid_http_port_specified">Please specify a valid HTTP port for this media center.</string>
<string name="wizard_invalid_tcp_port_specified">Please specify a valid TCP port for this media center.</string>
<string name="wizard_invalid_es_port_specified">Please specify a valid EventServer port for this media center.</string>
<string name="wizard_connecting_to_xbmc_title">Connecting to %1$s…</string>
<string name="wizard_connecting_to_xbmc_message">Please wait while I try to connect to your media center…</string>