package com.dataart.android.devicehive.client; import java.util.LinkedList; import java.util.List; import java.util.Queue; import android.content.Context; import android.os.Bundle; import android.util.Log; import com.dataart.android.devicehive.ApiInfo; import com.dataart.android.devicehive.Command; import com.dataart.android.devicehive.DeviceData; import com.dataart.android.devicehive.DeviceHive; import com.dataart.android.devicehive.Notification; import com.dataart.android.devicehive.client.commands.GetDeviceCommand; import com.dataart.android.devicehive.client.commands.NotificationsRetrivalCommand; import com.dataart.android.devicehive.client.commands.PollDeviceNotificationsCommand; import com.dataart.android.devicehive.client.commands.PollMultipleDeviceNotificationsCommand; import com.dataart.android.devicehive.client.commands.SendDeviceCommandCommand; import com.dataart.android.devicehive.commands.GetApiInfoCommand; import com.dataart.android.devicehive.network.DeviceHiveResultReceiver; import com.dataart.android.devicehive.network.NetworkCommand; import com.dataart.android.devicehive.network.NetworkCommandConfig; import com.dataart.android.devicehive.network.ServiceConnection; /* package */abstract class ClientServiceConnection extends ServiceConnection { private DeviceClient client; private final Queue<Notification> notificationQueue = new LinkedList<Notification>(); private boolean isReceivingNotifications = false; private String username; private String password; private boolean isPollRequestInProgress = false; private String lastNotificationPollTimestamp; private Integer notificationPollWaitTimeout; public ClientServiceConnection(Context context) { super(context); } /* package */void setAuthorisation(String username, String password) { this.username = username; this.password = password; } /* package */void setLastNotificationPollTimestamp(String timestamp) { this.lastNotificationPollTimestamp = timestamp; } /* package */String getLastNotificationPollTimestamp() { return lastNotificationPollTimestamp; } /* package */void setNotificationPollWaitTimeout(Integer timeout) { this.notificationPollWaitTimeout = timeout; } /* package */void sendCommand(DeviceData deviceData, Command command) { logD("Sending command: " + command.getCommand()); client.onStartSendingCommand(command); startNetworkCommand(new SendDeviceCommandCommand(deviceData.getId(), command)); } /* package */void reloadDeviceData(DeviceData deviceData) { startNetworkCommand(new GetDeviceCommand(deviceData.getId())); } /* package */void startReceivingNotifications() { if (isReceivingNotifications) { stopReceivingNotifications(); } isReceivingNotifications = true; handleNextNotification(); } /* package */void stopReceivingNotifications() { detachResultReceiver(); isReceivingNotifications = false; isPollRequestInProgress = false; } /* package */void setClient(DeviceClient client) { this.client = client; } /* package */boolean isReceivingNotifications() { return isReceivingNotifications; } private void handleNotification(final Notification notification) { if (client.shouldReceiveNotificationAsynchronously(notification)) { asyncHandler.post(new Runnable() { @Override public void run() { didReceiveNotification(notification); mainThreadHandler.post(new Runnable() { @Override public void run() { if (isReceivingNotifications) { handleNextNotification(); } } }); } }); } else { didReceiveNotification(notification); if (isReceivingNotifications) { handleNextNotification(); } } } private void handleNextNotification() { final Notification notification = notificationQueue.poll(); if (notification != null) { handleNotification(notification); } else { if (!isPollRequestInProgress) { isPollRequestInProgress = true; if (lastNotificationPollTimestamp == null) { // timestamp wasn't specified. Request and use server // timestamp instead. logD("Starting Get API info command"); startNetworkCommand(new GetApiInfoCommand()); } else { startPollNotificationsRequest(); } } } } protected abstract NotificationsRetrivalCommand getPollNotificationsCommand( String lastNotificationPollTimestamp, Integer waitTimeout); protected abstract void didReceiveNotification(Notification notification); private void startPollNotificationsRequest() { logD("Starting polling request with lastNotificationPollTimestamp = " + lastNotificationPollTimestamp); startNetworkCommand(getPollNotificationsCommand( lastNotificationPollTimestamp, notificationPollWaitTimeout)); } private int enqueueNotifications(List<Notification> notifications) { if (notifications == null || notifications.isEmpty()) { return 0; } int enqueuedCount = 0; for (Notification notification : notifications) { boolean added = notificationQueue.offer(notification); if (!added) { Log.e(DeviceHive.TAG, "Failed to add notification to the queue"); } else { enqueuedCount++; } } return enqueuedCount; } @Override protected void onReceiveResult(final int resultCode, final int tagId, final Bundle resultData) { switch (resultCode) { case DeviceHiveResultReceiver.MSG_HANDLED_RESPONSE: logD("Handled response"); if (tagId == TAG_SEND_COMMAND) { Command command = SendDeviceCommandCommand .getSentCommand(resultData); logD("Command sent with response: " + command); client.onFinishSendingCommand(command); } else if (tagId == TAG_POLL_NOTIFICATIONS || tagId == TAG_POLL_MULTIPLE_NOTIFICATIONS) { logD("Poll request finished"); isPollRequestInProgress = false; List<Notification> notifications = PollDeviceNotificationsCommand .getNotifications(resultData); logD("-------Received notifications: " + notifications); logD("Notifications count: " + notifications.size()); int enqueuedCount = enqueueNotifications(notifications); logD("Enqueued notifications count: " + enqueuedCount); if (!notifications.isEmpty()) { lastNotificationPollTimestamp = notifications.get( notifications.size() - 1).getTimestamp(); } if (isReceivingNotifications) { handleNextNotification(); } } else if (tagId == TAG_GET_DEVICE) { logD("Get device request finished"); final DeviceData deviceData = GetDeviceCommand .getDevice(resultData); client.onReloadDeviceDataFinishedInternal(deviceData); } else if (tagId == TAG_GET_API_INFO) { final ApiInfo apiInfo = GetApiInfoCommand .getApiInfo(resultData); logD("Get API info request finished: " + apiInfo); lastNotificationPollTimestamp = apiInfo.getServerTimestamp(); logD("Starting polling request with lastNotificationPollTimestamp = " + lastNotificationPollTimestamp); startPollNotificationsRequest(); } break; case DeviceHiveResultReceiver.MSG_EXCEPTION: final Throwable exception = NetworkCommand.getThrowable(resultData); Log.e(DeviceHive.TAG, "DeviceHiveResultReceiver.MSG_EXCEPTION", exception); case DeviceHiveResultReceiver.MSG_STATUS_FAILURE: if (tagId == TAG_SEND_COMMAND) { SendDeviceCommandCommand command = (SendDeviceCommandCommand) NetworkCommand .getCommand(resultData); client.onFailSendingCommand(command.getCommand()); } else if (tagId == TAG_POLL_NOTIFICATIONS || tagId == TAG_POLL_MULTIPLE_NOTIFICATIONS || tagId == TAG_GET_API_INFO) { Log.d(DeviceHive.TAG, "Failed to poll notifications"); isPollRequestInProgress = false; if (isReceivingNotifications) { handleNextNotification(); } } else if (tagId == TAG_GET_DEVICE) { client.onReloadDeviceDataFailedInternal(); } break; } } @Override protected NetworkCommandConfig getCommandConfig() { final NetworkCommandConfig config = super.getCommandConfig(); config.setBasicAuthorisation(username, password); return config; } private final static int TAG_SEND_COMMAND = getTagId(SendDeviceCommandCommand.class); private final static int TAG_POLL_NOTIFICATIONS = getTagId(PollDeviceNotificationsCommand.class); private final static int TAG_POLL_MULTIPLE_NOTIFICATIONS = getTagId(PollMultipleDeviceNotificationsCommand.class); private final static int TAG_GET_DEVICE = getTagId(GetDeviceCommand.class); private static final int TAG_GET_API_INFO = getTagId(GetApiInfoCommand.class); }