Created foreground service
102
.idea/assetWizardSettings.xml
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="WizardSettings">
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="imageWizard">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="imageAssetPanel">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="launcher">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="foregroundImage">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="scalingPercent" value="146" />
|
||||||
|
<entry key="trimmed" value="true" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="foregroundImage" value="$PROJECT_DIR$/../Heart_Rate_Monitor_Flat_Icon_Vector.svg" />
|
||||||
|
<entry key="showSafeZone" value="false" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="notification">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="clipArt">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="trimmed" value="true" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="image">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="paddingPercent" value="5" />
|
||||||
|
<entry key="trimmed" value="true" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="assetType" value="IMAGE" />
|
||||||
|
<entry key="imageAsset" value="$PROJECT_DIR$/../icon_notif.png" />
|
||||||
|
<entry key="outputName" value="ic_notification" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="outputIconType" value="NOTIFICATION" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
1
README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Icon by [VideoPlasty](https://videoplasty.com/) Creative Commons BY-SA, trough [WikiMedia](https://commons.wikimedia.org/wiki/File:Heart_Rate_Monitor_Flat_Icon_Vector.svg)
|
BIN
app/src/main/ic_launcher-web.png
Normal file
After Width: | Height: | Size: 19 KiB |
|
@ -8,6 +8,7 @@ import android.content.IntentFilter;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
@ -28,6 +29,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private final String TAG = MainActivity.class.getSimpleName();
|
private final String TAG = MainActivity.class.getSimpleName();
|
||||||
|
|
||||||
private TextView statusLabel;
|
private TextView statusLabel;
|
||||||
|
private TextView serviceButton;
|
||||||
|
private TextView beatStatus;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -35,6 +38,21 @@ public class MainActivity extends AppCompatActivity {
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
statusLabel = findViewById(R.id.status_msg);
|
statusLabel = findViewById(R.id.status_msg);
|
||||||
|
serviceButton = findViewById(R.id.service_button);
|
||||||
|
beatStatus = findViewById(R.id.beat_status);
|
||||||
|
|
||||||
|
// View.OnClickListener listener = new On;
|
||||||
|
serviceButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
serviceButton.setEnabled(false);
|
||||||
|
if(HeartRateService.isRunning()) {
|
||||||
|
startHeartRateMonitor();
|
||||||
|
} else {
|
||||||
|
stopHeartRateMonitor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
|
@ -53,8 +71,10 @@ public class MainActivity extends AppCompatActivity {
|
||||||
public void setup() {
|
public void setup() {
|
||||||
if (HeartRateService.isRunning()) {
|
if (HeartRateService.isRunning()) {
|
||||||
statusLabel.setText("Running");
|
statusLabel.setText("Running");
|
||||||
|
updateServiceButton(true);
|
||||||
} else {
|
} else {
|
||||||
statusLabel.setText("Stopped");
|
statusLabel.setText("Stopped");
|
||||||
|
updateServiceButton(false);
|
||||||
}
|
}
|
||||||
registerBroadcastReceiver();
|
registerBroadcastReceiver();
|
||||||
}
|
}
|
||||||
|
@ -64,14 +84,25 @@ public class MainActivity extends AppCompatActivity {
|
||||||
Toast.makeText(this, "Already running", Toast.LENGTH_LONG);
|
Toast.makeText(this, "Already running", Toast.LENGTH_LONG);
|
||||||
} else {
|
} else {
|
||||||
Intent intent = new Intent(MainActivity.this, HeartRateService.class);
|
Intent intent = new Intent(MainActivity.this, HeartRateService.class);
|
||||||
|
intent.setAction(HeartRateService.ACTION_START);
|
||||||
startService(intent);
|
startService(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateServiceButton(boolean running) {
|
||||||
|
if(running) {
|
||||||
|
serviceButton.setText(R.string.service_stop);
|
||||||
|
} else {
|
||||||
|
serviceButton.setText(R.string.service_start);
|
||||||
|
}
|
||||||
|
serviceButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
public void stopHeartRateMonitor() {
|
public void stopHeartRateMonitor() {
|
||||||
// stop tracking
|
// stop tracking
|
||||||
Intent intent = new Intent(MainActivity.this, HeartRateService.class);
|
Intent intent = new Intent(MainActivity.this, HeartRateService.class);
|
||||||
stopService(intent);
|
intent.setAction(HeartRateService.ACTION_STOP);
|
||||||
|
startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,6 +117,11 @@ public class MainActivity extends AppCompatActivity {
|
||||||
filter.addAction(HeartRateService.BROADCAST_MONITOR_UPDATE);
|
filter.addAction(HeartRateService.BROADCAST_MONITOR_UPDATE);
|
||||||
filter.addAction(HeartRateService.BROADCAST_MONITOR_UNKNOWN);
|
filter.addAction(HeartRateService.BROADCAST_MONITOR_UNKNOWN);
|
||||||
filter.addAction(HeartRateService.BROADCAST_SOCKET_SENT);
|
filter.addAction(HeartRateService.BROADCAST_SOCKET_SENT);
|
||||||
|
filter.addAction(HeartRateService.BROADCAST_SERVICE_START);
|
||||||
|
filter.addAction(HeartRateService.BROADCAST_SERVICE_STOPPED);
|
||||||
|
filter.addAction(HeartRateService.BROADCAST_BEAT);
|
||||||
|
filter.addAction(HeartRateService.BROADCAST_MONITOR_CONNECT_ATTEMPT);
|
||||||
|
filter.addAction(HeartRateService.BROADCAST_MONITOR_CONNECT_RESULT);
|
||||||
registerReceiver(mBroadcastReceiver, filter);
|
registerReceiver(mBroadcastReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,53 +148,50 @@ public class MainActivity extends AppCompatActivity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
|
case HeartRateService.BROADCAST_SERVICE_START:
|
||||||
|
statusLabel.setText("Started");
|
||||||
|
updateServiceButton(true);
|
||||||
|
break;
|
||||||
|
case HeartRateService.BROADCAST_SERVICE_STOPPED:
|
||||||
|
statusLabel.setText("Service Stopped");
|
||||||
|
updateServiceButton(false);
|
||||||
|
break;
|
||||||
case HeartRateService.BROADCAST_MONITOR_CONNECTED:
|
case HeartRateService.BROADCAST_MONITOR_CONNECTED:
|
||||||
statusLabel.setText("Running");
|
statusLabel.setText("Connected");
|
||||||
break;
|
break;
|
||||||
case HeartRateService.BROADCAST_MONITOR_DISCONNECTED:
|
case HeartRateService.BROADCAST_MONITOR_DISCONNECTED:
|
||||||
statusLabel.setText("Stopped");
|
statusLabel.setText("Disconnected");
|
||||||
break;
|
break;
|
||||||
case HeartRateService.BROADCAST_MONITOR_UPDATE:
|
case HeartRateService.BROADCAST_MONITOR_UPDATE:
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
||||||
String currentDateandTime = sdf.format(new Date());
|
String currentDateandTime = sdf.format(new Date());
|
||||||
statusLabel.setText("Last update: %s".format(currentDateandTime));
|
statusLabel.setText("Last update: %s".format(currentDateandTime));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HeartRateService.BROADCAST_SOCKET_SENT:
|
case HeartRateService.BROADCAST_SOCKET_SENT:
|
||||||
String msg = intent.getStringExtra("msg");
|
String msg = intent.getStringExtra("msg");
|
||||||
statusLabel.setText("Last msg: %s".format(msg));
|
statusLabel.setText("Last msg: %s".format(msg));
|
||||||
break;
|
break;
|
||||||
|
case HeartRateService.BROADCAST_MONITOR_UNKNOWN:
|
||||||
|
int status = intent.getIntExtra("status", -1);
|
||||||
|
statusLabel.setText("Unknown state: %s".format(String.valueOf(status)));
|
||||||
|
break;
|
||||||
|
case HeartRateService.BROADCAST_BEAT:
|
||||||
|
String rate = intent.getStringExtra("rate");
|
||||||
|
String count = intent.getStringExtra("count");
|
||||||
|
String time = intent.getStringExtra("time");
|
||||||
|
Log.d(TAG, "Beat: " + rate);
|
||||||
|
beatStatus.setText(rate + " bpm / " + count + " beats / "+time + " s");
|
||||||
|
break;
|
||||||
|
case HeartRateService.BROADCAST_MONITOR_CONNECT_ATTEMPT:
|
||||||
|
beatStatus.setText("Attempting connection to monitor");
|
||||||
|
break;
|
||||||
|
case HeartRateService.BROADCAST_MONITOR_CONNECT_RESULT:
|
||||||
|
String connectMsg = intent.getStringExtra("msg");
|
||||||
|
beatStatus.setText("Connection: %s".format(connectMsg ));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the token for use with the syncing.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getToken() {
|
|
||||||
String json = null;
|
|
||||||
String token = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
InputStream is = getAssets().open("token.json");
|
|
||||||
int size = is.available();
|
|
||||||
byte[] buffer = new byte[size];
|
|
||||||
is.read(buffer);
|
|
||||||
is.close();
|
|
||||||
json = new String(buffer, "UTF-8");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
JSONObject jsonObject = new JSONObject(json);
|
|
||||||
token = jsonObject.getString("token'");
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.rubenvandeven.heartbeatstreamer.heartrate;
|
package com.rubenvandeven.heartbeatstreamer.heartrate;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
@ -9,6 +11,7 @@ import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -23,11 +26,19 @@ import com.dsi.ant.plugins.antplus.pccbase.PccReleaseHandle;
|
||||||
import com.koushikdutta.async.future.Future;
|
import com.koushikdutta.async.future.Future;
|
||||||
import com.koushikdutta.async.http.AsyncHttpClient;
|
import com.koushikdutta.async.http.AsyncHttpClient;
|
||||||
import com.koushikdutta.async.http.WebSocket;
|
import com.koushikdutta.async.http.WebSocket;
|
||||||
|
import com.rubenvandeven.heartbeatstreamer.MainActivity;
|
||||||
import com.rubenvandeven.heartbeatstreamer.R;
|
import com.rubenvandeven.heartbeatstreamer.R;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.text.DateFormat;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.DecimalFormatSymbols;
|
import java.text.DecimalFormatSymbols;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
public class HeartRateService extends Service {
|
public class HeartRateService extends Service {
|
||||||
|
@ -37,22 +48,51 @@ public class HeartRateService extends Service {
|
||||||
public static final String BROADCAST_MONITOR_DISCONNECTED = "com.rubenvandeven.heartbeat.broadcast.monitor_stopped";
|
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_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_MONITOR_UPDATE = "com.rubenvandeven.heartbeat.broadcast.monitor_update";
|
||||||
|
public static final String BROADCAST_MONITOR_CONNECT_ATTEMPT = "com.rubenvandeven.heartbeat.broadcast.connect_attempt";
|
||||||
|
public static final String BROADCAST_MONITOR_CONNECT_RESULT = "com.rubenvandeven.heartbeat.broadcast.connect_reslt";
|
||||||
public static final String BROADCAST_SOCKET_SENT = "com.rubenvandeven.heartbeat.broadcast.socket_sent";
|
public static final String BROADCAST_SOCKET_SENT = "com.rubenvandeven.heartbeat.broadcast.socket_sent";
|
||||||
|
public static final String BROADCAST_SERVICE_START = "com.rubenvandeven.heartbeat.broadcast.service_start";
|
||||||
|
public static final String BROADCAST_SERVICE_STOPPED = "com.rubenvandeven.heartbeat.broadcast.service_stopped";
|
||||||
|
public static final String BROADCAST_BEAT = "com.rubenvandeven.heartbeat.broadcast.beat";
|
||||||
|
public static final String ACTION_START = "com.rubenvandeven.heartbeat.service_start";
|
||||||
|
public static final String ACTION_STOP = "com.rubenvandeven.heartbeat.service_stop";
|
||||||
|
public static final int NOTIFICATION_ID = 101;
|
||||||
|
|
||||||
AsyncScanController<AntPlusHeartRatePcc> hrScanCtrl;
|
AsyncScanController<AntPlusHeartRatePcc> hrScanCtrl;
|
||||||
|
|
||||||
AntPlusHeartRatePcc hrPcc;
|
AntPlusHeartRatePcc hrPcc;
|
||||||
PccReleaseHandle<AntPlusHeartRatePcc> releaseHandle;
|
PccReleaseHandle<AntPlusHeartRatePcc> releaseHandle;
|
||||||
String lastMsg = "";
|
JSONObject lastMsg = null;
|
||||||
|
|
||||||
public static boolean isRunning = false;
|
public static boolean isRunning = false;
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder;
|
||||||
|
NotificationManager notificationManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
isRunning = true;
|
notificationManager =
|
||||||
|
(NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
|
setRunning(true);
|
||||||
|
|
||||||
requestAccessToPcc();
|
requestAccessToPcc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRunning(boolean running) {
|
||||||
|
if(running == isRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isRunning = running;
|
||||||
|
Intent i;
|
||||||
|
if(running) {
|
||||||
|
i = new Intent(BROADCAST_SERVICE_START);
|
||||||
|
} else {
|
||||||
|
i = new Intent(BROADCAST_SERVICE_STOPPED);
|
||||||
|
}
|
||||||
|
sendBroadcast(i);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start main thread, request location updates, start synchronization.
|
* Start main thread, request location updates, start synchronization.
|
||||||
*
|
*
|
||||||
|
@ -63,7 +103,32 @@ public class HeartRateService extends Service {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
if (intent.getAction().equals(ACTION_START)) {
|
||||||
Log.i(TAG, "Received start id " + startId + ": " + intent);
|
Log.i(TAG, "Received start id " + startId + ": " + intent);
|
||||||
|
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||||
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
|
||||||
|
notificationIntent, 0);
|
||||||
|
|
||||||
|
builder = new NotificationCompat.Builder(this)
|
||||||
|
.setContentText("Starting")
|
||||||
|
.setContentTitle("Heartbeat Streamer")
|
||||||
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
|
.setAutoCancel(false)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setOnlyAlertOnce(false)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
;
|
||||||
|
|
||||||
|
startForeground(NOTIFICATION_ID,
|
||||||
|
builder.build());
|
||||||
|
|
||||||
|
} else if (intent.getAction().equals(ACTION_STOP)) {
|
||||||
|
Log.i(TAG, "Received stop id " + startId + ": " + intent);
|
||||||
|
stopForeground(true);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
return START_STICKY;
|
return START_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,24 +158,31 @@ public class HeartRateService extends Service {
|
||||||
public void onResultReceived(AntPlusHeartRatePcc result, RequestAccessResult resultCode,
|
public void onResultReceived(AntPlusHeartRatePcc result, RequestAccessResult resultCode,
|
||||||
DeviceState initialDeviceState) {
|
DeviceState initialDeviceState) {
|
||||||
// showDataDisplay("Connecting...");
|
// showDataDisplay("Connecting...");
|
||||||
|
Intent i = new Intent(BROADCAST_MONITOR_CONNECT_RESULT);
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case SUCCESS:
|
case SUCCESS:
|
||||||
|
i.putExtra("msg", "Success");
|
||||||
connectHr(result);
|
connectHr(result);
|
||||||
break;
|
break;
|
||||||
case CHANNEL_NOT_AVAILABLE:
|
case CHANNEL_NOT_AVAILABLE:
|
||||||
showNotification("Channel Not Available");
|
showNotification("Channel Not Available");
|
||||||
|
i.putExtra("msg", "Channel Not Available");
|
||||||
break;
|
break;
|
||||||
case OTHER_FAILURE:
|
case OTHER_FAILURE:
|
||||||
showNotification("RequestAccess failed. See logcat for details.");
|
showNotification("RequestAccess failed. See logcat for details.");
|
||||||
|
i.putExtra("msg", "RequestAccess failed. See logcat for details.");
|
||||||
break;
|
break;
|
||||||
case USER_CANCELLED:
|
case USER_CANCELLED:
|
||||||
showNotification("Cancelled. Do reset.");
|
showNotification("Cancelled. Do reset.");
|
||||||
|
i.putExtra("msg", "Cancelled. Do reset.");
|
||||||
break;
|
break;
|
||||||
case UNRECOGNIZED:
|
case UNRECOGNIZED:
|
||||||
default:
|
default:
|
||||||
showNotification("Unknown error. Do reset.");
|
showNotification("Unknown error. Do reset.");
|
||||||
|
i.putExtra("msg", "unknown error. Do reset.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
sendBroadcast(i);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,30 +210,86 @@ public class HeartRateService extends Service {
|
||||||
final String textHeartBeatEventTime = df.format(heartBeatEventTime)
|
final String textHeartBeatEventTime = df.format(heartBeatEventTime)
|
||||||
+ ((AntPlusHeartRatePcc.DataState.INITIAL_VALUE.equals(dataState)) ? "*" : "");
|
+ ((AntPlusHeartRatePcc.DataState.INITIAL_VALUE.equals(dataState)) ? "*" : "");
|
||||||
|
|
||||||
final String msg = String.format("{\"rate\":\"%s\", \"count\":\"%s\", \"time\":\"%s\"}", textHeartRate, textHeartBeatCount, textHeartBeatEventTime);
|
Intent intent = new Intent(BROADCAST_BEAT);
|
||||||
|
intent.putExtra("rate", textHeartRate);
|
||||||
|
intent.putExtra("count", textHeartBeatCount);
|
||||||
|
intent.putExtra("time", textHeartBeatEventTime);
|
||||||
|
sendBroadcast(intent);
|
||||||
|
|
||||||
if(msg.contentEquals(lastMsg)) {
|
builder.setContentText(textHeartRate + " bpm");
|
||||||
|
notificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||||
|
|
||||||
|
JSONObject jObjectData = new JSONObject();
|
||||||
|
// final String msg = String.format("{\"rate\":\"%s\", \"count\":\"%s\", \"time\":\"%s\"}", textHeartRate, textHeartBeatCount, textHeartBeatEventTime);
|
||||||
|
final String msg;
|
||||||
|
try {
|
||||||
|
jObjectData.put("rate", textHeartRate);
|
||||||
|
jObjectData.put("count", textHeartBeatCount);
|
||||||
|
jObjectData.put("time", textHeartBeatEventTime);
|
||||||
|
jObjectData.put("timestamp", DateFormat.getDateTimeInstance().format(new Date()));
|
||||||
|
jObjectData.put("token", getToken());
|
||||||
|
|
||||||
|
msg = jObjectData.toString();
|
||||||
|
|
||||||
|
|
||||||
|
if(lastMsg != null && lastMsg.getString("time").equals(jObjectData.getString("time"))) {
|
||||||
Log.i(TAG, "Skip duplicate");
|
Log.i(TAG, "Skip duplicate");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastMsg = msg;
|
lastMsg = jObjectData;
|
||||||
|
|
||||||
sendResult(msg);
|
sendResult(jObjectData.toString());
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void sendResult(String msg) {
|
String token = null;
|
||||||
Log.d(TAG, "Send: " + msg);
|
/**
|
||||||
final String m = msg;
|
* Get the token for use with the syncing.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getToken() {
|
||||||
|
if(token != null) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
String json = null;
|
||||||
|
try {
|
||||||
|
InputStream is = getAssets().open("token.json");
|
||||||
|
int size = is.available();
|
||||||
|
byte[] buffer = new byte[size];
|
||||||
|
is.read(buffer);
|
||||||
|
is.close();
|
||||||
|
json = new String(buffer, "UTF-8");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JSONObject jsonObject = new JSONObject(json);
|
||||||
|
token = jsonObject.getString("token'");
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void sendResult(final String m) {
|
||||||
|
Log.d(TAG, "Send: " + m);
|
||||||
Future<WebSocket> fws = AsyncHttpClient.getDefaultInstance().websocket("ws://heartbeat.rubenvandeven.com:8888/ws", "my-protocol", new AsyncHttpClient.WebSocketConnectCallback() {
|
Future<WebSocket> fws = AsyncHttpClient.getDefaultInstance().websocket("ws://heartbeat.rubenvandeven.com:8888/ws", "my-protocol", new AsyncHttpClient.WebSocketConnectCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onCompleted(Exception ex, final WebSocket webSocket) {
|
public void onCompleted(Exception ex, final WebSocket webSocket) {
|
||||||
if (ex != null) {
|
if (ex != null) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
// TODO: retry in 30s - ie. after internet loss
|
// TODO: store for later sync
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
webSocket.send(m);
|
webSocket.send(m);
|
||||||
|
@ -188,10 +316,21 @@ public class HeartRateService extends Service {
|
||||||
* wait to reconnect to dead HR-monitor
|
* wait to reconnect to dead HR-monitor
|
||||||
*/
|
*/
|
||||||
protected void waitAndReconnect() {
|
protected void waitAndReconnect() {
|
||||||
//TODO:
|
|
||||||
Log.d(TAG, "Wait & reconnect");
|
Log.d(TAG, "Wait & reconnect");
|
||||||
|
|
||||||
|
if(hrPcc != null) {
|
||||||
|
hrPcc.subscribeHeartRateDataEvent(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
new android.os.Handler().postDelayed(
|
||||||
|
new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
Log.i(TAG, "Attempt reconnect");
|
||||||
requestAccessToPcc();
|
requestAccessToPcc();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
20000); // 20 sec delay for connection
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests the ANT+ device
|
* Requests the ANT+ device
|
||||||
|
@ -201,6 +340,7 @@ public class HeartRateService extends Service {
|
||||||
if(hrPcc != null) {
|
if(hrPcc != null) {
|
||||||
hrPcc.subscribeHeartRateDataEvent(null);
|
hrPcc.subscribeHeartRateDataEvent(null);
|
||||||
}
|
}
|
||||||
|
sendBroadcast( new Intent(BROADCAST_MONITOR_CONNECT_ATTEMPT) );
|
||||||
releaseHandle = AntPlusHeartRatePcc.requestAccess(this, 4818, 0, resultReceiver, stateChangeReceiver);
|
releaseHandle = AntPlusHeartRatePcc.requestAccess(this, 4818, 0, resultReceiver, stateChangeReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +356,7 @@ public class HeartRateService extends Service {
|
||||||
{
|
{
|
||||||
releaseHandle.close();
|
releaseHandle.close();
|
||||||
}
|
}
|
||||||
isRunning = false;
|
setRunning(false);
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
After Width: | Height: | Size: 322 B |
BIN
app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
After Width: | Height: | Size: 211 B |
BIN
app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
After Width: | Height: | Size: 444 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
After Width: | Height: | Size: 721 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
After Width: | Height: | Size: 1 KiB |
83
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="757.16064"
|
||||||
|
android:viewportHeight="757.16064">
|
||||||
|
<group android:translateX="-101.41968"
|
||||||
|
android:translateY="-101.41968">
|
||||||
|
<path
|
||||||
|
android:pathData="M733.8,717.5h-507c-5.7,0 -10.4,-4.7 -10.4,-10.4v-31.3c0,-5.7 4.7,-10.4 10.4,-10.4h507c5.7,0 10.4,4.7 10.4,10.4v31.3C744.2,712.9 739.6,717.5 733.8,717.5z"
|
||||||
|
android:fillColor="#E6E6E6"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M786.3,681H173.7c-13.5,0 -24.5,-10.9 -24.5,-24.5V266.9c0,-13.5 10.9,-24.5 24.5,-24.5h612.6c13.5,0 24.5,10.9 24.5,24.5v389.6C810.7,670 799.8,681 786.3,681z"
|
||||||
|
android:fillColor="#FFFFFF"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M203,289.3h554v304h-554z"
|
||||||
|
android:fillColor="#4B535C"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M223.4,309.7l533.6,0l0,-20.4l-554,0l0,304l20.4,0z"
|
||||||
|
android:fillColor="#333333"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M206.4,620.7h83.9v23.3h-83.9z"
|
||||||
|
android:fillColor="#333333"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M313.3,620.7h83.9v23.3h-83.9z"
|
||||||
|
android:fillColor="#333333"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M305,309.7L305,593.3"
|
||||||
|
android:strokeAlpha="0.12"
|
||||||
|
android:strokeWidth="4.8683"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:fillAlpha="0.12"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M417.7,309.7L417.7,593.3"
|
||||||
|
android:strokeAlpha="0.12"
|
||||||
|
android:strokeWidth="4.8683"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:fillAlpha="0.12"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M530.5,309.7L530.5,593.3"
|
||||||
|
android:strokeAlpha="0.12"
|
||||||
|
android:strokeWidth="4.8683"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:fillAlpha="0.12"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M643.2,309.7L643.2,593.3"
|
||||||
|
android:strokeAlpha="0.12"
|
||||||
|
android:strokeWidth="4.8683"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:fillAlpha="0.12"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M223.4,392.4L757,392.4"
|
||||||
|
android:strokeAlpha="0.12"
|
||||||
|
android:strokeWidth="4.8683"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:fillAlpha="0.12"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M223.4,485.1L757,485.1"
|
||||||
|
android:strokeAlpha="0.12"
|
||||||
|
android:strokeWidth="4.8683"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:fillAlpha="0.12"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M744.2,632.3m-11.7,0a11.7,11.7 0,1 1,23.4 0a11.7,11.7 0,1 1,-23.4 0"
|
||||||
|
android:fillColor="#C1272D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M704.7,632.3m-11.7,0a11.7,11.7 0,1 1,23.4 0a11.7,11.7 0,1 1,-23.4 0"
|
||||||
|
android:fillColor="#8CC63F"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M665.1,632.3m-11.7,0a11.7,11.7 0,1 1,23.4 0a11.7,11.7 0,1 1,-23.4 0"
|
||||||
|
android:fillColor="#C1272D"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M201.8,465.9l106.5,0l51.6,-85.3l37.8,139.6l52.2,-110.6l33.8,68.5l46.8,-126.5l46.9,142.5l45.4,-88.7l28.2,62.2l104.9,0"
|
||||||
|
android:strokeWidth="9.7366"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#12A88B"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
|
@ -1,19 +1,46 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/status_msg"
|
android:id="@+id/status_msg"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
android:text="..."
|
android:text="..."
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
<TextView
|
||||||
|
android:id="@+id/beat_status"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="..."
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/service_button"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/service_start" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -1,5 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">`
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
|
@ -1,5 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 6.4 KiB |
|
@ -22,4 +22,7 @@
|
||||||
<item>Bike Speed</item>
|
<item>Bike Speed</item>
|
||||||
<item>Stride-based Speed and Distance</item>
|
<item>Stride-based Speed and Distance</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string name="service_start">Start service</string>
|
||||||
|
<string name="service_stop">Stop service</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|