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.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -28,6 +29,8 @@ public class MainActivity extends AppCompatActivity {
|
|||
private final String TAG = MainActivity.class.getSimpleName();
|
||||
|
||||
private TextView statusLabel;
|
||||
private TextView serviceButton;
|
||||
private TextView beatStatus;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -35,6 +38,21 @@ public class MainActivity extends AppCompatActivity {
|
|||
setContentView(R.layout.activity_main);
|
||||
|
||||
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();
|
||||
|
||||
|
@ -53,8 +71,10 @@ public class MainActivity extends AppCompatActivity {
|
|||
public void setup() {
|
||||
if (HeartRateService.isRunning()) {
|
||||
statusLabel.setText("Running");
|
||||
updateServiceButton(true);
|
||||
} else {
|
||||
statusLabel.setText("Stopped");
|
||||
updateServiceButton(false);
|
||||
}
|
||||
registerBroadcastReceiver();
|
||||
}
|
||||
|
@ -64,14 +84,25 @@ public class MainActivity extends AppCompatActivity {
|
|||
Toast.makeText(this, "Already running", Toast.LENGTH_LONG);
|
||||
} else {
|
||||
Intent intent = new Intent(MainActivity.this, HeartRateService.class);
|
||||
intent.setAction(HeartRateService.ACTION_START);
|
||||
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() {
|
||||
// stop tracking
|
||||
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_UNKNOWN);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -112,53 +148,50 @@ public class MainActivity extends AppCompatActivity {
|
|||
return;
|
||||
}
|
||||
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:
|
||||
statusLabel.setText("Running");
|
||||
statusLabel.setText("Connected");
|
||||
break;
|
||||
case HeartRateService.BROADCAST_MONITOR_DISCONNECTED:
|
||||
statusLabel.setText("Stopped");
|
||||
statusLabel.setText("Disconnected");
|
||||
break;
|
||||
case HeartRateService.BROADCAST_MONITOR_UPDATE:
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
||||
String currentDateandTime = sdf.format(new Date());
|
||||
statusLabel.setText("Last update: %s".format(currentDateandTime));
|
||||
break;
|
||||
|
||||
case HeartRateService.BROADCAST_SOCKET_SENT:
|
||||
String msg = intent.getStringExtra("msg");
|
||||
statusLabel.setText("Last msg: %s".format(msg));
|
||||
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;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
|
@ -9,6 +11,7 @@ import android.os.Handler;
|
|||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
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.http.AsyncHttpClient;
|
||||
import com.koushikdutta.async.http.WebSocket;
|
||||
import com.rubenvandeven.heartbeatstreamer.MainActivity;
|
||||
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.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
|
||||
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_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_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_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;
|
||||
|
||||
AntPlusHeartRatePcc hrPcc;
|
||||
PccReleaseHandle<AntPlusHeartRatePcc> releaseHandle;
|
||||
String lastMsg = "";
|
||||
JSONObject lastMsg = null;
|
||||
|
||||
public static boolean isRunning = false;
|
||||
|
||||
NotificationCompat.Builder builder;
|
||||
NotificationManager notificationManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
isRunning = true;
|
||||
notificationManager =
|
||||
(NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
|
||||
|
||||
setRunning(true);
|
||||
|
||||
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.
|
||||
*
|
||||
|
@ -63,7 +103,32 @@ public class HeartRateService extends Service {
|
|||
*/
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.i(TAG, "Received start id " + startId + ": " + intent);
|
||||
if (intent.getAction().equals(ACTION_START)) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -93,24 +158,31 @@ public class HeartRateService extends Service {
|
|||
public void onResultReceived(AntPlusHeartRatePcc result, RequestAccessResult resultCode,
|
||||
DeviceState initialDeviceState) {
|
||||
// showDataDisplay("Connecting...");
|
||||
Intent i = new Intent(BROADCAST_MONITOR_CONNECT_RESULT);
|
||||
switch (resultCode) {
|
||||
case SUCCESS:
|
||||
i.putExtra("msg", "Success");
|
||||
connectHr(result);
|
||||
break;
|
||||
case CHANNEL_NOT_AVAILABLE:
|
||||
showNotification("Channel Not Available");
|
||||
i.putExtra("msg", "Channel Not Available");
|
||||
break;
|
||||
case OTHER_FAILURE:
|
||||
showNotification("RequestAccess failed. See logcat for details.");
|
||||
i.putExtra("msg", "RequestAccess failed. See logcat for details.");
|
||||
break;
|
||||
case USER_CANCELLED:
|
||||
showNotification("Cancelled. Do reset.");
|
||||
i.putExtra("msg", "Cancelled. Do reset.");
|
||||
break;
|
||||
case UNRECOGNIZED:
|
||||
default:
|
||||
showNotification("Unknown error. Do reset.");
|
||||
i.putExtra("msg", "unknown error. Do reset.");
|
||||
break;
|
||||
}
|
||||
sendBroadcast(i);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -138,30 +210,86 @@ public class HeartRateService extends Service {
|
|||
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);
|
||||
Intent intent = new Intent(BROADCAST_BEAT);
|
||||
intent.putExtra("rate", textHeartRate);
|
||||
intent.putExtra("count", textHeartBeatCount);
|
||||
intent.putExtra("time", textHeartBeatEventTime);
|
||||
sendBroadcast(intent);
|
||||
|
||||
if(msg.contentEquals(lastMsg)) {
|
||||
Log.i(TAG, "Skip duplicate");
|
||||
return;
|
||||
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");
|
||||
return;
|
||||
}
|
||||
lastMsg = jObjectData;
|
||||
|
||||
sendResult(jObjectData.toString());
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
lastMsg = msg;
|
||||
|
||||
sendResult(msg);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void sendResult(String msg) {
|
||||
Log.d(TAG, "Send: " + msg);
|
||||
final String m = msg;
|
||||
String token = null;
|
||||
/**
|
||||
* 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() {
|
||||
@Override
|
||||
public void onCompleted(Exception ex, final WebSocket webSocket) {
|
||||
if (ex != null) {
|
||||
ex.printStackTrace();
|
||||
// TODO: retry in 30s - ie. after internet loss
|
||||
// TODO: store for later sync
|
||||
return;
|
||||
}
|
||||
webSocket.send(m);
|
||||
|
@ -188,9 +316,20 @@ public class HeartRateService extends Service {
|
|||
* wait to reconnect to dead HR-monitor
|
||||
*/
|
||||
protected void waitAndReconnect() {
|
||||
//TODO:
|
||||
Log.d(TAG, "Wait & reconnect");
|
||||
requestAccessToPcc();
|
||||
|
||||
if(hrPcc != null) {
|
||||
hrPcc.subscribeHeartRateDataEvent(null);
|
||||
}
|
||||
|
||||
new android.os.Handler().postDelayed(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
Log.i(TAG, "Attempt reconnect");
|
||||
requestAccessToPcc();
|
||||
}
|
||||
},
|
||||
20000); // 20 sec delay for connection
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -201,6 +340,7 @@ public class HeartRateService extends Service {
|
|||
if(hrPcc != null) {
|
||||
hrPcc.subscribeHeartRateDataEvent(null);
|
||||
}
|
||||
sendBroadcast( new Intent(BROADCAST_MONITOR_CONNECT_ATTEMPT) );
|
||||
releaseHandle = AntPlusHeartRatePcc.requestAccess(this, 4818, 0, resultReceiver, stateChangeReceiver);
|
||||
}
|
||||
|
||||
|
@ -216,7 +356,7 @@ public class HeartRateService extends Service {
|
|||
{
|
||||
releaseHandle.close();
|
||||
}
|
||||
isRunning = false;
|
||||
setRunning(false);
|
||||
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"?>
|
||||
<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:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/status_msg"
|
||||
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" />
|
||||
|
||||
</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"?>
|
||||
<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" />
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">`
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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>
|
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>Stride-based Speed and Distance</item>
|
||||
</string-array>
|
||||
<string name="service_start">Start service</string>
|
||||
<string name="service_stop">Stop service</string>
|
||||
|
||||
</resources>
|
||||
|
|