Add support for OnAVStart event and tweak HostConnectionOberver

This commit is contained in:
Synced Synapse 2019-02-08 16:42:33 +00:00 committed by Martijn Brekhof
parent e60df7e8ac
commit c043c04264
4 changed files with 233 additions and 154 deletions

View File

@ -58,10 +58,10 @@ public class HostConnectionObserver
public interface ApplicationEventsObserver { public interface ApplicationEventsObserver {
/** /**
* Notifies the observer that volume has changed * Notifies the observer that volume has changed
* @param volume * @param volume Volume level
* @param muted * @param muted Is muted
*/ */
public void applicationOnVolumeChanged(int volume, boolean muted); void applicationOnVolumeChanged(int volume, boolean muted);
} }
/** /**
@ -72,13 +72,13 @@ public class HostConnectionObserver
* Constants for possible events. Useful to save the last event and compare with the * Constants for possible events. Useful to save the last event and compare with the
* current one to check for differences * current one to check for differences
*/ */
public static final int PLAYER_NO_RESULT = 0, int PLAYER_NO_RESULT = 0,
PLAYER_CONNECTION_ERROR = 1, PLAYER_CONNECTION_ERROR = 1,
PLAYER_IS_PLAYING = 2, PLAYER_IS_PLAYING = 2,
PLAYER_IS_PAUSED = 3, PLAYER_IS_PAUSED = 3,
PLAYER_IS_STOPPED = 4; PLAYER_IS_STOPPED = 4;
public void playerOnPropertyChanged(NotificationsData notificationsData); void playerOnPropertyChanged(NotificationsData notificationsData);
/** /**
* Notifies that something is playing * Notifies that something is playing
@ -86,7 +86,7 @@ public class HostConnectionObserver
* @param getPropertiesResult Properties obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetProperties} * @param getPropertiesResult Properties obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetProperties}
* @param getItemResult Currently playing item, obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetItem} * @param getItemResult Currently playing item, obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetItem}
*/ */
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult, void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
PlayerType.PropertyValue getPropertiesResult, PlayerType.PropertyValue getPropertiesResult,
ListType.ItemsAll getItemResult); ListType.ItemsAll getItemResult);
@ -96,41 +96,41 @@ public class HostConnectionObserver
* @param getPropertiesResult Properties obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetProperties} * @param getPropertiesResult Properties obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetProperties}
* @param getItemResult Currently paused item, obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetItem} * @param getItemResult Currently paused item, obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetItem}
*/ */
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult, void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
PlayerType.PropertyValue getPropertiesResult, PlayerType.PropertyValue getPropertiesResult,
ListType.ItemsAll getItemResult); ListType.ItemsAll getItemResult);
/** /**
* Notifies that media is stopped/nothing is playing * Notifies that media is stopped/nothing is playing
*/ */
public void playerOnStop(); void playerOnStop();
/** /**
* Called when we get a connection error * Called when we get a connection error
* @param errorCode * @param errorCode Code
* @param description * @param description Description
*/ */
public void playerOnConnectionError(int errorCode, String description); void playerOnConnectionError(int errorCode, String description);
/** /**
* Notifies that we don't have a result yet * Notifies that we don't have a result yet
*/ */
public void playerNoResultsYet(); void playerNoResultsYet();
/** /**
* Notifies that XBMC has quit/shutdown/sleep * Notifies that XBMC has quit/shutdown/sleep
*/ */
public void systemOnQuit(); void systemOnQuit();
/** /**
* Notifies that XBMC has requested input * Notifies that XBMC has requested input
*/ */
public void inputOnInputRequested(String title, String type, String value); void inputOnInputRequested(String title, String type, String value);
/** /**
* Notifies the observer that it this is stopping * Notifies the observer that it this is stopping
*/ */
public void observerOnStopObserving(); void observerOnStopObserving();
} }
/** /**
@ -141,33 +141,26 @@ public class HostConnectionObserver
/** /**
* The list of observers * The list of observers
*/ */
private List<PlayerEventsObserver> playerEventsObservers = new ArrayList<PlayerEventsObserver>(); private List<PlayerEventsObserver> playerEventsObservers = new ArrayList<>();
private List<ApplicationEventsObserver> applicationEventsObservers = new ArrayList<>(); private List<ApplicationEventsObserver> applicationEventsObservers = new ArrayList<>();
// /**
// * Handlers for which observer, on which to notify them
// */
// private Map<PlayerEventsObserver, Handler> observerHandlerMap = new HashMap<PlayerEventsObserver, Handler>();
// Associate the Handler with the UI thread // Associate the Handler with the UI thread
private Handler checkerHandler = new Handler(Looper.getMainLooper()); private Handler checkerHandler = new Handler(Looper.getMainLooper());
private Runnable httpCheckerRunnable = new Runnable() { private Runnable httpCheckerRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
final int HTTP_NOTIFICATION_CHECK_INTERVAL = 3000; final int HTTP_NOTIFICATION_CHECK_INTERVAL = 2000;
boolean keepChecking = false; // If no one is listening to this, just exit
if ( ! playerEventsObservers.isEmpty() ) { if (playerEventsObservers.isEmpty() && applicationEventsObservers.isEmpty())
keepChecking = true; return;
if (!playerEventsObservers.isEmpty())
checkWhatsPlaying(); checkWhatsPlaying();
}
if ( ! applicationEventsObservers.isEmpty() ) { if (!applicationEventsObservers.isEmpty())
keepChecking = true;
getApplicationProperties(); getApplicationProperties();
}
if (keepChecking) checkerHandler.postDelayed(this, HTTP_NOTIFICATION_CHECK_INTERVAL);
checkerHandler.postDelayed(this, HTTP_NOTIFICATION_CHECK_INTERVAL);
} }
}; };
@ -184,24 +177,16 @@ public class HostConnectionObserver
ping.execute(connection, new ApiCallback<String>() { ping.execute(connection, new ApiCallback<String>() {
@Override @Override
public void onSuccess(String result) { public void onSuccess(String result) {
boolean keepChecking = false;
// Ok, we've got a ping, if there are playerEventsObservers and // Ok, we've got a ping, if there are playerEventsObservers and
// we were in a error or uninitialized state, update // we were in a error or uninitialized state, update
if ((! playerEventsObservers.isEmpty()) && if ((!playerEventsObservers.isEmpty()) &&
((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) || ((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) ||
(hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR))) { (hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR))) {
LogUtils.LOGD(TAG, "Checking what's playing because we don't have info about it");
checkWhatsPlaying(); checkWhatsPlaying();
keepChecking = true;
} }
if ( ! applicationEventsObservers.isEmpty() ) { checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
getApplicationProperties();
keepChecking = true;
}
if (keepChecking)
checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
} }
@Override @Override
@ -235,8 +220,6 @@ public class HostConnectionObserver
public HostState hostState; public HostState hostState;
private HostConnectionObserver() {}
public HostConnectionObserver(HostConnection connection) { public HostConnectionObserver(HostConnection connection) {
this.hostState = new HostState(); this.hostState = new HostState();
this.connection = connection; this.connection = connection;
@ -250,7 +233,7 @@ public class HostConnectionObserver
if (this.connection == null) if (this.connection == null)
return; return;
if ( ! playerEventsObservers.contains(observer) ) if (!playerEventsObservers.contains(observer))
playerEventsObservers.add(observer); playerEventsObservers.add(observer);
if (replyImmediately) replyWithLastResult(observer); if (replyImmediately) replyWithLastResult(observer);
@ -263,9 +246,8 @@ public class HostConnectionObserver
connection.registerSystemNotificationsObserver(this, checkerHandler); connection.registerSystemNotificationsObserver(this, checkerHandler);
connection.registerInputNotificationsObserver(this, checkerHandler); connection.registerInputNotificationsObserver(this, checkerHandler);
} }
startCheckerHandler();
} }
startCheckerHandler();
} }
/** /**
@ -274,7 +256,6 @@ public class HostConnectionObserver
*/ */
public void unregisterPlayerObserver(PlayerEventsObserver observer) { public void unregisterPlayerObserver(PlayerEventsObserver observer) {
playerEventsObservers.remove(observer); playerEventsObservers.remove(observer);
// observerHandlerMap.remove(observer);
LogUtils.LOGD(TAG, "Unregistering player observer " + observer.getClass().getSimpleName() + LogUtils.LOGD(TAG, "Unregistering player observer " + observer.getClass().getSimpleName() +
". Still got " + playerEventsObservers.size() + ". Still got " + playerEventsObservers.size() +
@ -295,13 +276,13 @@ public class HostConnectionObserver
/** /**
* Registers a new observer that will be notified about application events * Registers a new observer that will be notified about application events
* @param observer Observer * @param observer Observer
* @param replyImmediately * @param replyImmediately Wether to immediatlely issue a reply with the current status
*/ */
public void registerApplicationObserver(ApplicationEventsObserver observer, boolean replyImmediately) { public void registerApplicationObserver(ApplicationEventsObserver observer, boolean replyImmediately) {
if (this.connection == null) if (this.connection == null)
return; return;
if (! applicationEventsObservers.contains(observer) ) if (!applicationEventsObservers.contains(observer))
applicationEventsObservers.add(observer); applicationEventsObservers.add(observer);
if (replyImmediately) { if (replyImmediately) {
@ -318,9 +299,8 @@ public class HostConnectionObserver
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) { if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
connection.registerApplicationNotificationsObserver(this, checkerHandler); connection.registerApplicationNotificationsObserver(this, checkerHandler);
} }
startCheckerHandler();
} }
startCheckerHandler();
} }
/** /**
@ -343,6 +323,19 @@ public class HostConnectionObserver
} }
} }
private void startCheckerHandler() {
// Check if checkerHandler is already running, to prevent multiple runnables to be posted
// when multiple observers are registered.
if (checkerHandler.hasMessages(0))
return;
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
checkerHandler.post(tcpCheckerRunnable);
} else {
checkerHandler.post(httpCheckerRunnable);
}
}
/** /**
* Unregisters all observers * Unregisters all observers
*/ */
@ -372,35 +365,48 @@ public class HostConnectionObserver
/** /**
* The {@link HostConnection.PlayerNotificationsObserver} interface methods * The {@link HostConnection.PlayerNotificationsObserver} interface methods
* Start the chain calls to get whats playing
*/ */
public void onPlay(org.xbmc.kore.jsonrpc.notification.Player.OnPlay notification) { public void onPlay(org.xbmc.kore.jsonrpc.notification.Player.OnPlay notification) {
// Just start our chain calls // Ignore this if Kodi is Leia or higher, as we'll be properly notified via OnAVStart
chainCallGetActivePlayers(); // See https://github.com/xbmc/Kore/issues/602 and https://github.com/xbmc/xbmc/pull/13726
if (connection.getHostInfo().isLeiaOrLater()) {
LogUtils.LOGD(TAG, "OnPlay notification ignored. Will wait for OnAVStart.");
return;
}
checkWhatsPlaying();
} }
public void onResume(org.xbmc.kore.jsonrpc.notification.Player.OnResume notification) { public void onResume(org.xbmc.kore.jsonrpc.notification.Player.OnResume notification) {
// Just start our chain calls checkWhatsPlaying();
chainCallGetActivePlayers();
} }
public void onPause(org.xbmc.kore.jsonrpc.notification.Player.OnPause notification) { public void onPause(org.xbmc.kore.jsonrpc.notification.Player.OnPause notification) {
// Just start our chain calls checkWhatsPlaying();
chainCallGetActivePlayers();
} }
public void onSpeedChanged(org.xbmc.kore.jsonrpc.notification.Player.OnSpeedChanged notification) { public void onSpeedChanged(org.xbmc.kore.jsonrpc.notification.Player.OnSpeedChanged notification) {
// Just start our chain calls checkWhatsPlaying();
chainCallGetActivePlayers();
} }
public void onSeek(org.xbmc.kore.jsonrpc.notification.Player.OnSeek notification) { public void onSeek(org.xbmc.kore.jsonrpc.notification.Player.OnSeek notification) {
// Just start our chain calls checkWhatsPlaying();
chainCallGetActivePlayers();
} }
public void onStop(org.xbmc.kore.jsonrpc.notification.Player.OnStop notification) { public void onStop(org.xbmc.kore.jsonrpc.notification.Player.OnStop notification) {
// Just start our chain calls // We could directly notify that nothing is playing here, but in Kodi Leia everytime
notifyNothingIsPlaying(playerEventsObservers); // there's a playlist change, onStop is triggered, which caused the UI to display
// that nothing was being played. Checking what's playing prevents this.
checkWhatsPlaying();
}
public void onAVStart(org.xbmc.kore.jsonrpc.notification.Player.OnAVStart notification) {
checkWhatsPlaying();
}
public void onAVChange(org.xbmc.kore.jsonrpc.notification.Player.OnAVChange notification) {
// Just ignore this, as it is fired by Kodi very often, and we're only
// interested in play/resume/stop changes
} }
/** /**
@ -448,18 +454,6 @@ public class HostConnectionObserver
} }
} }
private void startCheckerHandler() {
// Check if checkerHandler is already running, to prevent multiple runnables to be posted
// when multiple observers are registered.
if ( checkerHandler.hasMessages(0) )
return;
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
checkerHandler.post(tcpCheckerRunnable);
} else {
checkerHandler.post(httpCheckerRunnable);
}
}
private void getApplicationProperties() { private void getApplicationProperties() {
org.xbmc.kore.jsonrpc.method.Application.GetProperties getProperties = org.xbmc.kore.jsonrpc.method.Application.GetProperties getProperties =
new org.xbmc.kore.jsonrpc.method.Application.GetProperties(org.xbmc.kore.jsonrpc.method.Application.GetProperties.VOLUME, new org.xbmc.kore.jsonrpc.method.Application.GetProperties(org.xbmc.kore.jsonrpc.method.Application.GetProperties.VOLUME,
@ -483,10 +477,23 @@ public class HostConnectionObserver
}, checkerHandler); }, checkerHandler);
} }
/**
* Indicator set when we are calling Kodi to check what's playing, so that we don't call it
* while there are still pending calls
*/
private boolean checkingWhatsPlaying = false;
/** /**
* Checks whats playing and notifies observers * Checks whats playing and notifies observers
*/ */
private void checkWhatsPlaying() { private void checkWhatsPlaying() {
// We don't properly protect this against race conditions because it's
// not worth the trouble - we can safely call Kodi multiple times.
if (checkingWhatsPlaying) {
LogUtils.LOGD(TAG, "Already checking whats playing, returning");
return;
}
checkingWhatsPlaying = true;
LogUtils.LOGD(TAG, "Checking whats playing"); LogUtils.LOGD(TAG, "Checking whats playing");
// Start the calls: Player.GetActivePlayers -> Player.GetProperties -> Player.GetItem // Start the calls: Player.GetActivePlayers -> Player.GetProperties -> Player.GetItem
@ -627,6 +634,7 @@ public class HostConnectionObserver
* @param observers List of observers * @param observers List of observers
*/ */
private void notifyConnectionError(final int errorCode, final String description, List<PlayerEventsObserver> observers) { private void notifyConnectionError(final int errorCode, final String description, List<PlayerEventsObserver> observers) {
checkingWhatsPlaying = false;
// Reply if different from last result // Reply if different from last result
if (forceReply || if (forceReply ||
(hostState.lastCallResult != PlayerEventsObserver.PLAYER_CONNECTION_ERROR) || (hostState.lastCallResult != PlayerEventsObserver.PLAYER_CONNECTION_ERROR) ||
@ -652,22 +660,15 @@ public class HostConnectionObserver
*/ */
private void notifyConnectionError(final int errorCode, final String description, PlayerEventsObserver observer) { private void notifyConnectionError(final int errorCode, final String description, PlayerEventsObserver observer) {
observer.playerOnConnectionError(errorCode, description); observer.playerOnConnectionError(errorCode, description);
// Handler observerHandler = observerHandlerMap.get(observer);
// observerHandler.post(new Runnable() {
// @Override
// public void run() {
// observer.playerOnConnectionError(errorCode, description);
// }
// });
} }
/** /**
* Nothing is playing, notify observers calling playerOnStop * Nothing is playing, notify observers calling playerOnStop
* Only notifies them if the result is different from the last one * Only notifies them if the result is different from the last one
* @param observers List of observers * @param observers List of observers
*/ */
private void notifyNothingIsPlaying(List<PlayerEventsObserver> observers) { private void notifyNothingIsPlaying(List<PlayerEventsObserver> observers) {
checkingWhatsPlaying = false;
// Reply if forced or different from last result // Reply if forced or different from last result
if (forceReply || if (forceReply ||
(hostState.lastCallResult != PlayerEventsObserver.PLAYER_IS_STOPPED)) { (hostState.lastCallResult != PlayerEventsObserver.PLAYER_IS_STOPPED)) {
@ -706,15 +707,16 @@ public class HostConnectionObserver
/** /**
* Something is playing or paused, notify observers * Something is playing or paused, notify observers
* Only notifies them if the result is different from the last one * Only notifies them if the result is different from the last one
* @param getActivePlayersResult * @param getActivePlayersResult Previous call result
* @param getPropertiesResult * @param getPropertiesResult Previous call result
* @param getItemResult * @param getItemResult Previous call result
* @param observers List of observers * @param observers List of observers
*/ */
private void notifySomethingIsPlaying(final PlayerType.GetActivePlayersReturnType getActivePlayersResult, private void notifySomethingIsPlaying(final PlayerType.GetActivePlayersReturnType getActivePlayersResult,
final PlayerType.PropertyValue getPropertiesResult, final PlayerType.PropertyValue getPropertiesResult,
final ListType.ItemsAll getItemResult, final ListType.ItemsAll getItemResult,
List<PlayerEventsObserver> observers) { List<PlayerEventsObserver> observers) {
checkingWhatsPlaying = false;
int currentCallResult = (getPropertiesResult.speed == 0) ? int currentCallResult = (getPropertiesResult.speed == 0) ?
PlayerEventsObserver.PLAYER_IS_PAUSED : PlayerEventsObserver.PLAYER_IS_PLAYING; PlayerEventsObserver.PLAYER_IS_PAUSED : PlayerEventsObserver.PLAYER_IS_PLAYING;
if (forceReply || if (forceReply ||
@ -740,7 +742,7 @@ public class HostConnectionObserver
if ((currentCallResult == PlayerEventsObserver.PLAYER_IS_PLAYING) && if ((currentCallResult == PlayerEventsObserver.PLAYER_IS_PLAYING) &&
(connection.getProtocol() == HostConnection.PROTOCOL_TCP) && (connection.getProtocol() == HostConnection.PROTOCOL_TCP) &&
(getPropertiesResult.time.ToSeconds() == 0)) { (getPropertiesResult.time.ToSeconds() == 0)) {
LogUtils.LOGD(TAG, "Scheduling new call to check what's playing."); LogUtils.LOGD(TAG, "Scheduling new call to check what's playing because time is 0.");
final int RECHECK_INTERVAL = 3000; final int RECHECK_INTERVAL = 3000;
checkerHandler.postDelayed(new Runnable() { checkerHandler.postDelayed(new Runnable() {
@Override @Override
@ -750,15 +752,14 @@ public class HostConnectionObserver
} }
}, RECHECK_INTERVAL); }, RECHECK_INTERVAL);
} }
} }
/** /**
* Something is playing or paused, notify a specific observer * Something is playing or paused, notify a specific observer
* Always notifies the observer, and doesn't save results in last call * Always notifies the observer, and doesn't save results in last call
* @param getActivePlayersResult * @param getActivePlayersResult Previous call result
* @param getPropertiesResult * @param getPropertiesResult Previous call result
* @param getItemResult * @param getItemResult Previous call result
* @param observer Specific observer * @param observer Specific observer
*/ */
private void notifySomethingIsPlaying(final PlayerType.GetActivePlayersReturnType getActivePlayersResult, private void notifySomethingIsPlaying(final PlayerType.GetActivePlayersReturnType getActivePlayersResult,
@ -801,8 +802,9 @@ public class HostConnectionObserver
* Forces a refresh of the current cached results * Forces a refresh of the current cached results
*/ */
public void forceRefreshResults() { public void forceRefreshResults() {
LogUtils.LOGD(TAG, "Forcing a refresh");
forceReply = true; forceReply = true;
chainCallGetActivePlayers(); checkWhatsPlaying();
} }
public HostState getHostState() { public HostState getHostState() {

View File

@ -307,7 +307,11 @@ public class HostInfo {
return kodiVersionMajor >= KODI_V17_KRYPTON; return kodiVersionMajor >= KODI_V17_KRYPTON;
} }
/** public boolean isLeiaOrLater() {
return kodiVersionMajor >= KODI_V18_LEIA;
}
/**
* Returns the URL of the host * Returns the URL of the host
* @return HTTP URL eg. http://192.168.1.1:8080 * @return HTTP URL eg. http://192.168.1.1:8080
*/ */

View File

@ -77,6 +77,8 @@ public class HostConnection {
void onSpeedChanged(Player.OnSpeedChanged notification); void onSpeedChanged(Player.OnSpeedChanged notification);
void onSeek(Player.OnSeek notification); void onSeek(Player.OnSeek notification);
void onStop(Player.OnStop notification); void onStop(Player.OnStop notification);
void onAVStart(Player.OnAVStart notification);
void onAVChange(Player.OnAVChange notification);
} }
/** /**
@ -126,25 +128,25 @@ public class HostConnection {
* {@link java.util.HashMap} that will hold the {@link MethodCallInfo} with the information * {@link java.util.HashMap} that will hold the {@link MethodCallInfo} with the information
* necessary to respond to clients (TCP only) * necessary to respond to clients (TCP only)
*/ */
private final HashMap<String, MethodCallInfo<?>> clientCallbacks = new HashMap<String, MethodCallInfo<?>>(); private final HashMap<String, MethodCallInfo<?>> clientCallbacks = new HashMap<>();
/** /**
* The observers that will be notified of player notifications * The observers that will be notified of player notifications
*/ */
private final HashMap<PlayerNotificationsObserver, Handler> playerNotificationsObservers = private final HashMap<PlayerNotificationsObserver, Handler> playerNotificationsObservers =
new HashMap<PlayerNotificationsObserver, Handler>(); new HashMap<>();
/** /**
* The observers that will be notified of system notifications * The observers that will be notified of system notifications
*/ */
private final HashMap<SystemNotificationsObserver, Handler> systemNotificationsObservers = private final HashMap<SystemNotificationsObserver, Handler> systemNotificationsObservers =
new HashMap<SystemNotificationsObserver, Handler>(); new HashMap<>();
/** /**
* The observers that will be notified of input notifications * The observers that will be notified of input notifications
*/ */
private final HashMap<InputNotificationsObserver, Handler> inputNotificationsObservers = private final HashMap<InputNotificationsObserver, Handler> inputNotificationsObservers =
new HashMap<InputNotificationsObserver, Handler>(); new HashMap<>();
/** /**
* The observers that will be notified of application notifications * The observers that will be notified of application notifications
@ -317,10 +319,9 @@ public class HostConnection {
" on host: " + hostInfo.getJsonRpcHttpEndpoint()); " on host: " + hostInfo.getJsonRpcHttpEndpoint());
if (protocol == PROTOCOL_TCP) { if (protocol == PROTOCOL_TCP) {
/** // Do not call this from the runnable below as it may cause a race condition
* Do not call this from the runnable below as it may cause a race condition // with {@link #updateClientCallback(int, ApiCallback, Handler)}
* with {@link #updateClientCallback(int, ApiCallback, Handler)} //
*/
// Save this method/callback for any later response // Save this method/callback for any later response
addClientCallback(method, callback, handler); addClientCallback(method, callback, handler);
} }
@ -411,10 +412,10 @@ public class HostConnection {
/** /**
* Stores the method and callback to handle asynchronous responses. * Stores the method and callback to handle asynchronous responses.
* Note this is only needed for requests over TCP. * Note this is only needed for requests over TCP.
* @param method * @param method Method
* @param callback * @param callback Callback
* @param handler * @param handler Handler
* @param <T> * @param <T> Method/Callback type
*/ */
private <T> void addClientCallback(final ApiMethod<T> method, final ApiCallback<T> callback, private <T> void addClientCallback(final ApiMethod<T> method, final ApiCallback<T> callback,
final Handler handler) { final Handler handler) {
@ -437,7 +438,7 @@ public class HostConnection {
} }
return; return;
} }
clientCallbacks.put(methodId, new MethodCallInfo<T>(method, callback, handler)); clientCallbacks.put(methodId, new MethodCallInfo<>(method, callback, handler));
} }
} }
@ -489,7 +490,7 @@ public class HostConnection {
httpClient.setAuthenticator(new Authenticator() { httpClient.setAuthenticator(new Authenticator() {
@Override @Override
public Request authenticate(Proxy proxy, Response response) throws IOException { public Request authenticate(Proxy proxy, Response response) {
if (TextUtils.isEmpty(hostInfo.getUsername())) if (TextUtils.isEmpty(hostInfo.getUsername()))
return null; return null;
@ -498,7 +499,7 @@ public class HostConnection {
} }
@Override @Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException { public Request authenticateProxy(Proxy proxy, Response response) {
return null; return null;
} }
}); });
@ -517,7 +518,7 @@ public class HostConnection {
/** /**
* Send an OkHttp POST request * Send an OkHttp POST request
* @param request Request to send * @param request Request to send
* @throws ApiException * @throws ApiException {@link ApiException} if request can't be sent
*/ */
private Response sendOkHttpRequest(final OkHttpClient client, final Request request) throws ApiException { private Response sendOkHttpRequest(final OkHttpClient client, final Request request) throws ApiException {
try { try {
@ -543,7 +544,7 @@ public class HostConnection {
* Reads the response from the server * Reads the response from the server
* @param response Response from OkHttp * @param response Response from OkHttp
* @return Response body string * @return Response body string
* @throws ApiException * @throws ApiException {@link ApiException} if response can't be read/processed
*/ */
private String handleOkHttpResponse(Response response) throws ApiException { private String handleOkHttpResponse(Response response) throws ApiException {
try { try {
@ -582,7 +583,7 @@ public class HostConnection {
* If it is an error (contains the error tag), returns an {@link ApiException} with the info. * If it is an error (contains the error tag), returns an {@link ApiException} with the info.
* @param response JSON response * @param response JSON response
* @return {@link com.fasterxml.jackson.databind.node.ObjectNode} constructed * @return {@link com.fasterxml.jackson.databind.node.ObjectNode} constructed
* @throws ApiException * @throws ApiException Exception trown if we can't parse the response
*/ */
private ObjectNode parseJsonResponse(String response) throws ApiException { private ObjectNode parseJsonResponse(String response) throws ApiException {
// LogUtils.LOGD(TAG, "Parsing JSON response"); // LogUtils.LOGD(TAG, "Parsing JSON response");
@ -661,7 +662,7 @@ public class HostConnection {
* Send a TCP request * Send a TCP request
* @param socket Socket to write to * @param socket Socket to write to
* @param request Request to send * @param request Request to send
* @throws ApiException * @throws ApiException Exception if can't send
*/ */
private void sendTcpRequest(Socket socket, String request) throws ApiException { private void sendTcpRequest(Socket socket, String request) throws ApiException {
try { try {
@ -713,7 +714,7 @@ public class HostConnection {
ignoreTcpResponse = false; ignoreTcpResponse = false;
ignore = true; ignore = true;
} }
LogUtils.LOGD(TAG, "ignore tcp response - " + ignore); //LogUtils.LOGD(TAG, "ignore tcp response - " + ignore);
return ignore; return ignore;
} }
@ -726,10 +727,10 @@ public class HostConnection {
String notificationName = jsonResponse.get(ApiNotification.METHOD_NODE).asText(); String notificationName = jsonResponse.get(ApiNotification.METHOD_NODE).asText();
ObjectNode params = (ObjectNode)jsonResponse.get(ApiNotification.PARAMS_NODE); ObjectNode params = (ObjectNode)jsonResponse.get(ApiNotification.PARAMS_NODE);
if (notificationName.equals(Player.OnPause.NOTIFICATION_NAME)) { switch (notificationName) {
case Player.OnPause.NOTIFICATION_NAME: {
final Player.OnPause apiNotification = new Player.OnPause(params); final Player.OnPause apiNotification = new Player.OnPause(params);
for (final PlayerNotificationsObserver observer : for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer); Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -738,10 +739,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Player.OnPlay.NOTIFICATION_NAME)) { break;
}
case Player.OnPlay.NOTIFICATION_NAME: {
final Player.OnPlay apiNotification = new Player.OnPlay(params); final Player.OnPlay apiNotification = new Player.OnPlay(params);
for (final PlayerNotificationsObserver observer : for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer); Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -750,10 +752,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Player.OnResume.NOTIFICATION_NAME)) { break;
}
case Player.OnResume.NOTIFICATION_NAME: {
final Player.OnResume apiNotification = new Player.OnResume(params); final Player.OnResume apiNotification = new Player.OnResume(params);
for (final PlayerNotificationsObserver observer : for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer); Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -762,10 +765,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Player.OnSeek.NOTIFICATION_NAME)) { break;
}
case Player.OnSeek.NOTIFICATION_NAME: {
final Player.OnSeek apiNotification = new Player.OnSeek(params); final Player.OnSeek apiNotification = new Player.OnSeek(params);
for (final PlayerNotificationsObserver observer : for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer); Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -774,10 +778,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Player.OnSpeedChanged.NOTIFICATION_NAME)) { break;
}
case Player.OnSpeedChanged.NOTIFICATION_NAME: {
final Player.OnSpeedChanged apiNotification = new Player.OnSpeedChanged(params); final Player.OnSpeedChanged apiNotification = new Player.OnSpeedChanged(params);
for (final PlayerNotificationsObserver observer : for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer); Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -786,10 +791,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Player.OnStop.NOTIFICATION_NAME)) { break;
}
case Player.OnStop.NOTIFICATION_NAME: {
final Player.OnStop apiNotification = new Player.OnStop(params); final Player.OnStop apiNotification = new Player.OnStop(params);
for (final PlayerNotificationsObserver observer : for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer); Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -798,10 +804,37 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Player.OnPropertyChanged.NOTIFICATION_NAME)) { break;
}
case Player.OnAVStart.NOTIFICATION_NAME: {
final Player.OnAVStart apiNotification = new Player.OnAVStart(params);
for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() {
@Override
public void run() {
observer.onAVStart(apiNotification);
}
});
}
break;
}
case Player.OnAVChange.NOTIFICATION_NAME: {
final Player.OnAVChange apiNotification = new Player.OnAVChange(params);
for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() {
@Override
public void run() {
observer.onAVChange(apiNotification);
}
});
}
break;
}
case Player.OnPropertyChanged.NOTIFICATION_NAME: {
final Player.OnPropertyChanged apiNotification = new Player.OnPropertyChanged(params); final Player.OnPropertyChanged apiNotification = new Player.OnPropertyChanged(params);
for (final PlayerNotificationsObserver observer : for (final PlayerNotificationsObserver observer : playerNotificationsObservers.keySet()) {
playerNotificationsObservers.keySet()) {
Handler handler = playerNotificationsObservers.get(observer); Handler handler = playerNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -810,10 +843,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(System.OnQuit.NOTIFICATION_NAME)) { break;
}
case System.OnQuit.NOTIFICATION_NAME: {
final System.OnQuit apiNotification = new System.OnQuit(params); final System.OnQuit apiNotification = new System.OnQuit(params);
for (final SystemNotificationsObserver observer : for (final SystemNotificationsObserver observer : systemNotificationsObservers.keySet()) {
systemNotificationsObservers.keySet()) {
Handler handler = systemNotificationsObservers.get(observer); Handler handler = systemNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -822,10 +856,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(System.OnRestart.NOTIFICATION_NAME)) { break;
}
case System.OnRestart.NOTIFICATION_NAME: {
final System.OnRestart apiNotification = new System.OnRestart(params); final System.OnRestart apiNotification = new System.OnRestart(params);
for (final SystemNotificationsObserver observer : for (final SystemNotificationsObserver observer : systemNotificationsObservers.keySet()) {
systemNotificationsObservers.keySet()) {
Handler handler = systemNotificationsObservers.get(observer); Handler handler = systemNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -834,10 +869,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(System.OnSleep.NOTIFICATION_NAME)) { break;
}
case System.OnSleep.NOTIFICATION_NAME: {
final System.OnSleep apiNotification = new System.OnSleep(params); final System.OnSleep apiNotification = new System.OnSleep(params);
for (final SystemNotificationsObserver observer : for (final SystemNotificationsObserver observer : systemNotificationsObservers.keySet()) {
systemNotificationsObservers.keySet()) {
Handler handler = systemNotificationsObservers.get(observer); Handler handler = systemNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -846,10 +882,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Input.OnInputRequested.NOTIFICATION_NAME)) { break;
}
case Input.OnInputRequested.NOTIFICATION_NAME: {
final Input.OnInputRequested apiNotification = new Input.OnInputRequested(params); final Input.OnInputRequested apiNotification = new Input.OnInputRequested(params);
for (final InputNotificationsObserver observer : for (final InputNotificationsObserver observer : inputNotificationsObservers.keySet()) {
inputNotificationsObservers.keySet()) {
Handler handler = inputNotificationsObservers.get(observer); Handler handler = inputNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -858,11 +895,11 @@ public class HostConnection {
} }
}); });
} }
} else if (notificationName.equals(Application.OnVolumeChanged.NOTIFICATION_NAME)) { break;
final Application.OnVolumeChanged apiNotification = }
new Application.OnVolumeChanged(params); case Application.OnVolumeChanged.NOTIFICATION_NAME: {
for (final ApplicationNotificationsObserver observer : final Application.OnVolumeChanged apiNotification = new Application.OnVolumeChanged(params);
applicationNotificationsObservers.keySet()) { for (final ApplicationNotificationsObserver observer : applicationNotificationsObservers.keySet()) {
Handler handler = applicationNotificationsObservers.get(observer); Handler handler = applicationNotificationsObservers.get(observer);
postOrRunNow(handler, new Runnable() { postOrRunNow(handler, new Runnable() {
@Override @Override
@ -871,8 +908,8 @@ public class HostConnection {
} }
}); });
} }
} break;
}}
LogUtils.LOGD(TAG, "Got a notification: " + jsonResponse.get("method").textValue()); LogUtils.LOGD(TAG, "Got a notification: " + jsonResponse.get("method").textValue());
} else { } else {
String methodId = jsonResponse.get(ApiMethod.ID_NODE).asText(); String methodId = jsonResponse.get(ApiMethod.ID_NODE).asText();

View File

@ -158,6 +158,42 @@ public class Player {
public String getNotificationName() { return NOTIFICATION_NAME; } public String getNotificationName() { return NOTIFICATION_NAME; }
} }
/**
* Player.OnAVStart notification
* Will be triggered on playback start if the first frame was drawn.
* If there is no ID available extra information will be provided
*/
public static class OnAVStart extends ApiNotification {
public static final String NOTIFICATION_NAME = "Player.OnAVStart";
public final NotificationsData data;
public OnAVStart(ObjectNode node) {
super(node);
data = new NotificationsData(node.get(NotificationsData.DATA_NODE));
}
public String getNotificationName() { return NOTIFICATION_NAME; }
}
/**
* Player.OnAVChange notification
* Audio- or videostream has changed.
* If there is no ID available extra information will be provided
*/
public static class OnAVChange extends ApiNotification {
public static final String NOTIFICATION_NAME = "Player.OnAVChange";
public final NotificationsData data;
public OnAVChange(ObjectNode node) {
super(node);
data = new NotificationsData(node.get(NotificationsData.DATA_NODE));
}
public String getNotificationName() { return NOTIFICATION_NAME; }
}
/** /**
* Notification data for Player * Notification data for Player
*/ */