D-Pad on the remote screen now uses EventServer by default.
Not finished: EventServer port is still hardcoded (9777) and there's no possibility of configuration.
This commit is contained in:
parent
a9e0a07572
commit
8ce709c55a
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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.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 final InetAddress hostInetAddress;
|
||||
private boolean isConnected = false;
|
||||
|
||||
// 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");
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor. Starts the thread that keeps the connection alive. Make sure to call quit() when done.
|
||||
* @param hostInfo Host to connect to
|
||||
* @throws UnknownHostException
|
||||
*/
|
||||
public EventServerConnection(final HostInfo hostInfo) throws UnknownHostException{
|
||||
this.hostInfo = hostInfo;
|
||||
hostInetAddress = InetAddress.getByName(hostInfo.getAddress());
|
||||
|
||||
startEventClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the HandlerThread that will be used to post packets, establishes a connection with EventServer
|
||||
* (sends HELO packet) and starts the ping thread
|
||||
*/
|
||||
private void startEventClient() {
|
||||
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());
|
||||
|
||||
// Send Hello Packet
|
||||
// commHandler.post(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// PacketHELO p;
|
||||
// p = new PacketHELO(DEVICE_NAME);
|
||||
// try {
|
||||
// p.send(hostInetAddress, hostInfo.getEventServerPort());
|
||||
// } catch (IOException exc) {
|
||||
// // We are ignoring this one... Not sure if a good idea, but we're not on the UI thread
|
||||
// LogUtils.LOGD(TAG, "Got an IOException when sending a HELO Packet to Kodi's EventServer");
|
||||
// }
|
||||
//
|
||||
// // Start pinging
|
||||
// commHandler.postDelayed(pingRunnable, PING_INTERVAL);
|
||||
// }
|
||||
// });
|
||||
// Start pinging
|
||||
commHandler.postDelayed(pingRunnable, PING_INTERVAL);
|
||||
|
||||
isConnected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the HandlerThread that is being used to send packets to Kodi
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public void quit() {
|
||||
LogUtils.LOGD(TAG, "Quiting EventServer handler thread");
|
||||
if (Utils.isJellybeanMR2OrLater()) {
|
||||
handlerThread.quitSafely();
|
||||
} else {
|
||||
handlerThread.quit();
|
||||
}
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (!isConnected) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -60,6 +65,7 @@ public class HostInfo {
|
|||
private String address;
|
||||
private int httpPort;
|
||||
private int tcpPort;
|
||||
private int eventServerPort;
|
||||
|
||||
/**
|
||||
* Authentication information
|
||||
|
@ -108,6 +114,8 @@ public class HostInfo {
|
|||
this.macAddress = macAddress;
|
||||
this.wolPort = wolPort;
|
||||
|
||||
this.eventServerPort = DEFAULT_EVENT_SERVER_PORT;
|
||||
|
||||
// For performance reasons
|
||||
this.auxImageHttpAddress = getHttpURL() + "/image/";
|
||||
}
|
||||
|
@ -177,7 +185,11 @@ public class HostInfo {
|
|||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
public int getEventServerPort() {
|
||||
return eventServerPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the protocol for this host info
|
||||
* @param protocol Protocol
|
||||
*/
|
||||
|
|
|
@ -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,29 @@ 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 (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 +206,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 +254,42 @@ 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() {
|
||||
try {
|
||||
eventServerConnection = new EventServerConnection(hostManager.getHostInfo());
|
||||
} catch (UnknownHostException exc) {
|
||||
LogUtils.LOGD(TAG, "Got an UnknownHostException, disabling EventServer");
|
||||
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 +311,41 @@ public class RemoteFragment extends Fragment
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue