heartbeatapp/app/src/main/java/com/rubenvandeven/heartbeatstreamer/heartrate/HeartRateService.java

227 lines
8.8 KiB
Java

package com.rubenvandeven.heartbeatstreamer.heartrate;
import android.app.AlertDialog;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc;
import com.dsi.ant.plugins.antplus.pcc.defines.DeviceState;
import com.dsi.ant.plugins.antplus.pcc.defines.EventFlag;
import com.dsi.ant.plugins.antplus.pcc.defines.RequestAccessResult;
import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc;
import com.dsi.ant.plugins.antplus.pccbase.AsyncScanController;
import com.dsi.ant.plugins.antplus.pccbase.PccReleaseHandle;
import com.koushikdutta.async.future.Future;
import com.koushikdutta.async.http.AsyncHttpClient;
import com.koushikdutta.async.http.WebSocket;
import com.rubenvandeven.heartbeatstreamer.R;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.EnumSet;
public class HeartRateService extends Service {
private static final String TAG = HeartRateService.class.getSimpleName();
public static final String BROADCAST_MONITOR_CONNECTED = "com.rubenvandeven.heartbeat.broadcast.monitor_started";
public static final String BROADCAST_MONITOR_DISCONNECTED = "com.rubenvandeven.heartbeat.broadcast.monitor_stopped";
public static final String BROADCAST_MONITOR_UNKNOWN = "com.rubenvandeven.heartbeat.broadcast.monitor_unknown";
public static final String BROADCAST_MONITOR_UPDATE = "com.rubenvandeven.heartbeat.broadcast.monitor_update";
public static final String BROADCAST_SOCKET_SENT = "com.rubenvandeven.heartbeat.broadcast.socket_sent";
AsyncScanController<AntPlusHeartRatePcc> hrScanCtrl;
AntPlusHeartRatePcc hrPcc;
PccReleaseHandle<AntPlusHeartRatePcc> releaseHandle;
String lastMsg = "";
public static boolean isRunning = false;
@Override
public void onCreate() {
isRunning = true;
requestAccessToPcc();
}
/**
* Start main thread, request location updates, start synchronization.
*
* @param intent Intent
* @param flags Flags
* @param startId Unique id
* @return Always returns START_STICKY
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Received start id " + startId + ": " + intent);
return START_STICKY;
}
protected AntPluginPcc.IDeviceStateChangeReceiver stateChangeReceiver =
new AntPluginPcc.IDeviceStateChangeReceiver()
{
@Override
public void onDeviceStateChange(final DeviceState state)
{
if(DeviceState.DEAD.equals(state) || DeviceState.CLOSED.equals(state)) {
sendBroadcast(new Intent(BROADCAST_MONITOR_DISCONNECTED));
waitAndReconnect();
} else if (DeviceState.TRACKING.equals(state)) {
sendBroadcast(new Intent(BROADCAST_MONITOR_CONNECTED));
} else {
Intent i = new Intent(BROADCAST_MONITOR_UNKNOWN);
i.putExtra("status", state.getIntValue());
sendBroadcast(i);
}
}
};
protected AntPluginPcc.IPluginAccessResultReceiver<AntPlusHeartRatePcc> resultReceiver =
new AntPluginPcc.IPluginAccessResultReceiver<AntPlusHeartRatePcc>() {
//Handle the result, connecting to events on success or reporting failure to user.
@Override
public void onResultReceived(AntPlusHeartRatePcc result, RequestAccessResult resultCode,
DeviceState initialDeviceState) {
// showDataDisplay("Connecting...");
switch (resultCode) {
case SUCCESS:
connectHr(result);
break;
case CHANNEL_NOT_AVAILABLE:
showNotification("Channel Not Available");
break;
case OTHER_FAILURE:
showNotification("RequestAccess failed. See logcat for details.");
break;
case USER_CANCELLED:
showNotification("Cancelled. Do reset.");
break;
case UNRECOGNIZED:
default:
showNotification("Unknown error. Do reset.");
break;
}
}
};
private void connectHr(AntPlusHeartRatePcc result) {
hrPcc = result; // keep track of current connection
hrPcc.subscribeHeartRateDataEvent(new AntPlusHeartRatePcc.IHeartRateDataReceiver()
{
@Override
public void onNewHeartRateData(final long estTimestamp, EnumSet<EventFlag> eventFlags,
final int computedHeartRate, final long heartBeatCount,
final BigDecimal heartBeatEventTime, final AntPlusHeartRatePcc.DataState dataState)
{
DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols();
otherSymbols.setDecimalSeparator('.');
DecimalFormat df = new DecimalFormat("#.##", otherSymbols);
// Mark heart rate with asterisk if zero detected
final String textHeartRate = String.valueOf(computedHeartRate)
+ ((AntPlusHeartRatePcc.DataState.ZERO_DETECTED.equals(dataState)) ? "*" : "");
// Mark heart beat count and heart beat event time with asterisk if initial value
final String textHeartBeatCount = String.valueOf(heartBeatCount)
+ ((AntPlusHeartRatePcc.DataState.INITIAL_VALUE.equals(dataState)) ? "*" : "");
final String textHeartBeatEventTime = df.format(heartBeatEventTime)
+ ((AntPlusHeartRatePcc.DataState.INITIAL_VALUE.equals(dataState)) ? "*" : "");
final String msg = String.format("{\"rate\":\"%s\", \"count\":\"%s\", \"time\":\"%s\"}", textHeartRate, textHeartBeatCount, textHeartBeatEventTime);
if(msg.contentEquals(lastMsg)) {
Log.i(TAG, "Skip duplicate");
return;
}
lastMsg = msg;
sendResult(msg);
}
});
}
private void sendResult(String msg) {
Log.d(TAG, "Send: " + msg);
final String m = msg;
Future<WebSocket> fws = AsyncHttpClient.getDefaultInstance().websocket("ws://heartbeat.rubenvandeven.com:8888/ws", "my-protocol", new AsyncHttpClient.WebSocketConnectCallback() {
@Override
public void onCompleted(Exception ex, final WebSocket webSocket) {
if (ex != null) {
ex.printStackTrace();
// TODO: retry in 30s - ie. after internet loss
return;
}
webSocket.send(m);
Intent intent = new Intent(BROADCAST_SOCKET_SENT);
intent.putExtra("msg", m);
sendBroadcast(intent);
webSocket.close();
}
});
}
private void showNotification(String msg) {
final String m = msg;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(HeartRateService.this.getApplicationContext(),m,Toast.LENGTH_SHORT).show();
}
});
}
/**
* wait to reconnect to dead HR-monitor
*/
protected void waitAndReconnect() {
//TODO:
Log.d(TAG, "Wait & reconnect");
requestAccessToPcc();
}
/**
* Requests the ANT+ device
*/
protected void requestAccessToPcc()
{
if(hrPcc != null) {
hrPcc.subscribeHeartRateDataEvent(null);
}
releaseHandle = AntPlusHeartRatePcc.requestAccess(this, 4818, 0, resultReceiver, stateChangeReceiver);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onDestroy() {
if(releaseHandle != null)
{
releaseHandle.close();
}
isRunning = false;
super.onDestroy();
}
public static boolean isRunning() {
return isRunning;
}
}