diff --git a/contrib/RideLogger/.classpath b/contrib/RideLogger/.classpath
new file mode 100644
index 000000000..51769745b
--- /dev/null
+++ b/contrib/RideLogger/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/contrib/RideLogger/.gitignore b/contrib/RideLogger/.gitignore
new file mode 100644
index 000000000..f6da3b79e
--- /dev/null
+++ b/contrib/RideLogger/.gitignore
@@ -0,0 +1,12 @@
+# Ignores for Eclipse
+/.settings/
+
+# Ignores for Android Projects
+/bin/
+/gen/
+/publish/
+/local.properties
+/secure.properties
+
+# Ignores for Mac machines
+.DS_Store
diff --git a/contrib/RideLogger/AndroidManifest.xml b/contrib/RideLogger/AndroidManifest.xml
new file mode 100644
index 000000000..0ee61ea2d
--- /dev/null
+++ b/contrib/RideLogger/AndroidManifest.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/contrib/RideLogger/assets/WorkoutRepeatSteps.fit b/contrib/RideLogger/assets/WorkoutRepeatSteps.fit
new file mode 100644
index 000000000..0dbe51a0e
Binary files /dev/null and b/contrib/RideLogger/assets/WorkoutRepeatSteps.fit differ
diff --git a/contrib/RideLogger/ic_launcher-web.png b/contrib/RideLogger/ic_launcher-web.png
new file mode 100644
index 000000000..2988fc3a5
Binary files /dev/null and b/contrib/RideLogger/ic_launcher-web.png differ
diff --git a/contrib/RideLogger/libs/android-support-v4.jar b/contrib/RideLogger/libs/android-support-v4.jar
new file mode 100644
index 000000000..6080877d4
Binary files /dev/null and b/contrib/RideLogger/libs/android-support-v4.jar differ
diff --git a/contrib/RideLogger/libs/antpluginlib_3-1-0.jar b/contrib/RideLogger/libs/antpluginlib_3-1-0.jar
new file mode 100644
index 000000000..ce1d421f0
Binary files /dev/null and b/contrib/RideLogger/libs/antpluginlib_3-1-0.jar differ
diff --git a/contrib/RideLogger/libs/fit_12.00.jar b/contrib/RideLogger/libs/fit_12.00.jar
new file mode 100644
index 000000000..7aa581756
Binary files /dev/null and b/contrib/RideLogger/libs/fit_12.00.jar differ
diff --git a/contrib/RideLogger/lint.xml b/contrib/RideLogger/lint.xml
new file mode 100644
index 000000000..ee0eead5b
--- /dev/null
+++ b/contrib/RideLogger/lint.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/contrib/RideLogger/project.properties b/contrib/RideLogger/project.properties
new file mode 100644
index 000000000..2e18f6073
--- /dev/null
+++ b/contrib/RideLogger/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library.reference.1=../AntPluginLib
diff --git a/contrib/RideLogger/res/drawable-hdpi/ic_launcher.png b/contrib/RideLogger/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..feb241fa8
Binary files /dev/null and b/contrib/RideLogger/res/drawable-hdpi/ic_launcher.png differ
diff --git a/contrib/RideLogger/res/drawable-ldpi/ic_launcher.png b/contrib/RideLogger/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..1857ef3b2
Binary files /dev/null and b/contrib/RideLogger/res/drawable-ldpi/ic_launcher.png differ
diff --git a/contrib/RideLogger/res/drawable-mdpi/ic_launcher.png b/contrib/RideLogger/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..747f16cb4
Binary files /dev/null and b/contrib/RideLogger/res/drawable-mdpi/ic_launcher.png differ
diff --git a/contrib/RideLogger/res/drawable-xhdpi/ic_launcher.png b/contrib/RideLogger/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..f21ee6fa8
Binary files /dev/null and b/contrib/RideLogger/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/contrib/RideLogger/res/layout/activity_dashboard.xml b/contrib/RideLogger/res/layout/activity_dashboard.xml
new file mode 100644
index 000000000..ddc243ebd
--- /dev/null
+++ b/contrib/RideLogger/res/layout/activity_dashboard.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/contrib/RideLogger/res/values/strings.xml b/contrib/RideLogger/res/values/strings.xml
new file mode 100644
index 000000000..c56f9d469
--- /dev/null
+++ b/contrib/RideLogger/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ Ride Logger
+ Logging Ride
+
\ No newline at end of file
diff --git a/contrib/RideLogger/src/com/ridelogger/RideService.java b/contrib/RideLogger/src/com/ridelogger/RideService.java
new file mode 100644
index 000000000..9aa5b2a30
--- /dev/null
+++ b/contrib/RideLogger/src/com/ridelogger/RideService.java
@@ -0,0 +1,366 @@
+package com.ridelogger;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+
+import com.dsi.ant.plugins.antplus.pcc.defines.DeviceType;
+import com.dsi.ant.plugins.antplus.pcc.defines.RequestAccessResult;
+import com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch;
+import com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch.MultiDeviceSearchResult;
+import com.ridelogger.R;
+import com.ridelogger.listners.Gps;
+import com.ridelogger.listners.HeartRate;
+import com.ridelogger.listners.Power;
+import com.ridelogger.listners.Sensors;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.TaskStackBuilder;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+
+public class RideService extends Service
+{
+ public static BufferedWriter buf;
+ public static long start_time;
+ public static Map current_values;
+ public boolean rideStarted = false;
+
+ public static HeartRate hr;
+ public static Power w;
+ public static Gps gps;
+ public static Sensors sensors;
+
+ MultiDeviceSearch mSearch;
+ MultiDeviceSearch.SearchCallbacks mCallback;
+ MultiDeviceSearch.RssiCallback mRssiCallback;
+
+ public int notifyID = 1;
+ NotificationManager mNotificationManager;
+
+ public String file_name = "";
+ SharedPreferences settings;
+
+ /**
+ *
+ * @return BufferedWriter
+ */
+ public static BufferedWriter getBuf() {
+ return buf;
+ }
+
+ /**
+ *
+ * @return start_time
+ */
+ public static long getStartTime() {
+ return start_time;
+ }
+
+
+ /**
+ *
+ * @return start_time
+ */
+ public static Map getCurrentValues() {
+ return current_values;
+ }
+
+
+ /**
+ *
+ * @return w
+ */
+ public static Power getPower() {
+ return w;
+ }
+
+ /**
+ *
+ * @return w
+ */
+ public static void setPower(Power pw) {
+ w = pw;
+ if(w == null) {
+ w = pw;
+ }
+ }
+
+ /**
+ *
+ * @return hr
+ */
+ public static HeartRate getHeartRate() {
+ return hr;
+ }
+
+
+ public static void setHeartRate(HeartRate phr) {
+ if(hr == null) {
+ hr = phr;
+ }
+ }
+
+
+ public static void setGps(Gps pgps) {
+ if(gps == null) {
+ gps = pgps;
+ }
+ }
+
+ public static Gps getGps() {
+ return gps;
+ }
+
+
+ public static void setSensors(Sensors psens) {
+ if(sensors == null) {
+ sensors = psens;
+ }
+ }
+
+ public static Sensors getSensors() {
+ return sensors;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ startRide();
+ return Service.START_NOT_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return null;
+ }
+
+ public boolean onUnbind (Intent intent) {
+ return true;
+ }
+
+ @Override
+ public void onDestroy() {
+ stopRide();
+ super.onDestroy();
+ }
+
+ protected void startRide() {
+ if(rideStarted) return;
+
+ start_time = System.currentTimeMillis();
+ file_name = "ride-" + start_time + ".json";
+
+ current_values = new HashMap();
+
+ SimpleDateFormat f = new SimpleDateFormat("yyyy/MMM/dd HH:mm:ss");
+ f.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String utc = f.format(new Date(start_time));
+
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(start_time);
+
+ String month = cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US);
+ String week_day = cal.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.US);
+ String year = Integer.toString(cal.get(Calendar.YEAR));
+ settings = getSharedPreferences(StartActivity.PREFS_NAME, 0);
+ String rider_name = settings.getString(StartActivity.RIDER_NAME, "");
+ final Set pairedAnts = settings.getStringSet(StartActivity.PAIRED_ANTS, null);
+
+
+ current_values.put("SECS", "0.0");
+
+ String rideHeadder = "{" +
+ "\"RIDE\":{" +
+ "\"STARTTIME\":\"" + utc + " UTC\"," +
+ "\"RECINTSECS\":1," +
+ "\"DEVICETYPE\":\"Android\"," +
+ "\"IDENTIFIER\":\"\"," +
+ "\"TAGS\":{" +
+ "\"Athlete\":\"" + rider_name + "\"," +
+ "\"Calendar Text\":\"Auto Recored Android Ride\"," +
+ "\"Change History\":\"\"," +
+ "\"Data\":\"\"," +
+ "\"Device\":\"\"," +
+ "\"Device Info\":\"\"," +
+ "\"File Format\":\"\"," +
+ "\"Filename\":\"\"," +
+ "\"Month\":\"" + month +"\"," +
+ "\"Notes\":\"\"," +
+ "\"Objective\":\"\"," +
+ "\"Sport\":\"Bike\"," +
+ "\"Weekday\":\"" + week_day + "\"," +
+ "\"Workout Code\":\"\"," +
+ "\"Year\":\"" + year + "\"" +
+ "}," +
+ "\"SAMPLES\":[{\"SECS\":0}";
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ File dir = new File(
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOCUMENTS
+ ),
+ "Rides"
+ );
+
+ File file = new File(
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOCUMENTS
+ ) + "/Rides",
+ "ride-" + start_time + ".json"
+ );
+
+ try {
+ dir.mkdirs();
+ file.createNewFile();
+ buf = new BufferedWriter(new FileWriter(file, true));
+ buf.write(rideHeadder);
+
+ if(gps == null) {
+ gps = new Gps(this);
+ }
+
+ if(sensors == null) {
+ sensors = new Sensors(this);
+ }
+
+ mCallback = new MultiDeviceSearch.SearchCallbacks(){
+ public void onDeviceFound(final MultiDeviceSearchResult deviceFound)
+ {
+ if (!deviceFound.isAlreadyConnected() && (pairedAnts == null || pairedAnts.contains(Integer.toString(deviceFound.getAntDeviceNumber())))) {
+ launchConnection(deviceFound);
+ }
+ }
+
+ @Override
+ public void onSearchStopped(RequestAccessResult arg0) {}
+ };
+
+ mRssiCallback = new MultiDeviceSearch.RssiCallback() {
+ @Override
+ public void onRssiUpdate(final int resultId, final int rssi){}
+ };
+
+ // start the multi-device search
+ mSearch = new MultiDeviceSearch(this, EnumSet.allOf(DeviceType.class), mCallback, mRssiCallback);
+ } catch (IOException e) {}
+
+ }
+ rideStarted = true;
+
+
+ NotificationCompat.Builder mBuilder =
+ new NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setContentTitle("Ride On")
+ .setContentText("Building ride: " + file_name + " Click to stop ride.");
+ mBuilder.setProgress(0, 0, true);
+ // Creates an explicit intent for an Activity in your app
+ Intent resultIntent = new Intent(this, StartActivity.class);
+
+ // The stack builder object will contain an artificial back stack for the
+ // started Activity.
+ // This ensures that navigating backward from the Activity leads out of
+ // your application to the Home screen.
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+ // Adds the back stack for the Intent (but not the Intent itself)
+ stackBuilder.addParentStack(StartActivity.class);
+ // Adds the Intent that starts the Activity to the top of the stack
+ stackBuilder.addNextIntent(resultIntent);
+ PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+ mBuilder.setContentIntent(resultPendingIntent);
+ if(mNotificationManager == null) {
+ mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ startForeground(notifyID, mBuilder.build());
+ }
+
+
+ protected void stopRide() {
+ if(!rideStarted) return;
+
+ if(w != null) {
+ w.onDestroy();
+ }
+
+ if(hr != null) {
+ hr.onDestroy();
+ }
+
+ if(gps != null) {
+ gps.onDestroy();
+ }
+
+ if(sensors != null) {
+ sensors.onDestroy();
+ }
+
+ mSearch.close();
+
+ try {
+ buf.write("]}}");
+ buf.close();
+ } catch (IOException e) {}
+
+ rideStarted = false;
+ mNotificationManager.cancel(notifyID);
+ }
+
+
+ public void launchConnection(MultiDeviceSearchResult result)
+ {
+ switch (result.getAntDeviceType())
+ {
+ case BIKE_CADENCE:
+ break;
+ case BIKE_POWER:
+ if(w == null) {
+ w = new Power(result, this);
+ }
+ break;
+ case BIKE_SPD:
+ break;
+ case BIKE_SPDCAD:
+ break;
+ case BLOOD_PRESSURE:
+ break;
+ case ENVIRONMENT:
+ break;
+ case WEIGHT_SCALE:
+ break;
+ case HEARTRATE:
+ if(hr == null) {
+ hr = new HeartRate(result, this);
+ }
+ break;
+ case STRIDE_SDM:
+ break;
+ case FITNESS_EQUIPMENT:
+ break;
+ case GEOCACHE:
+ case CONTROLLABLE_DEVICE:
+ break;
+ case UNKNOWN:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
diff --git a/contrib/RideLogger/src/com/ridelogger/StartActivity.java b/contrib/RideLogger/src/com/ridelogger/StartActivity.java
new file mode 100644
index 000000000..97172b317
--- /dev/null
+++ b/contrib/RideLogger/src/com/ridelogger/StartActivity.java
@@ -0,0 +1,196 @@
+package com.ridelogger;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashSet;
+
+import com.dsi.ant.plugins.antplus.pcc.defines.DeviceType;
+import com.dsi.ant.plugins.antplus.pcc.defines.RequestAccessResult;
+import com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch;
+import com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch.MultiDeviceSearchResult;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.text.InputType;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class StartActivity extends FragmentActivity
+{
+ Intent rsi;
+ public static final String PREFS_NAME = "RideLogger";
+ public static final String RIDER_NAME = "RiderName";
+ public static final String PAIRED_ANTS = "PairedAnts";
+ SharedPreferences settings;
+ AlertDialog dialog;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ rsi = new Intent(this, RideService.class);
+
+ settings = getSharedPreferences(PREFS_NAME, 0);
+ String rider_name = settings.getString(RIDER_NAME, "");
+
+ if(rider_name == "") {
+ // 1. Instantiate an AlertDialog.Builder with its constructor
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ // 2. Chain together various setter methods to set the dialog characteristics
+ builder.setMessage("What is your Golder Cheata Rider Name")
+ .setTitle("Chose Rider Name");
+
+ // Set up the input
+ final EditText input = new EditText(this);
+ // Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
+ input.setInputType(InputType.TYPE_CLASS_TEXT);
+ builder.setView(input);
+
+
+ builder.setPositiveButton("Set", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ String name = input.getText().toString();
+ if(name != "" && name != null) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putString(RIDER_NAME, name);
+ editor.commit();
+ setupAnt();
+ }
+ }
+ });
+
+ // 3. Get the AlertDialog from create()
+ dialog = builder.create();
+ dialog.show();
+ } else {
+ toggleRide();
+ finish();
+ }
+ }
+
+ protected void setupAnt() {
+
+ MultiDeviceSearch mSearch;
+ MultiDeviceSearch.SearchCallbacks mCallback;
+ MultiDeviceSearch.RssiCallback mRssiCallback;
+ final ArrayList foundDevices = new ArrayList();
+ mCallback = new MultiDeviceSearch.SearchCallbacks(){
+ public void onDeviceFound(final MultiDeviceSearchResult deviceFound)
+ {
+ if(!foundDevices.contains(deviceFound)) {
+ foundDevices.add(deviceFound);
+ selectDevicesDialog(foundDevices);
+ }
+ }
+
+ @Override
+ public void onSearchStopped(RequestAccessResult arg0) {}
+ };
+
+ mRssiCallback = new MultiDeviceSearch.RssiCallback() {
+ @Override
+ public void onRssiUpdate(final int resultId, final int rssi){}
+ };
+
+ // start the multi-device search
+ mSearch = new MultiDeviceSearch(this, EnumSet.allOf(DeviceType.class), mCallback, mRssiCallback);
+ }
+
+ protected void selectDevicesDialog(final ArrayList foundDevices) {
+ final ArrayList mSelectedItems = new ArrayList(); // Where we track the selected items
+ final ArrayList foundDevicesString = new ArrayList();
+
+ for(MultiDeviceSearchResult device : foundDevices) {
+ foundDevicesString.add(device.getAntDeviceType() + " - " + device.getDeviceDisplayName());
+ }
+ CharSequence[] foundDevicesCharSeq = foundDevicesString.toArray(new CharSequence[foundDevicesString.size()]);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ // Set the dialog title
+ builder.setTitle("Select Paired Ant Devices")
+ // Specify the list array, the items to be selected by default (null for none),
+ // and the listener through which to receive callbacks when items are selected
+ .setMultiChoiceItems(foundDevicesCharSeq, null, new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ if (isChecked) {
+ // If the user checked the item, add it to the selected items
+ mSelectedItems.add(which);
+ } else if (mSelectedItems.contains(which)) {
+ // Else, if the item is already in the array, remove it
+ mSelectedItems.remove(Integer.valueOf(which));
+ }
+ }
+ })
+ // Set the action buttons
+ .setPositiveButton("Pair", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ ArrayList ids = new ArrayList();
+ for(Integer index : mSelectedItems) {
+ ids.add(Integer.toString(foundDevices.get(index).getAntDeviceNumber()));
+ }
+
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putStringSet(PAIRED_ANTS, new HashSet(ids));
+ editor.commit();
+ toggleRide();
+ finish();
+ }
+ });
+
+ dialog = builder.create();
+ dialog.show();
+ }
+
+ @Override
+ protected void onDestroy() {
+ // TODO Auto-generated method stub
+ super.onDestroy();
+ }
+
+
+ private void stopRide() {
+ Toast toast = Toast.makeText(getApplicationContext(), "Stoping Ride!", Toast.LENGTH_LONG);
+ toast.show();
+ this.stopService(rsi);
+ }
+
+
+ protected void toggleRide() {
+ if(!isServiceRunning(RideService.class)) {
+ startRide();
+ } else {
+ stopRide();
+ }
+ }
+
+
+ private void startRide() {
+ if(!isServiceRunning(RideService.class)) {
+ this.startService(rsi);
+ }
+ Toast toast = Toast.makeText(getApplicationContext(), "Starting Ride!", Toast.LENGTH_LONG);
+ toast.show();
+ }
+
+
+ private boolean isServiceRunning(Class> serviceClass) {
+ ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+ if (serviceClass.getName().equals(service.service.getClassName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/contrib/RideLogger/src/com/ridelogger/listners/Base.java b/contrib/RideLogger/src/com/ridelogger/listners/Base.java
new file mode 100644
index 000000000..0f9476f98
--- /dev/null
+++ b/contrib/RideLogger/src/com/ridelogger/listners/Base.java
@@ -0,0 +1,114 @@
+package com.ridelogger.listners;
+
+import android.content.Context;
+
+import com.dsi.ant.plugins.antplus.pcc.defines.DeviceState;
+import com.dsi.ant.plugins.antplus.pccbase.PccReleaseHandle;
+import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.IDeviceStateChangeReceiver;
+import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.IPluginAccessResultReceiver;
+import com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch.MultiDeviceSearchResult;
+import com.ridelogger.RideService;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Base class to connects to Heart Rate Plugin and display all the event data.
+ */
+public class Base
+{
+ public static BufferedWriter buf;
+ public static long start_time;
+ public static Map current_values;
+ public PccReleaseHandle> releaseHandle;
+ public Context context;
+
+ public Base(MultiDeviceSearchResult result, Context mContext) {
+ init(mContext);
+ }
+
+ public Base(Context mContext) {
+ init(mContext);
+ }
+
+ public void init(Context mContext) {
+ buf = RideService.getBuf();
+ start_time = RideService.getStartTime();
+ current_values = RideService.getCurrentValues();
+ context = mContext;
+ }
+
+ IDeviceStateChangeReceiver mDeviceStateChangeReceiver = new IDeviceStateChangeReceiver() {
+ @Override
+ public void onDeviceStateChange(final DeviceState newDeviceState){}
+ };
+
+ public IPluginAccessResultReceiver> mResultReceiver;
+
+ public void writeData(String key, String value)
+ {
+ if(!current_values.containsKey(key) || current_values.get(key) != value) {
+ String ts = String.valueOf((double) (System.currentTimeMillis() - start_time) / 1000.0);
+ current_values.put("SECS", ts);
+ current_values.put(key, value);
+
+ try {
+ buf.write(",{");
+
+ buf.write("\"");
+ buf.write("SECS");
+ buf.write("\":");
+ buf.write(ts);
+
+ buf.write(",\"");
+ buf.write(key);
+ buf.write("\":");
+ buf.write(value);
+
+ buf.write("}");
+ } catch (IOException e) {}
+ }
+ }
+
+
+ public void writeData(Map map)
+ {
+ String ts = String.valueOf((double) (System.currentTimeMillis() - start_time) / 1000.0);
+ current_values.put("SECS", ts);
+
+ try {
+ buf.write(",{");
+
+ buf.write("\"");
+ buf.write("SECS");
+ buf.write("\":");
+ buf.write(ts);
+
+ for (Map.Entry entry : map.entrySet())
+ {
+ String key = entry.getKey();
+ String value = entry.getValue();
+
+ buf.write(",\"");
+ buf.write(key);
+ buf.write("\":");
+ buf.write(value);
+
+ current_values.put(key, value);
+ }
+
+ buf.write("}");
+ } catch (IOException e) {}
+ }
+
+
+ public void onDestroy()
+ {
+ if(releaseHandle != null) {
+ releaseHandle.close();
+ }
+ }
+}
+
+
diff --git a/contrib/RideLogger/src/com/ridelogger/listners/Gps.java b/contrib/RideLogger/src/com/ridelogger/listners/Gps.java
new file mode 100644
index 000000000..c86ddc27d
--- /dev/null
+++ b/contrib/RideLogger/src/com/ridelogger/listners/Gps.java
@@ -0,0 +1,45 @@
+package com.ridelogger.listners;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+
+public class Gps extends Base
+{
+ public Gps(Context mContext)
+ {
+ super(mContext);
+
+ LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+
+
+ LocationListener locationListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ Map map = new HashMap();
+ map.put("ALTITUDE", Double.toString(location.getAltitude()) );
+ map.put("KPH", Float.toString( location.getSpeed()) );
+ map.put("bearing", Float.toString( location.getBearing()) );
+ map.put("gpsa", Float.toString( location.getAccuracy()) );
+ map.put("LAT", Double.toString(location.getLatitude()) );
+ map.put("LON", Double.toString(location.getLongitude()));
+ writeData(map);
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+
+ @Override
+ public void onProviderEnabled(String provider) {}
+
+ @Override
+ public void onProviderDisabled(String provider) {}
+ };
+
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
+ }
+}
\ No newline at end of file
diff --git a/contrib/RideLogger/src/com/ridelogger/listners/HeartRate.java b/contrib/RideLogger/src/com/ridelogger/listners/HeartRate.java
new file mode 100644
index 000000000..b91152d0e
--- /dev/null
+++ b/contrib/RideLogger/src/com/ridelogger/listners/HeartRate.java
@@ -0,0 +1,44 @@
+package com.ridelogger.listners;
+
+import android.content.Context;
+
+import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.DataState;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.IHeartRateDataReceiver;
+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.IPluginAccessResultReceiver;
+import com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch.MultiDeviceSearchResult;
+
+import java.math.BigDecimal;
+import java.util.EnumSet;
+
+/**
+ * Base class to connects to Heart Rate Plugin and display all the event data.
+ */
+public class HeartRate extends Base
+{
+ public HeartRate(MultiDeviceSearchResult result, Context mContext) {
+ super(result, mContext);
+ releaseHandle = AntPlusHeartRatePcc.requestAccess(context, result.getAntDeviceNumber(), 0, mResultReceiver, mDeviceStateChangeReceiver);
+ }
+
+ public IPluginAccessResultReceiver mResultReceiver = new IPluginAccessResultReceiver() {
+ //Handle the result, connecting to events on success or reporting failure to user.
+ @Override
+ public void onResultReceived(AntPlusHeartRatePcc result, RequestAccessResult resultCode, DeviceState initialDeviceState)
+ {
+ if(resultCode == com.dsi.ant.plugins.antplus.pcc.defines.RequestAccessResult.SUCCESS) {
+ result.subscribeHeartRateDataEvent(
+ new IHeartRateDataReceiver() {
+ @Override
+ public void onNewHeartRateData(final long estTimestamp, EnumSet eventFlags, final int computedHeartRate, final long heartBeatCount, final BigDecimal heartBeatEventTime, final DataState dataState) {
+ writeData("HR", String.valueOf(computedHeartRate));
+ }
+ }
+ );
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/contrib/RideLogger/src/com/ridelogger/listners/Power.java b/contrib/RideLogger/src/com/ridelogger/listners/Power.java
new file mode 100644
index 000000000..5a2971a72
--- /dev/null
+++ b/contrib/RideLogger/src/com/ridelogger/listners/Power.java
@@ -0,0 +1,183 @@
+package com.ridelogger.listners;
+
+import android.content.Context;
+
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.CalculatedWheelDistanceReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.CalculatedWheelSpeedReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.DataSource;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.ICalculatedCrankCadenceReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.ICalculatedPowerReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.ICalculatedTorqueReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.IInstantaneousCadenceReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.IPedalPowerBalanceReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.IPedalSmoothnessReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.IRawCrankTorqueDataReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.IRawPowerOnlyDataReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.IRawWheelTorqueDataReceiver;
+import com.dsi.ant.plugins.antplus.pcc.AntPlusBikePowerPcc.ITorqueEffectivenessReceiver;
+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.IDeviceStateChangeReceiver;
+import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.IPluginAccessResultReceiver;
+import com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch.MultiDeviceSearchResult;
+
+import java.math.BigDecimal;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class to connects to Heart Rate Plugin and display all the event data.
+ */
+public class Power extends Base
+{
+
+ public Power(MultiDeviceSearchResult result, Context mContext) {
+ super(result, mContext);
+ releaseHandle = AntPlusBikePowerPcc.requestAccess(context, result.getAntDeviceNumber(), 0, mResultReceiver, mDeviceStateChangeReceiver);
+ }
+
+ BigDecimal wheelCircumferenceInMeters = new BigDecimal("2.07");
+
+ IDeviceStateChangeReceiver mDeviceStateChangeReceiver = new IDeviceStateChangeReceiver()
+ {
+ @Override
+ public void onDeviceStateChange(final DeviceState newDeviceState){}
+ };
+
+
+ protected IPluginAccessResultReceiver mResultReceiver = new IPluginAccessResultReceiver() {
+ //Handle the result, connecting to events on success or reporting failure to user.
+ @Override
+ public void onResultReceived(AntPlusBikePowerPcc result, RequestAccessResult resultCode, DeviceState initialDeviceState)
+ {
+ if(resultCode == com.dsi.ant.plugins.antplus.pcc.defines.RequestAccessResult.SUCCESS) {
+ result.subscribeCalculatedPowerEvent(new ICalculatedPowerReceiver() {
+ @Override
+ public void onNewCalculatedPower(final long estTimestamp, final EnumSet eventFlags, final DataSource dataSource, final BigDecimal calculatedPower) {
+ writeData("WATTS", String.format("%.1f", calculatedPower));
+ }
+ }
+ );
+
+ result.subscribeCalculatedTorqueEvent(
+ new ICalculatedTorqueReceiver() {
+ @Override
+ public void onNewCalculatedTorque(final long estTimestamp, final EnumSet eventFlags, final DataSource dataSource, final BigDecimal calculatedTorque) {
+ writeData("NM", String.format("%.1f", calculatedTorque));
+ }
+ }
+ );
+
+ result.subscribeCalculatedCrankCadenceEvent(
+ new ICalculatedCrankCadenceReceiver() {
+ @Override
+ public void onNewCalculatedCrankCadence(final long estTimestamp, final EnumSet eventFlags, final DataSource dataSource, final BigDecimal calculatedCrankCadence) {
+ writeData("RPM", String.format("%.1f", calculatedCrankCadence));
+ }
+ }
+ );
+
+ result.subscribeCalculatedWheelSpeedEvent(
+ new CalculatedWheelSpeedReceiver(wheelCircumferenceInMeters) {
+ @Override
+ public void onNewCalculatedWheelSpeed(final long estTimestamp, final EnumSet eventFlags, final DataSource dataSource, final BigDecimal calculatedWheelSpeed)
+ {
+ writeData("KMH", String.format("%.1f", calculatedWheelSpeed));
+ }
+ }
+ );
+
+ result.subscribeCalculatedWheelDistanceEvent(
+ new CalculatedWheelDistanceReceiver(wheelCircumferenceInMeters) {
+ @Override
+ public void onNewCalculatedWheelDistance(final long estTimestamp, final EnumSet eventFlags, final DataSource dataSource, final BigDecimal calculatedWheelDistance)
+ {
+ writeData("KM", String.format("%.1f", calculatedWheelDistance));
+ }
+ }
+ );
+
+ result.subscribeInstantaneousCadenceEvent(
+ new IInstantaneousCadenceReceiver() {
+ @Override
+ public void onNewInstantaneousCadence(final long estTimestamp, final EnumSet eventFlags, final DataSource dataSource, final int instantaneousCadence)
+ {
+ writeData("RPM", String.format("%d", instantaneousCadence));
+ }
+ }
+ );
+
+ result.subscribeRawPowerOnlyDataEvent(
+ new IRawPowerOnlyDataReceiver() {
+ @Override
+ public void onNewRawPowerOnlyData(final long estTimestamp, final EnumSet eventFlags, final long powerOnlyUpdateEventCount, final int instantaneousPower, final long accumulatedPower)
+ {
+ writeData("WATTS", String.format("%d", instantaneousPower));
+ }
+ }
+ );
+
+ result.subscribePedalPowerBalanceEvent(
+ new IPedalPowerBalanceReceiver() {
+ @Override
+ public void onNewPedalPowerBalance(final long estTimestamp, final EnumSet eventFlags, final boolean rightPedalIndicator, final int pedalPowerPercentage)
+ {
+ writeData("LTE", String.format("%d", pedalPowerPercentage));
+ }
+ }
+ );
+
+ result.subscribeRawWheelTorqueDataEvent(
+ new IRawWheelTorqueDataReceiver() {
+ @Override
+ public void onNewRawWheelTorqueData(final long estTimestamp, final EnumSet eventFlags, final long wheelTorqueUpdateEventCount, final long accumulatedWheelTicks, final BigDecimal accumulatedWheelPeriod, final BigDecimal accumulatedWheelTorque)
+ {
+ writeData("NM", String.format("%.1f", accumulatedWheelTorque));
+ }
+ }
+ );
+
+ result.subscribeRawCrankTorqueDataEvent(
+ new IRawCrankTorqueDataReceiver() {
+ @Override
+ public void onNewRawCrankTorqueData(final long estTimestamp, final EnumSet eventFlags, final long crankTorqueUpdateEventCount, final long accumulatedCrankTicks, final BigDecimal accumulatedCrankPeriod, final BigDecimal accumulatedCrankTorque)
+ {
+ writeData("NM", String.format("%.1f", accumulatedCrankTorque));
+ }
+ }
+ );
+
+ result.subscribeTorqueEffectivenessEvent(
+ new ITorqueEffectivenessReceiver() {
+ @Override
+ public void onNewTorqueEffectiveness(final long estTimestamp, final EnumSet eventFlags, final long powerOnlyUpdateEventCount, final BigDecimal leftTorqueEffectiveness, final BigDecimal rightTorqueEffectiveness)
+ {
+ Map map = new HashMap();
+ map.put("LTE", String.format("%.1f", leftTorqueEffectiveness));
+ map.put("RTE", String.format("%.1f", rightTorqueEffectiveness));
+ writeData(map);
+ }
+
+ }
+ );
+
+ result.subscribePedalSmoothnessEvent(new IPedalSmoothnessReceiver() {
+ @Override
+ public void onNewPedalSmoothness(final long estTimestamp, final EnumSet eventFlags, final long powerOnlyUpdateEventCount, final boolean separatePedalSmoothnessSupport, final BigDecimal leftOrCombinedPedalSmoothness, final BigDecimal rightPedalSmoothness)
+ {
+ Map map = new HashMap();
+ map.put("SNPLC", String.format("%.1f", leftOrCombinedPedalSmoothness));
+ map.put("SNPR", String.format("%.1f", rightPedalSmoothness));
+ writeData(map);
+ }
+ }
+ );
+ }
+ }
+ };
+}
+
+
diff --git a/contrib/RideLogger/src/com/ridelogger/listners/Sensors.java b/contrib/RideLogger/src/com/ridelogger/listners/Sensors.java
new file mode 100644
index 000000000..c8ecefdf9
--- /dev/null
+++ b/contrib/RideLogger/src/com/ridelogger/listners/Sensors.java
@@ -0,0 +1,141 @@
+package com.ridelogger.listners;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+public class Sensors extends Base
+{
+ private SensorManager mSensorManager;
+
+ private Sensor mLight;
+ private Sensor mAccel;
+ private Sensor mPress;
+ private Sensor mTemp;
+ private Sensor mField;
+
+ private SensorEventListener luxListner;
+ private SensorEventListener accelListner;
+ private SensorEventListener pressListner;
+ private SensorEventListener tempListner;
+ private SensorEventListener fieldListner;
+
+ public Sensors(Context mContext)
+ {
+ super(mContext);
+
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+
+ mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
+ mPress = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
+ mTemp = mSensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
+ mField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
+
+ if(mLight != null) {
+ luxListner = new SensorEventListener() {
+ @Override
+ public final void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ @Override
+ public final void onSensorChanged(SensorEvent event) {
+ // The light sensor returns a single value.
+ // Many sensors return 3 values, one for each axis.
+ writeData("lux", Float.toString(event.values[0]));
+ }
+ };
+
+ mSensorManager.registerListener(luxListner, mLight, 3000000);
+ }
+ if(mAccel != null) {
+ accelListner = new SensorEventListener() {
+ @Override
+ public final void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ @Override
+ public final void onSensorChanged(SensorEvent event) {
+ Map map = new HashMap();
+ map.put("ms2x", Float.toString(event.values[0]));
+ map.put("ms2y", Float.toString(event.values[1]));
+ map.put("ms2z", Float.toString(event.values[2]));
+ writeData(map);
+ }
+ };
+
+ mSensorManager.registerListener(accelListner, mAccel, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if(mPress != null) {
+ pressListner = new SensorEventListener() {
+ @Override
+ public final void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ @Override
+ public final void onSensorChanged(SensorEvent event) {
+ // The light sensor returns a single value.
+ // Many sensors return 3 values, one for each axis.
+ writeData("press", Float.toString(event.values[0]));
+ }
+ };
+
+ mSensorManager.registerListener(pressListner, mPress, 3000000);
+ }
+ if(mTemp != null) {
+ tempListner = new SensorEventListener() {
+ @Override
+ public final void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ @Override
+ public final void onSensorChanged(SensorEvent event) {
+ // The light sensor returns a single value.
+ // Many sensors return 3 values, one for each axis.
+ writeData("temp", Float.toString(event.values[0]));
+ }
+ };
+
+ mSensorManager.registerListener(tempListner, mTemp, 3000000);
+ }
+ if(mField != null) {
+ fieldListner = new SensorEventListener() {
+ @Override
+ public final void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ @Override
+ public final void onSensorChanged(SensorEvent event) {
+ Map map = new HashMap();
+ map.put("uTx", Float.toString(event.values[0]));
+ map.put("uTy", Float.toString(event.values[1]));
+ map.put("uTz", Float.toString(event.values[2]));
+ writeData(map);
+ }
+ };
+
+ mSensorManager.registerListener(fieldListner, mField, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ }
+
+
+ @Override
+ public void onDestroy()
+ {
+ if(luxListner != null) {
+ mSensorManager.unregisterListener(luxListner);
+ }
+ if(accelListner != null) {
+ mSensorManager.unregisterListener(accelListner);
+ }
+ if(pressListner != null) {
+ mSensorManager.unregisterListener(pressListner);
+ }
+ if(tempListner != null) {
+ mSensorManager.unregisterListener(tempListner);
+ }
+ if(fieldListner != null) {
+ mSensorManager.unregisterListener(fieldListner);
+ }
+ }
+}
\ No newline at end of file