forked from upstream-mirrors/OpenTracks
Merging gingerbread changes to head.
This commit is contained in:
@@ -16,11 +16,14 @@
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.ChartView.Mode;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||
@@ -178,12 +181,12 @@ public class ChartActivity extends Activity implements
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
ArrayList<double[]> data = new ArrayList<double[]>();
|
||||
// Need two locations so we can keep track of the last location.
|
||||
Location location = new Location("");
|
||||
Location location = new MyTracksLocation("");
|
||||
do {
|
||||
lastSeenLocationId = cursor.getLong(idColumnIdx);
|
||||
providerUtils.fillLocation(cursor, location);
|
||||
if (MyTracksUtils.isValidLocation(location)) {
|
||||
double[] point = new double[3];
|
||||
double[] point = new double[6];
|
||||
location = getDataPoint(location, track, point);
|
||||
data.add(point);
|
||||
}
|
||||
@@ -464,10 +467,14 @@ public class ChartActivity extends Activity implements
|
||||
|
||||
/**
|
||||
* Given a location, creates a new data point for the chart. A data point is
|
||||
* an array double[2], where:
|
||||
* an array double[3 or 6], where:
|
||||
* data[0] = the time or distance
|
||||
* data[1] = the elevation
|
||||
* data[2] = the speed
|
||||
* and possibly:
|
||||
* data[3] = power
|
||||
* data[4] = cadence
|
||||
* data[5] = heart rate
|
||||
*
|
||||
* This must be called in order for each point.
|
||||
*
|
||||
@@ -477,6 +484,35 @@ public class ChartActivity extends Activity implements
|
||||
* @return the previous location, now available for reuse
|
||||
*/
|
||||
private Location getDataPoint(Location location, Track track, double[] result) {
|
||||
if (location instanceof MyTracksLocation &&
|
||||
((MyTracksLocation) location).getSensorDataSet() != null) {
|
||||
SensorDataSet sensorData = ((MyTracksLocation) location).getSensorDataSet();
|
||||
if (sensorData.hasPower()
|
||||
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getPower().hasValue()) {
|
||||
result[3] = sensorData.getPower().getValue();
|
||||
} else {
|
||||
result[3] = Double.NaN;
|
||||
}
|
||||
if (sensorData.hasCadence()
|
||||
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getCadence().hasValue()) {
|
||||
result[4] = sensorData.getCadence().getValue();
|
||||
} else {
|
||||
result[4] = Double.NaN;
|
||||
}
|
||||
if (sensorData.hasHeartRate()
|
||||
&& sensorData.getHeartRate().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getHeartRate().hasValue()) {
|
||||
result[5] = sensorData.getHeartRate().getValue();
|
||||
} else {
|
||||
result[5] = Double.NaN;
|
||||
}
|
||||
} else {
|
||||
result[3] = Double.NaN;
|
||||
result[4] = Double.NaN;
|
||||
result[5] = Double.NaN;
|
||||
}
|
||||
switch (mode) {
|
||||
case BY_DISTANCE:
|
||||
result[0] = profileLength;
|
||||
@@ -529,7 +565,7 @@ public class ChartActivity extends Activity implements
|
||||
|
||||
if (oldLastLocation == null) {
|
||||
// No previous location, but return a blank one for reuse
|
||||
return new Location("");
|
||||
return new MyTracksLocation("");
|
||||
}
|
||||
|
||||
return oldLastLocation;
|
||||
@@ -576,7 +612,7 @@ public class ChartActivity extends Activity implements
|
||||
final ArrayList<double[]> theData = new ArrayList<double[]>();
|
||||
int points = 0;
|
||||
// Need two locations so we can keep track of the last location.
|
||||
Location location = new Location("");
|
||||
Location location = new MyTracksLocation("");
|
||||
while (lastLocationRead < track.getStopId()) {
|
||||
cursor = providerUtils.getLocationsCursor(
|
||||
selectedTrackId, lastLocationRead, bufferSize, false);
|
||||
@@ -592,7 +628,8 @@ public class ChartActivity extends Activity implements
|
||||
if (MyTracksUtils.isValidLocation(location)) {
|
||||
lastLocationRead = lastSeenLocationId =
|
||||
cursor.getLong(idColumnIdx);
|
||||
double[] point = new double[3];
|
||||
// TODO Can we be smarter about choosing 3 or 6 entries?
|
||||
double[] point = new double[6];
|
||||
location = getDataPoint(location, track, point);
|
||||
if (points % chartSamplingFrequency == 0) {
|
||||
theData.add(point);
|
||||
|
||||
@@ -35,8 +35,7 @@ import android.widget.RadioGroup;
|
||||
*/
|
||||
public class ChartSettingsDialog extends Dialog {
|
||||
private RadioButton distance;
|
||||
private CheckBox elevation;
|
||||
private CheckBox speed;
|
||||
private CheckBox[] series;
|
||||
|
||||
public ChartSettingsDialog(Context context) {
|
||||
super(context);
|
||||
@@ -61,8 +60,18 @@ public class ChartSettingsDialog extends Dialog {
|
||||
});
|
||||
|
||||
distance = (RadioButton) findViewById(R.id.chart_settings_by_distance);
|
||||
elevation = (CheckBox) findViewById(R.id.chart_settings_elevation);
|
||||
speed = (CheckBox) findViewById(R.id.chart_settings_speed);
|
||||
|
||||
series = new CheckBox[ChartView.NUM_SERIES];
|
||||
series[ChartView.ELEVATION_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_elevation);
|
||||
series[ChartView.SPEED_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_speed);
|
||||
series[ChartView.POWER_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_power);
|
||||
series[ChartView.CADENCE_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_cadence);
|
||||
series[ChartView.HEART_RATE_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_heart_rate);
|
||||
}
|
||||
|
||||
public void setup(ChartActivity chart) {
|
||||
@@ -74,8 +83,9 @@ public class ChartSettingsDialog extends Dialog {
|
||||
rd.check(chart.getMode() == Mode.BY_DISTANCE
|
||||
? R.id.chart_settings_by_distance
|
||||
: R.id.chart_settings_by_time);
|
||||
elevation.setChecked(chart.isSeriesEnabled(ChartView.ELEVATION_SERIES));
|
||||
speed.setChecked(chart.isSeriesEnabled(ChartView.SPEED_SERIES));
|
||||
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||
series[i].setChecked(chart.isSeriesEnabled(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOk() {
|
||||
@@ -83,8 +93,9 @@ public class ChartSettingsDialog extends Dialog {
|
||||
chart.setMode(distance.isChecked() ? Mode.BY_DISTANCE : Mode.BY_TIME);
|
||||
|
||||
// TODO: check that something is visible.
|
||||
chart.setSeriesEnabled(ChartView.ELEVATION_SERIES, elevation.isChecked());
|
||||
chart.setSeriesEnabled(ChartView.SPEED_SERIES, speed.isChecked());
|
||||
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||
chart.setSeriesEnabled(i, series[i].isChecked());
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class ChartValueSeries {
|
||||
private double min = 0;
|
||||
private double max = 1;
|
||||
private int effectiveMax = 0;
|
||||
private int absoluteMax = Integer.MIN_VALUE;
|
||||
private int effectiveMin = 0;
|
||||
private double spread = 0;
|
||||
private boolean enabled = true;
|
||||
@@ -131,7 +132,7 @@ public class ChartValueSeries {
|
||||
if (min < 0) {
|
||||
effectiveMin -= rounding;
|
||||
}
|
||||
spread = effectiveMax - effectiveMin;
|
||||
spread = getMax() - effectiveMin;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +140,7 @@ public class ChartValueSeries {
|
||||
*/
|
||||
public int getMaxLabelLength() {
|
||||
String minS = format.format(effectiveMin);
|
||||
String maxS = format.format(effectiveMax);
|
||||
String maxS = format.format(getMax());
|
||||
return Math.max(minS.length(), maxS.length());
|
||||
}
|
||||
|
||||
@@ -151,10 +152,16 @@ public class ChartValueSeries {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The rounded up maximum value
|
||||
* @return The minimum of the rounded up max value and the effective max.
|
||||
*/
|
||||
public int getMax() {
|
||||
return effectiveMax;
|
||||
// Return the effective max if no absolute is set
|
||||
// or the effective max is less than the absolute.
|
||||
if ((absoluteMax == Integer.MIN_VALUE) || (effectiveMax < absoluteMax)) {
|
||||
return effectiveMax;
|
||||
} else {
|
||||
return absoluteMax;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,4 +218,16 @@ public class ChartValueSeries {
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getAbsoluteMax() {
|
||||
return absoluteMax;
|
||||
}
|
||||
|
||||
public void setAbsoluteMax(int absoluteMax) {
|
||||
this.absoluteMax = absoluteMax;
|
||||
}
|
||||
|
||||
public boolean hasData() {
|
||||
return monitor.hasData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,10 @@ public class ChartView extends View {
|
||||
*/
|
||||
public static final int ELEVATION_SERIES = 0;
|
||||
public static final int SPEED_SERIES = 1;
|
||||
public static final int NUM_SERIES = 2;
|
||||
public static final int POWER_SERIES = 2;
|
||||
public static final int CADENCE_SERIES = 3;
|
||||
public static final int HEART_RATE_SERIES = 4;
|
||||
public static final int NUM_SERIES = 5;
|
||||
private ChartValueSeries[] series;
|
||||
|
||||
private final ExtremityMonitor xMonitor = new ExtremityMonitor();
|
||||
@@ -172,38 +175,44 @@ public class ChartView extends View {
|
||||
series = new ChartValueSeries[NUM_SERIES];
|
||||
|
||||
// Create the paints
|
||||
Paint fillPaint1 = new Paint();
|
||||
fillPaint1.setStyle(Style.FILL);
|
||||
fillPaint1.setColor(context.getResources().getColor(R.color.green));
|
||||
fillPaint1.setAntiAlias(true);
|
||||
|
||||
Paint fillPaint2 = new Paint();
|
||||
fillPaint2.setStyle(Style.FILL);
|
||||
fillPaint2.setColor(
|
||||
context.getResources().getColor(R.color.blue_transparent));
|
||||
fillPaint2.setAntiAlias(true);
|
||||
|
||||
Paint strokePaint = new Paint();
|
||||
strokePaint.setStyle(Style.STROKE);
|
||||
strokePaint.setColor(context.getResources().getColor(R.color.blue));
|
||||
strokePaint.setAntiAlias(true);
|
||||
Paint fillPaint = new Paint();
|
||||
fillPaint.setStyle(Style.FILL);
|
||||
fillPaint.setColor(context.getResources().getColor(R.color.green));
|
||||
fillPaint.setAntiAlias(true);
|
||||
|
||||
// Create the value series
|
||||
series[ELEVATION_SERIES] =
|
||||
new ChartValueSeries(context,
|
||||
"###,###",
|
||||
fillPaint1,
|
||||
fillPaint,
|
||||
null,
|
||||
100,
|
||||
R.string.elevation);
|
||||
|
||||
series[SPEED_SERIES] =
|
||||
new ChartValueSeries(context,
|
||||
"###,###.0",
|
||||
fillPaint2,
|
||||
strokePaint,
|
||||
5,
|
||||
R.string.speed);
|
||||
series[SPEED_SERIES] = getSeries(context, R.color.blue_transparent,
|
||||
R.color.blue, "###,###.0", R.string.speed);
|
||||
series[POWER_SERIES] = getSeries(context, R.color.red_transparent,
|
||||
R.color.red, "###,###", R.string.power);
|
||||
series[POWER_SERIES].setAbsoluteMax(1500);
|
||||
series[CADENCE_SERIES] = getSeries(context, R.color.purple_transparent,
|
||||
R.color.purple, "###,###", R.string.cadence);
|
||||
series[HEART_RATE_SERIES] = getSeries(context, R.color.yellow_transparent,
|
||||
R.color.yellow, "###,###", R.string.heart_rate);
|
||||
}
|
||||
|
||||
private ChartValueSeries getSeries(Context context,
|
||||
int fillColor, int strokeColor, String format, int seriesName) {
|
||||
Paint fillPaint = new Paint();
|
||||
fillPaint.setStyle(Style.FILL);
|
||||
fillPaint.setColor(context.getResources().getColor(fillColor));
|
||||
fillPaint.setAntiAlias(true);
|
||||
|
||||
Paint strokePaint = new Paint();
|
||||
strokePaint.setStyle(Style.STROKE);
|
||||
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
||||
strokePaint.setAntiAlias(true);
|
||||
return new ChartValueSeries(context, format, fillPaint, strokePaint, 5,
|
||||
seriesName);
|
||||
}
|
||||
|
||||
public void clearWaypoints() {
|
||||
@@ -266,13 +275,25 @@ public class ChartView extends View {
|
||||
*/
|
||||
public synchronized void addDataPoint(double[] theData) {
|
||||
data.add(theData);
|
||||
addDataPointInternal(theData);
|
||||
updateDimensions();
|
||||
setupPath();
|
||||
}
|
||||
|
||||
private void addDataPointInternal(double[] theData) {
|
||||
xMonitor.update(theData[0]);
|
||||
int min = Math.min(series.length, theData.length - 1);
|
||||
for (int i = 0; i < min; i++) {
|
||||
series[i].update(theData[i + 1]);
|
||||
if (!Double.isNaN(theData[i + 1])) {
|
||||
series[i].update(theData[i + 1]);
|
||||
}
|
||||
}
|
||||
// Fill in the extra's if needed.
|
||||
for (int i = theData.length; i < series.length; i++) {
|
||||
if (series[i].hasData()) {
|
||||
series[i].update(0);
|
||||
}
|
||||
}
|
||||
updateDimensions();
|
||||
setupPath();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,11 +305,7 @@ public class ChartView extends View {
|
||||
data.addAll(theData);
|
||||
for (int i = 0; i < theData.size(); i++) {
|
||||
double d[] = theData.get(i);
|
||||
xMonitor.update(d[0]);
|
||||
int min = Math.min(series.length, d.length - 1);
|
||||
for (int j = 0; j < min; j++) {
|
||||
series[j].update(d[j + 1]);
|
||||
}
|
||||
addDataPointInternal(d);
|
||||
}
|
||||
updateDimensions();
|
||||
setupPath();
|
||||
@@ -526,7 +543,7 @@ public class ChartView extends View {
|
||||
|
||||
// Draw the data series
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
cvs.drawPath(c);
|
||||
}
|
||||
}
|
||||
@@ -560,7 +577,7 @@ public class ChartView extends View {
|
||||
c.translate(getScrollX(), 0);
|
||||
drawYAxis(c);
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
drawYLabels(cvs, c);
|
||||
}
|
||||
}
|
||||
@@ -576,13 +593,13 @@ public class ChartView extends View {
|
||||
private void drawSeriesTitles(Canvas c) {
|
||||
int sections = 1;
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
sections++;
|
||||
}
|
||||
}
|
||||
int j = 0;
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
int y = (int) (w * zoomLevel * ((double) (++j) / sections));
|
||||
c.drawText(cvs.getTitle(), y, TOP_BORDER, cvs.getPaint());
|
||||
}
|
||||
@@ -601,11 +618,11 @@ public class ChartView extends View {
|
||||
return;
|
||||
}
|
||||
|
||||
int min = Math.min(series.length, data.get(0).length - 1);
|
||||
|
||||
// All of the data points to the respective series.
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
double[] d = data.get(i);
|
||||
int min = Math.min(series.length, d.length - 1);
|
||||
|
||||
for (int j = 0; j < min; j++) {
|
||||
ChartValueSeries cvs = series[j];
|
||||
Path path = cvs.getPath();
|
||||
@@ -616,17 +633,36 @@ public class ChartView extends View {
|
||||
// Close the path.
|
||||
int yCorner = TOP_BORDER + effectiveHeight;
|
||||
int xCorner = getX(data.get(0)[0]);
|
||||
int min = series.length;
|
||||
for (int j = 0; j < min; j++) {
|
||||
ChartValueSeries cvs = series[j];
|
||||
Path path = cvs.getPath();
|
||||
// Bottom right corner
|
||||
path.lineTo(getX(data.get(data.size() - 1)[0]), yCorner);
|
||||
// Bottom left corner
|
||||
path.lineTo(xCorner, yCorner);
|
||||
// Top right corner
|
||||
path.lineTo(xCorner, getY(cvs, data.get(0)[j + 1]));
|
||||
int first = getFirstPointPopulatedIndex(j + 1);
|
||||
if (first != -1) {
|
||||
// Bottom right corner
|
||||
path.lineTo(getX(data.get(data.size() - 1)[0]), yCorner);
|
||||
// Bottom left corner
|
||||
path.lineTo(xCorner, yCorner);
|
||||
// Top right corner
|
||||
path.lineTo(xCorner, getY(cvs, data.get(first)[j + 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first point which has a series populated.
|
||||
* @param seriesIndex The index of the value series to search for.
|
||||
* @return The index in the first data for the point in the series that has series
|
||||
* index value populated or -1 if none is found.
|
||||
*/
|
||||
private int getFirstPointPopulatedIndex(int seriesIndex) {
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
if (data.get(i).length > seriesIndex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the histogram dimensions.
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -63,8 +64,8 @@ class MenuManager {
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_list_tracks: {
|
||||
Intent startIntent = new Intent(activity, MyTracksList.class);
|
||||
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_TRACK);
|
||||
activity.startActivityForResult(new Intent(activity, MyTracksList.class),
|
||||
MyTracksConstants.SHOW_TRACK);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_list_markers: {
|
||||
@@ -73,20 +74,17 @@ class MenuManager {
|
||||
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_WAYPOINT);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_sensor_state: {
|
||||
return startActivity(SensorStateActivity.class);
|
||||
}
|
||||
case R.id.menu_settings: {
|
||||
Intent startIntent = new Intent(activity, MyTracksSettings.class);
|
||||
activity.startActivity(startIntent);
|
||||
return true;
|
||||
return startActivity(MyTracksSettings.class);
|
||||
}
|
||||
case R.id.menu_aggregated_stats: {
|
||||
Intent startIntent = new Intent(activity, AggregatedStatsActivity.class);
|
||||
activity.startActivity(startIntent);
|
||||
return true;
|
||||
return startActivity(AggregatedStatsActivity.class);
|
||||
}
|
||||
case R.id.menu_help: {
|
||||
Intent startIntent = new Intent(activity, WelcomeActivity.class);
|
||||
activity.startActivity(startIntent);
|
||||
return true;
|
||||
return startActivity(WelcomeActivity.class);
|
||||
}
|
||||
case MyTracksConstants.MENU_CLEAR_MAP: {
|
||||
activity.setSelectedTrackId(-1);
|
||||
@@ -95,4 +93,9 @@ class MenuManager {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean startActivity(Class<? extends Activity> activityClass) {
|
||||
activity.startActivity(new Intent(activity, activityClass));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -229,7 +228,9 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
}
|
||||
try {
|
||||
return trackRecordingService.isRecording();
|
||||
} catch (RemoteException e) {
|
||||
// TODO: We catch Exception, because after eliminating the service process
|
||||
// all exceptions it may throw are no longer wrapped in a RemoteException.
|
||||
} catch (Exception e) {
|
||||
Log.e(MyTracksConstants.TAG, "MyTracks: Remote exception.", e);
|
||||
|
||||
// Fall back to alternative check method.
|
||||
@@ -411,6 +412,11 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
return dialogManager.onCreateDialog(id, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
return dialogManager.onCreateDialog(id, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, Dialog dialog) {
|
||||
super.onPrepareDialog(id, dialog);
|
||||
@@ -914,7 +920,9 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
Log.e(MyTracksConstants.TAG, "Cannot insert waypoint marker?");
|
||||
return -4;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// TODO: We catch Exception, because after eliminating the service process
|
||||
// all exceptions it may throw are no longer wrapped in a RemoteException.
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, R.string.error_unable_to_insert_marker,
|
||||
Toast.LENGTH_LONG).show();
|
||||
Log.e(MyTracksConstants.TAG, "Cannot insert waypoint marker.", e);
|
||||
@@ -954,7 +962,9 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
Log.e(MyTracksConstants.TAG, "Cannot insert statistics marker?");
|
||||
return -4;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// TODO: We catch Exception, because after eliminating the service process
|
||||
// all exceptions it may throw are no longer wrapped in a RemoteException.
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, R.string.error_unable_to_insert_marker,
|
||||
Toast.LENGTH_LONG).show();
|
||||
Log.e(MyTracksConstants.TAG, "Cannot insert statistics marker?", e);
|
||||
@@ -1022,7 +1032,9 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
setSelectedTrackId(recordingTrackId);
|
||||
Toast.makeText(this, getString(R.string.status_now_recording),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} catch (RemoteException e) {
|
||||
// TODO: We catch Exception, because after eliminating the service process
|
||||
// all exceptions it may throw are no longer wrapped in a RemoteException.
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this,
|
||||
getString(R.string.error_unable_to_start_recording),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
@@ -1055,7 +1067,9 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
long currentTrackId = recordingTrackId;
|
||||
try {
|
||||
trackRecordingService.endCurrentTrack();
|
||||
} catch (RemoteException e) {
|
||||
// TODO: We catch Exception, because after eliminating the service process
|
||||
// all exceptions it may throw are no longer wrapped in a RemoteException.
|
||||
} catch (Exception e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to stop recording.", e);
|
||||
}
|
||||
Intent intent = new Intent(MyTracks.this, MyTracksDetails.class);
|
||||
@@ -1120,7 +1134,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
long getSelectedTrackId() {
|
||||
return selectedTrackId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds to track recording service if it is running.
|
||||
*/
|
||||
|
||||
@@ -15,12 +15,17 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.android.apps.mytracks.io.backup.BackupActivityHelper;
|
||||
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
|
||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
@@ -30,6 +35,7 @@ import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.provider.Settings;
|
||||
|
||||
/**
|
||||
* An activity that let's the user see and edit the settings.
|
||||
@@ -53,6 +59,7 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
public static final int DEFAULT_SPLIT_FREQUENCY = 0;
|
||||
|
||||
private BackupPreferencesListener backupListener;
|
||||
|
||||
private SharedPreferences preferences;
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@@ -96,6 +103,19 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
});
|
||||
updatePreferenceUnits(metricUnitsPreference.isChecked());
|
||||
|
||||
ListPreference sensorTypePreference =
|
||||
(ListPreference) findPreference(getString(R.string.sensor_type_key));
|
||||
sensorTypePreference.setOnPreferenceChangeListener(
|
||||
new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference,
|
||||
Object newValue) {
|
||||
updateSensorSettings((String) newValue);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
updateSensorSettings(sensorTypePreference.getValue());
|
||||
|
||||
// Disable TTS announcement preference if not available
|
||||
if (!apiFeatures.hasTextToSpeech()) {
|
||||
IntegerListPreference announcementFrequency =
|
||||
@@ -112,6 +132,7 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
configureBluetoothPreferences();
|
||||
Preference backupNowPreference =
|
||||
findPreference(getString(R.string.backup_to_sd_key));
|
||||
Preference restoreNowPreference =
|
||||
@@ -158,7 +179,15 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
getPreferenceManager().getSharedPreferences()
|
||||
.unregisterOnSharedPreferenceChangeListener(backupListener);
|
||||
|
||||
super.onDestroy();
|
||||
super.onPause();
|
||||
}
|
||||
private void updateSensorSettings(String sensorType) {
|
||||
boolean usesBluetooth =
|
||||
getString(R.string.zephyr_sensor_type).equals(sensorType);
|
||||
findPreference(
|
||||
getString(R.string.bluetooth_sensor_key)).setEnabled(usesBluetooth);
|
||||
findPreference(
|
||||
getString(R.string.bluetooth_pairing_key)).setEnabled(usesBluetooth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,4 +223,44 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
? R.array.split_frequency_options
|
||||
: R.array.split_frequency_options_ft);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures preference actions related to bluetooth.
|
||||
*/
|
||||
private void configureBluetoothPreferences() {
|
||||
if (BluetoothDeviceUtils.isBluetoothMethodSupported()) {
|
||||
// Populate the list of bluetooth devices
|
||||
populateBluetoothDeviceList();
|
||||
|
||||
// Make the pair devices preference go to the system preferences
|
||||
findPreference(getString(R.string.bluetooth_pairing_key))
|
||||
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
|
||||
startActivity(settingsIntent);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the list preference with all available bluetooth devices.
|
||||
*/
|
||||
private void populateBluetoothDeviceList() {
|
||||
// Build the list of entries and their values
|
||||
List<String> entries = new ArrayList<String>();
|
||||
List<String> entryValues = new ArrayList<String>();
|
||||
|
||||
// The actual devices
|
||||
BluetoothDeviceUtils.getInstance().populateDeviceLists(entries, entryValues);
|
||||
|
||||
CharSequence[] entriesArray = entries.toArray(new CharSequence[entries.size()]);
|
||||
CharSequence[] entryValuesArray = entryValues.toArray(new CharSequence[entryValues.size()]);
|
||||
ListPreference devicesPreference =
|
||||
(ListPreference) findPreference(getString(R.string.bluetooth_sensor_key));
|
||||
devicesPreference.setEntryValues(entryValuesArray);
|
||||
devicesPreference.setEntries(entriesArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import static com.google.android.apps.mytracks.MyTracksConstants.TAG;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* An activity that displays information about sensors.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SensorStateActivity extends Activity {
|
||||
|
||||
private static final SimpleDateFormat TIMESTAMP_FORMAT =
|
||||
new SimpleDateFormat("HH:mm:ss");
|
||||
private static final long REFRESH_PERIOD_MS = 250;
|
||||
|
||||
private final StatsUtilities utils;
|
||||
|
||||
/**
|
||||
* This timer periodically invokes the refresh timer task.
|
||||
*/
|
||||
private Timer timer;
|
||||
|
||||
private final Runnable stateUpdater = new Runnable() {
|
||||
public void run() {
|
||||
updateState();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A task which will update the U/I.
|
||||
*/
|
||||
private class RefreshTask extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
runOnUiThread(stateUpdater);
|
||||
}
|
||||
};
|
||||
|
||||
public SensorStateActivity() {
|
||||
utils = new StatsUtilities(this);
|
||||
Log.w(TAG, "SensorStateActivity()");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Log.w(TAG, "SensorStateActivity.onCreate");
|
||||
|
||||
setContentView(R.layout.sensor_state);
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
timer = new Timer();
|
||||
timer.schedule(new RefreshTask(), REFRESH_PERIOD_MS, REFRESH_PERIOD_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
protected void updateState() {
|
||||
MyTracks mt = MyTracks.getInstance();
|
||||
|
||||
ITrackRecordingService service =
|
||||
mt == null ? null : mt.getTrackRecordingService();
|
||||
if (service == null) {
|
||||
Log.d(MyTracksConstants.TAG, "Could not get track recording service.");
|
||||
updateSensorState(Sensor.SensorState.NONE);
|
||||
updateSensorData(null);
|
||||
return;
|
||||
}
|
||||
Sensor.SensorDataSet sds = null;
|
||||
try {
|
||||
byte[] buff = service.getSensorData();
|
||||
if (buff != null) {
|
||||
sds = Sensor.SensorDataSet.parseFrom(buff);
|
||||
updateSensorData(sds);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Could not read sensor data.", e);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Could not read sensor data.", e);
|
||||
}
|
||||
updateSensorData(sds);
|
||||
|
||||
try {
|
||||
int i = service.getSensorState();
|
||||
updateSensorState(Sensor.SensorState.valueOf(i));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Could not read sensor state.", e);
|
||||
updateSensorState(Sensor.SensorState.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSensorState(Sensor.SensorState state) {
|
||||
TextView sensorTime =
|
||||
((TextView) findViewById(R.id.sensor_state_register));
|
||||
sensorTime.setText(SensorUtils.getStateAsString(state, this));
|
||||
}
|
||||
|
||||
protected void updateSensorData(Sensor.SensorDataSet sds) {
|
||||
if (sds == null) {
|
||||
utils.setUnknown(R.id.sensor_data_time_register);
|
||||
utils.setUnknown(R.id.cadence_state_register);
|
||||
utils.setUnknown(R.id.power_state_register);
|
||||
utils.setUnknown(R.id.heart_rate_register);
|
||||
return;
|
||||
}
|
||||
|
||||
TextView sensorTime =
|
||||
((TextView) findViewById(R.id.sensor_data_time_register));
|
||||
sensorTime.setText(
|
||||
TIMESTAMP_FORMAT.format(new Date(sds.getCreationTime())));
|
||||
|
||||
if (sds.hasPower() && sds.getPower().hasValue()
|
||||
&& sds.getPower().getState() == Sensor.SensorState.SENDING) {
|
||||
utils.setText(R.id.power_state_register,
|
||||
Integer.toString(sds.getPower().getValue()));
|
||||
} else {
|
||||
utils.setText(R.id.power_state_register,
|
||||
SensorUtils.getStateAsString(
|
||||
sds.hasPower()
|
||||
? sds.getPower().getState()
|
||||
: Sensor.SensorState.NONE,
|
||||
this));
|
||||
}
|
||||
|
||||
if (sds.hasCadence() && sds.getCadence().hasValue()
|
||||
&& sds.getCadence().getState() == Sensor.SensorState.SENDING) {
|
||||
utils.setText(R.id.cadence_state_register,
|
||||
Integer.toString(sds.getCadence().getValue()));
|
||||
} else {
|
||||
utils.setText(R.id.cadence_state_register,
|
||||
SensorUtils.getStateAsString(
|
||||
sds.hasCadence()
|
||||
? sds.getCadence().getState()
|
||||
: Sensor.SensorState.NONE,
|
||||
this));
|
||||
}
|
||||
|
||||
if (sds.hasHeartRate() && sds.getHeartRate().hasValue()
|
||||
&& sds.getHeartRate().getState() == Sensor.SensorState.SENDING) {
|
||||
utils.setText(R.id.heart_rate_register,
|
||||
Integer.toString(sds.getHeartRate().getValue()));
|
||||
} else {
|
||||
utils.setText(R.id.heart_rate_register,
|
||||
SensorUtils.getStateAsString(
|
||||
sds.hasHeartRate()
|
||||
? sds.getHeartRate().getState()
|
||||
: Sensor.SensorState.NONE,
|
||||
this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* An interface for an object that can generate descriptions of waypoints.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public interface DescriptionGenerator {
|
||||
|
||||
/**
|
||||
* Generate a description of the waypoint.
|
||||
*/
|
||||
public String generateWaypointDescription(Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* Generates a description for a track (with information about the
|
||||
* statistics).
|
||||
*
|
||||
* @param track the track
|
||||
* @return a track description
|
||||
*/
|
||||
public String generateTrackDescription(Track track, Vector<Double> distances,
|
||||
Vector<Double> elevations);
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
@@ -38,7 +40,7 @@ import android.util.Log;
|
||||
public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
private static final String DATABASE_NAME = "mytracks.db";
|
||||
private static final int DATABASE_VERSION = 17;
|
||||
private static final int DATABASE_VERSION = 18;
|
||||
private static final int TRACKPOINTS = 1;
|
||||
private static final int TRACKPOINTS_ID = 2;
|
||||
private static final int TRACKS = 3;
|
||||
@@ -70,7 +72,8 @@ public class MyTracksProvider extends ContentProvider {
|
||||
+ TrackPointsColumns.ALTITUDE + " FLOAT, "
|
||||
+ TrackPointsColumns.ACCURACY + " FLOAT, "
|
||||
+ TrackPointsColumns.SPEED + " FLOAT, "
|
||||
+ TrackPointsColumns.BEARING + " FLOAT);");
|
||||
+ TrackPointsColumns.BEARING + " FLOAT, "
|
||||
+ TrackPointsColumns.SENSOR + " BLOB);");
|
||||
db.execSQL("CREATE TABLE " + TRACKS_TABLE + " ("
|
||||
+ TracksColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ TracksColumns.NAME + " STRING, "
|
||||
@@ -132,12 +135,19 @@ public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", which will destroy all old data");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKPOINTS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + WAYPOINTS_TABLE);
|
||||
onCreate(db);
|
||||
if (oldVersion < 17) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", which will destroy all old data");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKPOINTS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + WAYPOINTS_TABLE);
|
||||
onCreate(db);
|
||||
} else if (oldVersion >= 17) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", adding sensor column.");
|
||||
db.execSQL("ALTER TABLE " + TRACKPOINTS_TABLE
|
||||
+ " ADD " + TrackPointsColumns.SENSOR + " BLOB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,6 +245,7 @@ public class MyTracksProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
|
||||
@@ -346,6 +357,8 @@ public class MyTracksProvider extends ContentProvider {
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
}
|
||||
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Build query: " + qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null));
|
||||
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null,
|
||||
sortOrder);
|
||||
c.setNotificationUri(getContext().getContentResolver(), url);
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility to access data from the mytracks content provider.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public interface MyTracksProviderUtils {
|
||||
|
||||
/**
|
||||
* Authority (first part of URI) for the MyTracks content provider:
|
||||
*/
|
||||
public static final String AUTHORITY = "com.google.android.maps.mytracks";
|
||||
|
||||
/**
|
||||
* Deletes all tracks (including track points) from the provider.
|
||||
*/
|
||||
void deleteAllTracks();
|
||||
|
||||
/**
|
||||
* Deletes a track with the given track id.
|
||||
*
|
||||
* @param trackId the unique track id
|
||||
*/
|
||||
void deleteTrack(long trackId);
|
||||
|
||||
/**
|
||||
* Deletes a way point with the given way point id.
|
||||
* This will also correct the next statistics way point after the deleted one
|
||||
* to reflect the deletion.
|
||||
* The generator is needed to stitch together statistics waypoints.
|
||||
*
|
||||
* @param waypointId the unique way point id
|
||||
* @param descriptionGenerator the class to generate descriptions
|
||||
*/
|
||||
void deleteWaypoint(long waypointId,
|
||||
DescriptionGenerator descriptionGenerator);
|
||||
|
||||
/**
|
||||
* Finds the next statistics waypoint after the given waypoint.
|
||||
*
|
||||
* @param waypoint a given waypoint
|
||||
* @return the next statistics waypoint, or null if none found.
|
||||
*/
|
||||
Waypoint getNextStatisticsWaypointAfter(Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* Updates the waypoint in the provider.
|
||||
*
|
||||
* @param waypoint
|
||||
* @return true if successful
|
||||
*/
|
||||
boolean updateWaypoint(Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* Finds the last recorded location from the location provider.
|
||||
*
|
||||
* @return the last location, or null if no locations available
|
||||
*/
|
||||
Location getLastLocation();
|
||||
|
||||
/**
|
||||
* Finds the first recorded waypoint for a given track from the location
|
||||
* provider.
|
||||
* This is a special waypoint that holds the stats for current segment.
|
||||
*
|
||||
* @param trackId the id of the track the waypoint belongs to
|
||||
* @return the first waypoint, or null if no waypoints available
|
||||
*/
|
||||
Waypoint getFirstWaypoint(long trackId);
|
||||
|
||||
/**
|
||||
* Finds the given waypoint from the location provider.
|
||||
*
|
||||
* @param waypointId
|
||||
* @return the waypoint, or null if it does not exist
|
||||
*/
|
||||
Waypoint getWaypoint(long waypointId);
|
||||
|
||||
/**
|
||||
* Finds the last recorded location id from the track points provider.
|
||||
*
|
||||
* @param trackId find last location on this track
|
||||
* @return the location id, or -1 if no locations available
|
||||
*/
|
||||
long getLastLocationId(long trackId);
|
||||
|
||||
/**
|
||||
* Finds the id of the 1st waypoint for a given track.
|
||||
* The 1st waypoint is special as it contains the stats for the current
|
||||
* segment.
|
||||
*
|
||||
* @param trackId find last location on this track
|
||||
* @return the waypoint id, or -1 if no waypoints are available
|
||||
*/
|
||||
long getFirstWaypointId(long trackId);
|
||||
|
||||
/**
|
||||
* Finds the id of the 1st waypoint for a given track.
|
||||
* The 1st waypoint is special as it contains the stats for the current
|
||||
* segment.
|
||||
*
|
||||
* @param trackId find last location on this track
|
||||
* @return the waypoint id, or -1 if no waypoints are available
|
||||
*/
|
||||
long getLastWaypointId(long trackId);
|
||||
|
||||
/**
|
||||
* Finds the last recorded track from the track provider.
|
||||
*
|
||||
* @return the last track, or null if no tracks available
|
||||
*/
|
||||
Track getLastTrack();
|
||||
|
||||
/**
|
||||
* Finds the last recorded track id from the tracks provider.
|
||||
*
|
||||
* @return the track id, or -1 if no tracks available
|
||||
*/
|
||||
long getLastTrackId();
|
||||
|
||||
/**
|
||||
* Finds a location by given unique id.
|
||||
*
|
||||
* @param id the desired id
|
||||
* @return a Location object, or null if not found
|
||||
*/
|
||||
Location getLocation(long id);
|
||||
|
||||
/**
|
||||
* Creates a cursor over the locations in the track points provider which
|
||||
* iterates over a given range of unique ids.
|
||||
* Caller gets to own the returned cursor. Don't forget to close it.
|
||||
*
|
||||
* @param trackId the id of the track for which to get the points
|
||||
* @param minTrackPointId the minimum id for the track points
|
||||
* @param maxLocations maximum number of locations retrieved
|
||||
* @param descending if true the results will be returned in descending id
|
||||
* order (latest location first)
|
||||
* @return A cursor over the selected range of locations
|
||||
*/
|
||||
Cursor getLocationsCursor(long trackId, long minTrackPointId,
|
||||
int maxLocations, boolean descending);
|
||||
|
||||
/**
|
||||
* Creates a cursor over the waypoints of a track.
|
||||
* Caller gets to own the returned cursor. Don't forget to close it.
|
||||
*
|
||||
* @param trackId the id of the track for which to get the points
|
||||
* @param minWaypointId the minimum id for the track points
|
||||
* @param maxWaypoints the maximum number of waypoints to return
|
||||
* @return A cursor over the selected range of locations
|
||||
*/
|
||||
Cursor getWaypointsCursor(long trackId, long minWaypointId,
|
||||
int maxWaypoints);
|
||||
|
||||
/**
|
||||
* Finds a track by given unique track id.
|
||||
* Note that the returned track object does not have any track points
|
||||
* attached. Use {@link #getTrackPoints(Track, int)} to load the track points.
|
||||
*
|
||||
* @param id desired unique track id
|
||||
* @return a Track object, or null if not found
|
||||
*/
|
||||
Track getTrack(long id);
|
||||
|
||||
/**
|
||||
* Retrieves all tracks without track points. If no tracks exist an empty
|
||||
* list will be returned. Use {@link #getTrackPoints(Track, int)} to load
|
||||
* the track points.
|
||||
*
|
||||
* @return a list of all the recorded tracks
|
||||
*/
|
||||
List<Track> getAllTracks();
|
||||
|
||||
/**
|
||||
* Loads the track points for a given track.
|
||||
*
|
||||
* @param track assumes that startId and stopId are filled in correctly
|
||||
* @param maxPoints maximum number of points to load, or -1 for no limit
|
||||
* (oldest ones will be discarded)
|
||||
* @return the id of the last location in the track
|
||||
*/
|
||||
long getTrackPoints(Track track, int maxPoints);
|
||||
|
||||
/**
|
||||
* Creates a cursor over the tracks provider with a given selection.
|
||||
* Caller gets to own the returned cursor. Don't forget to close it.
|
||||
*
|
||||
* @param selection a given selection
|
||||
* @return a cursor of the selected tracks
|
||||
*/
|
||||
Cursor getTracksCursor(String selection);
|
||||
|
||||
/**
|
||||
* Inserts a track in the tracks provider.
|
||||
* Note: This does not insert any track points.
|
||||
* Use {@link #insertTrackPoint(Location, long)} to insert them.
|
||||
*
|
||||
* @param track the track to insert
|
||||
* @return the content provider URI for the inserted track
|
||||
*/
|
||||
Uri insertTrack(Track track);
|
||||
|
||||
/**
|
||||
* Inserts a track point in the tracks provider.
|
||||
*
|
||||
* @param location the location to insert
|
||||
* @return the content provider URI for the inserted track point
|
||||
*/
|
||||
Uri insertTrackPoint(Location location, long trackId);
|
||||
|
||||
/**
|
||||
* Inserts multiple track points in a single operation.
|
||||
*
|
||||
* @param locations an array of locations to insert
|
||||
* @param length the number of locations (from the beginning of the array)
|
||||
* to actually insert, or -1 for all of them
|
||||
* @param trackId the ID of the track to insert the points into
|
||||
* @return the number of points inserted
|
||||
*/
|
||||
int bulkInsertTrackPoints(Location[] locations, int length, long trackId);
|
||||
|
||||
/**
|
||||
* Inserts a waypoint in the provider.
|
||||
*
|
||||
* @param waypoint the waypoint to insert
|
||||
* @return the content provider URI for the inserted track
|
||||
*/
|
||||
Uri insertWaypoint(Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* Tests if a track with given id exists.
|
||||
*
|
||||
* @param id the unique id
|
||||
* @return true if track exists
|
||||
*/
|
||||
boolean trackExists(long id);
|
||||
|
||||
/**
|
||||
* Updates a track in the content provider.
|
||||
* Note: This will not update any track points.
|
||||
*
|
||||
* @param track a given track
|
||||
*/
|
||||
void updateTrack(Track track);
|
||||
|
||||
/**
|
||||
* Creates a Track object from a given cursor.
|
||||
*
|
||||
* @param cursor a cursor pointing at a db or provider with tracks
|
||||
* @return a new Track object
|
||||
*/
|
||||
Track createTrack(Cursor cursor);
|
||||
|
||||
/**
|
||||
* Creates the ContentValues for a given Track object.
|
||||
*
|
||||
* Note: If the track has an id<0 the id column will not be filled.
|
||||
*
|
||||
* @param track a given track object
|
||||
* @return a filled in ContentValues object
|
||||
*/
|
||||
ContentValues createContentValues(Track track);
|
||||
|
||||
/**
|
||||
* Creates a location object from a given cursor.
|
||||
*
|
||||
* @param cursor a cursor pointing at a db or provider with locations
|
||||
* @return a new location object
|
||||
*/
|
||||
Location createLocation(Cursor cursor);
|
||||
|
||||
/**
|
||||
* Fill a location object with values from a given cursor.
|
||||
*
|
||||
* @param cursor a cursor pointing at a db or provider with locations
|
||||
* @param location a location object to be overwritten
|
||||
*/
|
||||
void fillLocation(Cursor cursor, Location location);
|
||||
|
||||
/**
|
||||
* Creates a waypoint object from a given cursor.
|
||||
*
|
||||
* @param cursor a cursor pointing at a db or provider with waypoints.
|
||||
* @return a new waypoint object
|
||||
*/
|
||||
Waypoint createWaypoint(Cursor cursor);
|
||||
|
||||
/**
|
||||
* A factory which can produce instances of {@link MyTracksProviderUtils},
|
||||
* and can be overridden in tests (a.k.a. poor man's guice).
|
||||
*/
|
||||
public static class Factory {
|
||||
private static Factory instance = new Factory();
|
||||
|
||||
/**
|
||||
* Creates and returns an instance of {@link MyTracksProviderUtils} which
|
||||
* uses the given context to access its data.
|
||||
*/
|
||||
public static MyTracksProviderUtils get(Context context) {
|
||||
return instance.newForContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the global instance of this factory.
|
||||
*/
|
||||
public static Factory getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the global instance for this factory, to be used for testing.
|
||||
* If used, don't forget to set it back to the original value after the
|
||||
* test is run.
|
||||
*/
|
||||
public static void overrideInstance(Factory factory) {
|
||||
instance = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link MyTracksProviderUtils}.
|
||||
*/
|
||||
protected MyTracksProviderUtils newForContext(Context context) {
|
||||
return new MyTracksProviderUtilsImpl(context.getContentResolver());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,914 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class providing easy access to locations and tracks in the
|
||||
* MyTracksProvider. All static members.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
|
||||
public MyTracksProviderUtilsImpl(ContentResolver contentResolver) {
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the ContentValues for a given location object.
|
||||
*
|
||||
* @param location a given location
|
||||
* @param trackId the id of the track it belongs to
|
||||
* @return a filled in ContentValues object
|
||||
*/
|
||||
private static ContentValues createContentValues(
|
||||
Location location, long trackId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TrackPointsColumns.TRACKID, trackId);
|
||||
values.put(TrackPointsColumns.LATITUDE,
|
||||
(int) (location.getLatitude() * 1E6));
|
||||
values.put(TrackPointsColumns.LONGITUDE,
|
||||
(int) (location.getLongitude() * 1E6));
|
||||
// This is an ugly hack for Samsung phones that don't properly populate the
|
||||
// time field.
|
||||
values.put(TrackPointsColumns.TIME,
|
||||
(location.getTime() == 0)
|
||||
? System.currentTimeMillis()
|
||||
: location.getTime());
|
||||
if (location.hasAltitude()) {
|
||||
values.put(TrackPointsColumns.ALTITUDE, location.getAltitude());
|
||||
}
|
||||
if (location.hasBearing()) {
|
||||
values.put(TrackPointsColumns.BEARING, location.getBearing());
|
||||
}
|
||||
if (location.hasAccuracy()) {
|
||||
values.put(TrackPointsColumns.ACCURACY, location.getAccuracy());
|
||||
}
|
||||
if (location.hasSpeed()) {
|
||||
values.put(TrackPointsColumns.SPEED, location.getSpeed());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentValues createContentValues(Track track) {
|
||||
ContentValues values = new ContentValues();
|
||||
TripStatistics stats = track.getStatistics();
|
||||
|
||||
// Values id < 0 indicate no id is available:
|
||||
if (track.getId() >= 0) {
|
||||
values.put(TracksColumns._ID, track.getId());
|
||||
}
|
||||
values.put(TracksColumns.NAME, track.getName());
|
||||
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
||||
values.put(TracksColumns.MAPID, track.getMapId());
|
||||
values.put(TracksColumns.CATEGORY, track.getCategory());
|
||||
values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints());
|
||||
values.put(TracksColumns.STARTID, track.getStartId());
|
||||
values.put(TracksColumns.STARTTIME, stats.getStartTime());
|
||||
values.put(TracksColumns.STOPTIME, stats.getStopTime());
|
||||
values.put(TracksColumns.STOPID, track.getStopId());
|
||||
values.put(TracksColumns.TOTALDISTANCE, stats.getTotalDistance());
|
||||
values.put(TracksColumns.TOTALTIME, stats.getTotalTime());
|
||||
values.put(TracksColumns.MOVINGTIME, stats.getMovingTime());
|
||||
values.put(TracksColumns.MAXLAT, stats.getTop());
|
||||
values.put(TracksColumns.MINLAT, stats.getBottom());
|
||||
values.put(TracksColumns.MAXLON, stats.getRight());
|
||||
values.put(TracksColumns.MINLON, stats.getLeft());
|
||||
values.put(TracksColumns.AVGSPEED, stats.getAverageSpeed());
|
||||
values.put(TracksColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed());
|
||||
values.put(TracksColumns.MAXSPEED, stats.getMaxSpeed());
|
||||
values.put(TracksColumns.MINELEVATION, stats.getMinElevation());
|
||||
values.put(TracksColumns.MAXELEVATION, stats.getMaxElevation());
|
||||
values.put(TracksColumns.ELEVATIONGAIN, stats.getTotalElevationGain());
|
||||
values.put(TracksColumns.MINGRADE, stats.getMinGrade());
|
||||
values.put(TracksColumns.MAXGRADE, stats.getMaxGrade());
|
||||
return values;
|
||||
}
|
||||
|
||||
private static ContentValues createContentValues(Waypoint waypoint) {
|
||||
ContentValues values = new ContentValues();
|
||||
TripStatistics stats = waypoint.getStatistics();
|
||||
|
||||
// Values id < 0 indicate no id is available:
|
||||
if (waypoint.getId() >= 0) {
|
||||
values.put(WaypointsColumns._ID, waypoint.getId());
|
||||
}
|
||||
values.put(WaypointsColumns.NAME, waypoint.getName());
|
||||
values.put(WaypointsColumns.DESCRIPTION, waypoint.getDescription());
|
||||
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory());
|
||||
values.put(WaypointsColumns.ICON, waypoint.getIcon());
|
||||
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId());
|
||||
values.put(WaypointsColumns.TYPE, waypoint.getType());
|
||||
values.put(WaypointsColumns.LENGTH, waypoint.getLength());
|
||||
values.put(WaypointsColumns.DURATION, waypoint.getDuration());
|
||||
values.put(WaypointsColumns.STARTTIME, stats.getStartTime());
|
||||
values.put(WaypointsColumns.STARTID, waypoint.getStartId());
|
||||
values.put(WaypointsColumns.STOPID, waypoint.getStopId());
|
||||
|
||||
values.put(WaypointsColumns.TOTALDISTANCE, stats.getTotalDistance());
|
||||
values.put(WaypointsColumns.TOTALTIME, stats.getTotalTime());
|
||||
values.put(WaypointsColumns.MOVINGTIME, stats.getMovingTime());
|
||||
values.put(WaypointsColumns.AVGSPEED, stats.getAverageSpeed());
|
||||
values.put(WaypointsColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed());
|
||||
values.put(WaypointsColumns.MAXSPEED, stats.getMaxSpeed());
|
||||
values.put(WaypointsColumns.MINELEVATION, stats.getMinElevation());
|
||||
values.put(WaypointsColumns.MAXELEVATION, stats.getMaxElevation());
|
||||
values.put(WaypointsColumns.ELEVATIONGAIN, stats.getTotalElevationGain());
|
||||
values.put(WaypointsColumns.MINGRADE, stats.getMinGrade());
|
||||
values.put(WaypointsColumns.MAXGRADE, stats.getMaxGrade());
|
||||
|
||||
Location location = waypoint.getLocation();
|
||||
if (location != null) {
|
||||
values.put(WaypointsColumns.LATITUDE,
|
||||
(int) (location.getLatitude() * 1E6));
|
||||
values.put(WaypointsColumns.LONGITUDE,
|
||||
(int) (location.getLongitude() * 1E6));
|
||||
values.put(WaypointsColumns.TIME, location.getTime());
|
||||
if (location.hasAltitude()) {
|
||||
values.put(WaypointsColumns.ALTITUDE, location.getAltitude());
|
||||
}
|
||||
if (location.hasBearing()) {
|
||||
values.put(WaypointsColumns.BEARING, location.getBearing());
|
||||
}
|
||||
if (location.hasAccuracy()) {
|
||||
values.put(WaypointsColumns.ACCURACY, location.getAccuracy());
|
||||
}
|
||||
if (location.hasSpeed()) {
|
||||
values.put(WaypointsColumns.SPEED, location.getSpeed());
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location createLocation(Cursor cursor) {
|
||||
Location location = new Location("");
|
||||
fillLocation(cursor, location);
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillLocation(Cursor cursor, Location location) {
|
||||
location.reset();
|
||||
|
||||
int idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
|
||||
int idxLongitude =
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
|
||||
int idxAltitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE);
|
||||
int idxTime = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
|
||||
int idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
||||
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
||||
|
||||
if (!cursor.isNull(idxLatitude)) {
|
||||
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
|
||||
}
|
||||
if (!cursor.isNull(idxLongitude)) {
|
||||
location.setLongitude(1. * cursor.getInt(idxLongitude) / 1E6);
|
||||
}
|
||||
if (!cursor.isNull(idxAltitude)) {
|
||||
location.setAltitude(cursor.getFloat(idxAltitude));
|
||||
}
|
||||
if (!cursor.isNull(idxTime)) {
|
||||
location.setTime(cursor.getLong(idxTime));
|
||||
}
|
||||
if (!cursor.isNull(idxBearing)) {
|
||||
location.setBearing(cursor.getFloat(idxBearing));
|
||||
}
|
||||
if (!cursor.isNull(idxSpeed)) {
|
||||
location.setSpeed(cursor.getFloat(idxSpeed));
|
||||
}
|
||||
if (!cursor.isNull(idxAccuracy)) {
|
||||
location.setAccuracy(cursor.getFloat(idxAccuracy));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track createTrack(Cursor cursor) {
|
||||
int idxId = cursor.getColumnIndexOrThrow(TracksColumns._ID);
|
||||
int idxName = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
||||
int idxDescription =
|
||||
cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||
int idxMapId = cursor.getColumnIndexOrThrow(TracksColumns.MAPID);
|
||||
int idxCategory = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
||||
int idxStartId = cursor.getColumnIndexOrThrow(TracksColumns.STARTID);
|
||||
int idxStartTime = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
int idxStopTime = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
|
||||
int idxStopId = cursor.getColumnIndexOrThrow(TracksColumns.STOPID);
|
||||
int idxNumPoints = cursor.getColumnIndexOrThrow(TracksColumns.NUMPOINTS);
|
||||
int idxMaxlat = cursor.getColumnIndexOrThrow(TracksColumns.MAXLAT);
|
||||
int idxMinlat = cursor.getColumnIndexOrThrow(TracksColumns.MINLAT);
|
||||
int idxMaxlon = cursor.getColumnIndexOrThrow(TracksColumns.MAXLON);
|
||||
int idxMinlon = cursor.getColumnIndexOrThrow(TracksColumns.MINLON);
|
||||
|
||||
int idxTotalDistance =
|
||||
cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||
int idxTotalTime = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
||||
int idxMovingTime = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
|
||||
int idxMaxSpeed = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
|
||||
int idxMinElevation =
|
||||
cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION);
|
||||
int idxMaxElevation =
|
||||
cursor.getColumnIndexOrThrow(TracksColumns.MAXELEVATION);
|
||||
int idxElevationGain =
|
||||
cursor.getColumnIndexOrThrow(TracksColumns.ELEVATIONGAIN);
|
||||
int idxMinGrade = cursor.getColumnIndexOrThrow(TracksColumns.MINGRADE);
|
||||
int idxMaxGrade = cursor.getColumnIndexOrThrow(TracksColumns.MAXGRADE);
|
||||
|
||||
Track track = new Track();
|
||||
TripStatistics stats = track.getStatistics();
|
||||
if (!cursor.isNull(idxId)) {
|
||||
track.setId(cursor.getLong(idxId));
|
||||
}
|
||||
if (!cursor.isNull(idxName)) {
|
||||
track.setName(cursor.getString(idxName));
|
||||
}
|
||||
if (!cursor.isNull(idxDescription)) {
|
||||
track.setDescription(cursor.getString(idxDescription));
|
||||
}
|
||||
if (!cursor.isNull(idxMapId)) {
|
||||
track.setMapId(cursor.getString(idxMapId));
|
||||
}
|
||||
if (!cursor.isNull(idxCategory)) {
|
||||
track.setCategory(cursor.getString(idxCategory));
|
||||
}
|
||||
if (!cursor.isNull(idxStartId)) {
|
||||
track.setStartId(cursor.getInt(idxStartId));
|
||||
}
|
||||
if (!cursor.isNull(idxStartTime)) {
|
||||
stats.setStartTime(cursor.getLong(idxStartTime));
|
||||
}
|
||||
if (!cursor.isNull(idxStopTime)) {
|
||||
stats.setStopTime(cursor.getLong(idxStopTime));
|
||||
}
|
||||
if (!cursor.isNull(idxStopId)) {
|
||||
track.setStopId(cursor.getInt(idxStopId));
|
||||
}
|
||||
if (!cursor.isNull(idxNumPoints)) {
|
||||
track.setNumberOfPoints(cursor.getInt(idxNumPoints));
|
||||
}
|
||||
if (!cursor.isNull(idxTotalDistance)) {
|
||||
stats.setTotalDistance(cursor.getFloat(idxTotalDistance));
|
||||
}
|
||||
if (!cursor.isNull(idxTotalTime)) {
|
||||
stats.setTotalTime(cursor.getLong(idxTotalTime));
|
||||
}
|
||||
if (!cursor.isNull(idxMovingTime)) {
|
||||
stats.setMovingTime(cursor.getLong(idxMovingTime));
|
||||
}
|
||||
if (!cursor.isNull(idxMaxlat)
|
||||
&& !cursor.isNull(idxMinlat)
|
||||
&& !cursor.isNull(idxMaxlon)
|
||||
&& !cursor.isNull(idxMinlon)) {
|
||||
int top = cursor.getInt(idxMaxlat);
|
||||
int bottom = cursor.getInt(idxMinlat);
|
||||
int right = cursor.getInt(idxMaxlon);
|
||||
int left = cursor.getInt(idxMinlon);
|
||||
stats.setBounds(left, top, right, bottom);
|
||||
}
|
||||
if (!cursor.isNull(idxMaxSpeed)) {
|
||||
stats.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
|
||||
}
|
||||
if (!cursor.isNull(idxMinElevation)) {
|
||||
stats.setMinElevation(cursor.getFloat(idxMinElevation));
|
||||
}
|
||||
if (!cursor.isNull(idxMaxElevation)) {
|
||||
stats.setMaxElevation(cursor.getFloat(idxMaxElevation));
|
||||
}
|
||||
if (!cursor.isNull(idxElevationGain)) {
|
||||
stats.setTotalElevationGain(cursor.getFloat(idxElevationGain));
|
||||
}
|
||||
if (!cursor.isNull(idxMinGrade)) {
|
||||
stats.setMinGrade(cursor.getFloat(idxMinGrade));
|
||||
}
|
||||
if (!cursor.isNull(idxMaxGrade)) {
|
||||
stats.setMaxGrade(cursor.getFloat(idxMaxGrade));
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint createWaypoint(Cursor cursor) {
|
||||
int idxId = cursor.getColumnIndexOrThrow(WaypointsColumns._ID);
|
||||
int idxName = cursor.getColumnIndexOrThrow(WaypointsColumns.NAME);
|
||||
int idxDescription =
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns.DESCRIPTION);
|
||||
int idxCategory = cursor.getColumnIndexOrThrow(WaypointsColumns.CATEGORY);
|
||||
int idxIcon = cursor.getColumnIndexOrThrow(WaypointsColumns.ICON);
|
||||
int idxTrackId = cursor.getColumnIndexOrThrow(WaypointsColumns.TRACKID);
|
||||
int idxType = cursor.getColumnIndexOrThrow(WaypointsColumns.TYPE);
|
||||
int idxLength = cursor.getColumnIndexOrThrow(WaypointsColumns.LENGTH);
|
||||
int idxDuration = cursor.getColumnIndexOrThrow(WaypointsColumns.DURATION);
|
||||
int idxStartTime = cursor.getColumnIndexOrThrow(WaypointsColumns.STARTTIME);
|
||||
int idxStartId = cursor.getColumnIndexOrThrow(WaypointsColumns.STARTID);
|
||||
int idxStopId = cursor.getColumnIndexOrThrow(WaypointsColumns.STOPID);
|
||||
|
||||
int idxTotalDistance =
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALDISTANCE);
|
||||
int idxTotalTime = cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALTIME);
|
||||
int idxMovingTime =
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns.MOVINGTIME);
|
||||
int idxMaxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXSPEED);
|
||||
int idxMinElevation =
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns.MINELEVATION);
|
||||
int idxMaxElevation =
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns.MAXELEVATION);
|
||||
int idxElevationGain =
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns.ELEVATIONGAIN);
|
||||
int idxMinGrade = cursor.getColumnIndexOrThrow(WaypointsColumns.MINGRADE);
|
||||
int idxMaxGrade = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXGRADE);
|
||||
|
||||
int idxLatitude = cursor.getColumnIndexOrThrow(WaypointsColumns.LATITUDE);
|
||||
int idxLongitude = cursor.getColumnIndexOrThrow(WaypointsColumns.LONGITUDE);
|
||||
int idxAltitude = cursor.getColumnIndexOrThrow(WaypointsColumns.ALTITUDE);
|
||||
int idxTime = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
||||
int idxBearing = cursor.getColumnIndexOrThrow(WaypointsColumns.BEARING);
|
||||
int idxAccuracy = cursor.getColumnIndexOrThrow(WaypointsColumns.ACCURACY);
|
||||
int idxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.SPEED);
|
||||
|
||||
Waypoint waypoint = new Waypoint();
|
||||
TripStatistics stats = waypoint.getStatistics();
|
||||
|
||||
if (!cursor.isNull(idxId)) {
|
||||
waypoint.setId(cursor.getLong(idxId));
|
||||
}
|
||||
if (!cursor.isNull(idxName)) {
|
||||
waypoint.setName(cursor.getString(idxName));
|
||||
}
|
||||
if (!cursor.isNull(idxDescription)) {
|
||||
waypoint.setDescription(cursor.getString(idxDescription));
|
||||
}
|
||||
if (!cursor.isNull(idxCategory)) {
|
||||
waypoint.setCategory(cursor.getString(idxCategory));
|
||||
}
|
||||
if (!cursor.isNull(idxIcon)) {
|
||||
waypoint.setIcon(cursor.getString(idxIcon));
|
||||
}
|
||||
if (!cursor.isNull(idxTrackId)) {
|
||||
waypoint.setTrackId(cursor.getLong(idxTrackId));
|
||||
}
|
||||
if (!cursor.isNull(idxType)) {
|
||||
waypoint.setType(cursor.getInt(idxType));
|
||||
}
|
||||
if (!cursor.isNull(idxLength)) {
|
||||
waypoint.setLength(cursor.getDouble(idxLength));
|
||||
}
|
||||
if (!cursor.isNull(idxDuration)) {
|
||||
waypoint.setDuration(cursor.getLong(idxDuration));
|
||||
}
|
||||
if (!cursor.isNull(idxStartTime)) {
|
||||
stats.setStartTime(cursor.getLong(idxStartTime));
|
||||
}
|
||||
if (!cursor.isNull(idxStartId)) {
|
||||
waypoint.setStartId(cursor.getLong(idxStartId));
|
||||
}
|
||||
if (!cursor.isNull(idxStopId)) {
|
||||
waypoint.setStopId(cursor.getLong(idxStopId));
|
||||
}
|
||||
if (!cursor.isNull(idxTotalDistance)) {
|
||||
stats.setTotalDistance(cursor.getFloat(idxTotalDistance));
|
||||
}
|
||||
if (!cursor.isNull(idxTotalTime)) {
|
||||
stats.setTotalTime(cursor.getLong(idxTotalTime));
|
||||
}
|
||||
if (!cursor.isNull(idxMovingTime)) {
|
||||
stats.setMovingTime(cursor.getLong(idxMovingTime));
|
||||
}
|
||||
if (!cursor.isNull(idxMaxSpeed)) {
|
||||
stats.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
|
||||
}
|
||||
if (!cursor.isNull(idxMinElevation)) {
|
||||
stats.setMinElevation(cursor.getFloat(idxMinElevation));
|
||||
}
|
||||
if (!cursor.isNull(idxMaxElevation)) {
|
||||
stats.setMaxElevation(cursor.getFloat(idxMaxElevation));
|
||||
}
|
||||
if (!cursor.isNull(idxElevationGain)) {
|
||||
stats.setTotalElevationGain(cursor.getFloat(idxElevationGain));
|
||||
}
|
||||
if (!cursor.isNull(idxMinGrade)) {
|
||||
stats.setMinGrade(cursor.getFloat(idxMinGrade));
|
||||
}
|
||||
if (!cursor.isNull(idxMaxGrade)) {
|
||||
stats.setMaxGrade(cursor.getFloat(idxMaxGrade));
|
||||
}
|
||||
|
||||
Location location = new Location("");
|
||||
if (!cursor.isNull(idxLatitude) && !cursor.isNull(idxLongitude)) {
|
||||
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
|
||||
location.setLongitude(1. * cursor.getInt(idxLongitude) / 1E6);
|
||||
}
|
||||
if (!cursor.isNull(idxAltitude)) {
|
||||
location.setAltitude(cursor.getFloat(idxAltitude));
|
||||
}
|
||||
if (!cursor.isNull(idxTime)) {
|
||||
location.setTime(cursor.getLong(idxTime));
|
||||
}
|
||||
if (!cursor.isNull(idxBearing)) {
|
||||
location.setBearing(cursor.getFloat(idxBearing));
|
||||
}
|
||||
if (!cursor.isNull(idxSpeed)) {
|
||||
location.setSpeed(cursor.getFloat(idxSpeed));
|
||||
}
|
||||
if (!cursor.isNull(idxAccuracy)) {
|
||||
location.setAccuracy(cursor.getFloat(idxAccuracy));
|
||||
}
|
||||
waypoint.setLocation(location);
|
||||
return waypoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAllTracks() {
|
||||
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI, null, null);
|
||||
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTrack(long trackId) {
|
||||
Track track = getTrack(trackId);
|
||||
if (track != null) {
|
||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI,
|
||||
"_id>=" + track.getStartId() + " AND _id<=" + track.getStopId(),
|
||||
null);
|
||||
}
|
||||
contentResolver.delete(WaypointsColumns.CONTENT_URI,
|
||||
WaypointsColumns.TRACKID + "=" + trackId, null);
|
||||
contentResolver.delete(TracksColumns.CONTENT_URI, "_id=" + trackId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWaypoint(long waypointId,
|
||||
DescriptionGenerator descriptionGenerator) {
|
||||
final Waypoint deletedWaypoint = getWaypoint(waypointId);
|
||||
if (deletedWaypoint != null
|
||||
&& deletedWaypoint.getType() == Waypoint.TYPE_STATISTICS) {
|
||||
final Waypoint nextWaypoint =
|
||||
getNextStatisticsWaypointAfter(deletedWaypoint);
|
||||
if (nextWaypoint != null) {
|
||||
Log.d(MyTracksProvider.TAG, "Correcting marker " + nextWaypoint.getId()
|
||||
+ " after deleted marker " + deletedWaypoint.getId());
|
||||
nextWaypoint.getStatistics().merge(deletedWaypoint.getStatistics());
|
||||
nextWaypoint.setDescription(
|
||||
descriptionGenerator.generateWaypointDescription(nextWaypoint));
|
||||
if (!updateWaypoint(nextWaypoint)) {
|
||||
Log.w(MyTracksProvider.TAG, "Update of marker was unsuccessful.");
|
||||
}
|
||||
} else {
|
||||
Log.d(MyTracksProvider.TAG,
|
||||
"No statistics marker after the deleted one was found.");
|
||||
}
|
||||
}
|
||||
contentResolver.delete(
|
||||
WaypointsColumns.CONTENT_URI, "_id=" + waypointId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getNextStatisticsWaypointAfter(Waypoint waypoint) {
|
||||
final String selection = WaypointsColumns._ID + ">" + waypoint.getId()
|
||||
+ " AND " + WaypointsColumns.TRACKID + "=" + waypoint.getTrackId()
|
||||
+ " AND " + WaypointsColumns.TYPE + "=" + Waypoint.TYPE_STATISTICS;
|
||||
final String sortOrder = WaypointsColumns._ID + " LIMIT 1";
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = contentResolver.query(
|
||||
WaypointsColumns.CONTENT_URI,
|
||||
null /*projection*/,
|
||||
selection,
|
||||
null /*selectionArgs*/,
|
||||
sortOrder);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return createWaypoint(cursor);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateWaypoint(Waypoint waypoint) {
|
||||
try {
|
||||
final int rows = contentResolver.update(
|
||||
WaypointsColumns.CONTENT_URI,
|
||||
createContentValues(waypoint),
|
||||
"_id=" + waypoint.getId(),
|
||||
null /*selectionArgs*/);
|
||||
return rows == 1;
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(MyTracksProvider.TAG, "Caught unexpected exception.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a locations from the provider by the given selection.
|
||||
*
|
||||
* @param select a selection argument that identifies a unique location
|
||||
* @return the fist location matching, or null if not found
|
||||
*/
|
||||
private Location findLocationBy(String select) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = contentResolver.query(
|
||||
TrackPointsColumns.CONTENT_URI, null, select, null, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
return createLocation(cursor);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught an unexpeceted exception.", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a track from the provider by the given selection.
|
||||
*
|
||||
* @param select a selection argument that identifies a unique track
|
||||
* @return the first track matching, or null if not found
|
||||
*/
|
||||
private Track findTrackBy(String select) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = contentResolver.query(
|
||||
TracksColumns.CONTENT_URI, null, select, null, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
return createTrack(cursor);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLastLocation() {
|
||||
return findLocationBy("_id=(select max(_id) from trackpoints)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getFirstWaypoint(long trackId) {
|
||||
Cursor cursor = contentResolver.query(
|
||||
WaypointsColumns.CONTENT_URI,
|
||||
null /*projection*/,
|
||||
"trackid=" + trackId,
|
||||
null /*selectionArgs*/,
|
||||
"_id LIMIT 1");
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return createWaypoint(cursor);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getWaypoint(long waypointId) {
|
||||
Cursor cursor = contentResolver.query(
|
||||
WaypointsColumns.CONTENT_URI,
|
||||
null /*projection*/,
|
||||
"_id=" + waypointId,
|
||||
null /*selectionArgs*/,
|
||||
null /*sortOrder*/);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return createWaypoint(cursor);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastLocationId(long trackId) {
|
||||
final String[] projection = {"_id"};
|
||||
Cursor cursor = contentResolver.query(
|
||||
TrackPointsColumns.CONTENT_URI,
|
||||
projection,
|
||||
"_id=(select max(_id) from trackpoints WHERE trackid=" + trackId + ")",
|
||||
null /*selectionArgs*/,
|
||||
null /*sortOrder*/);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getLong(
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFirstWaypointId(long trackId) {
|
||||
final String[] projection = {"_id"};
|
||||
Cursor cursor = contentResolver.query(
|
||||
WaypointsColumns.CONTENT_URI,
|
||||
projection,
|
||||
"trackid=" + trackId,
|
||||
null /*selectionArgs*/,
|
||||
"_id LIMIT 1" /*sortOrder*/);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getLong(
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns._ID));
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastWaypointId(long trackId) {
|
||||
final String[] projection = {"_id"};
|
||||
Cursor cursor = contentResolver.query(
|
||||
WaypointsColumns.CONTENT_URI,
|
||||
projection,
|
||||
WaypointsColumns.TRACKID + "=" + trackId,
|
||||
null /*selectionArgs*/,
|
||||
"_id DESC LIMIT 1" /*sortOrder*/);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getLong(
|
||||
cursor.getColumnIndexOrThrow(WaypointsColumns._ID));
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track getLastTrack() {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = contentResolver.query(
|
||||
TracksColumns.CONTENT_URI, null, "_id=(select max(_id) from tracks)",
|
||||
null, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
return createTrack(cursor);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastTrackId() {
|
||||
String[] proj = { TracksColumns._ID };
|
||||
Cursor cursor = contentResolver.query(
|
||||
TracksColumns.CONTENT_URI, proj, "_id=(select max(_id) from tracks)",
|
||||
null, null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getLong(
|
||||
cursor.getColumnIndexOrThrow(TracksColumns._ID));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation(long id) {
|
||||
String selection = TrackPointsColumns._ID + "=" + id;
|
||||
return findLocationBy(selection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getLocationsCursor(long trackId, long minTrackPointId,
|
||||
int maxLocations, boolean descending) {
|
||||
String selection;
|
||||
if (minTrackPointId > 0) {
|
||||
selection = String.format("%s=%d AND %s>=%d",
|
||||
TrackPointsColumns.TRACKID, trackId,
|
||||
TrackPointsColumns._ID, minTrackPointId);
|
||||
} else {
|
||||
selection = String.format("%s=%d",
|
||||
TrackPointsColumns.TRACKID, trackId);
|
||||
}
|
||||
|
||||
String sortOrder = "_id " + (descending ? "DESC" : "ASC");
|
||||
if (maxLocations > 0) {
|
||||
sortOrder += " LIMIT " + maxLocations;
|
||||
}
|
||||
|
||||
return contentResolver.query(
|
||||
TrackPointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getWaypointsCursor(long trackId, long minWaypointId,
|
||||
int maxWaypoints) {
|
||||
String selection;
|
||||
if (minWaypointId > 0) {
|
||||
selection = String.format("%s=%d AND %s>=%d",
|
||||
WaypointsColumns.TRACKID, trackId,
|
||||
WaypointsColumns._ID, minWaypointId);
|
||||
} else {
|
||||
selection = String.format("%s=%d",
|
||||
WaypointsColumns.TRACKID, trackId);
|
||||
}
|
||||
|
||||
String sortOrder = "_id ASC";
|
||||
if (maxWaypoints > 0) {
|
||||
sortOrder += " LIMIT " + maxWaypoints;
|
||||
}
|
||||
|
||||
return contentResolver.query(
|
||||
WaypointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track getTrack(long id) {
|
||||
String select = TracksColumns._ID + "=" + id;
|
||||
return findTrackBy(select);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Track> getAllTracks() {
|
||||
Cursor cursor = getTracksCursor(null);
|
||||
if (cursor == null || !cursor.moveToFirst()) {
|
||||
return new ArrayList<Track>();
|
||||
}
|
||||
|
||||
List<Track> tracks = new ArrayList<Track>(cursor.getCount());
|
||||
do {
|
||||
tracks.add(createTrack(cursor));
|
||||
} while(cursor.moveToNext());
|
||||
|
||||
return tracks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTrackPoints(Track track, int maxPoints) {
|
||||
long lastId = -1;
|
||||
Cursor cursor = getLocationsCursor(track.getId(), -1, maxPoints, true);
|
||||
if (cursor == null) {
|
||||
Log.w(MyTracksProvider.TAG, "Cannot get a locations cursor!");
|
||||
return lastId;
|
||||
}
|
||||
try {
|
||||
final int idColumnIdx =
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
if (cursor.moveToLast()) {
|
||||
do {
|
||||
Location location = createLocation(cursor);
|
||||
if (location == null) {
|
||||
continue;
|
||||
}
|
||||
track.addLocation(location);
|
||||
lastId = cursor.getLong(idColumnIdx);
|
||||
} while (cursor.moveToPrevious());
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
return lastId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getTracksCursor(String selection) {
|
||||
Cursor cursor = contentResolver.query(
|
||||
TracksColumns.CONTENT_URI, null, selection, null, "_id");
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insertTrack(Track track) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrack");
|
||||
return contentResolver.insert(TracksColumns.CONTENT_URI,
|
||||
createContentValues(track));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insertTrackPoint(Location location, long trackId) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrackPoint");
|
||||
return contentResolver.insert(TrackPointsColumns.CONTENT_URI,
|
||||
createContentValues(location, trackId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bulkInsertTrackPoints(Location[] locations, int length, long trackId) {
|
||||
if (length == -1) { length = locations.length; }
|
||||
|
||||
ContentValues[] values = new ContentValues[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
values[i] = createContentValues(locations[i], trackId);
|
||||
}
|
||||
|
||||
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insertWaypoint(Waypoint waypoint) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
|
||||
waypoint.setId(-1);
|
||||
return contentResolver.insert(WaypointsColumns.CONTENT_URI,
|
||||
createContentValues(waypoint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trackExists(long id) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
final String[] projection = { TracksColumns._ID };
|
||||
cursor = contentResolver.query(
|
||||
TracksColumns.CONTENT_URI,
|
||||
projection,
|
||||
TracksColumns._ID + "=" + id/*selection*/,
|
||||
null/*selectionArgs*/,
|
||||
null/*sortOrder*/);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTrack(Track track) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.updateTrack");
|
||||
contentResolver.update(TracksColumns.CONTENT_URI,
|
||||
createContentValues(track), "_id=" + track.getId(), null);
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A class representing a (GPS) Track.
|
||||
*
|
||||
* TODO: hashCode and equals
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class Track implements Parcelable {
|
||||
|
||||
/**
|
||||
* Creator for a Track object
|
||||
*/
|
||||
public static class Creator implements Parcelable.Creator<Track> {
|
||||
|
||||
public Track createFromParcel(Parcel source) {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Track track = new Track();
|
||||
track.id = source.readLong();
|
||||
track.name = source.readString();
|
||||
track.description = source.readString();
|
||||
track.mapId = source.readString();
|
||||
track.category = source.readString();
|
||||
track.startId = source.readLong();
|
||||
track.stopId = source.readLong();
|
||||
track.stats = source.readParcelable(classLoader);
|
||||
|
||||
track.numberOfPoints = source.readInt();
|
||||
for (int i = 0; i < track.numberOfPoints; ++i) {
|
||||
Location loc = source.readParcelable(classLoader);
|
||||
track.locations.add(loc);
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
public Track[] newArray(int size) {
|
||||
return new Track[size];
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator CREATOR = new Creator();
|
||||
|
||||
/**
|
||||
* The track points (which may not have been loaded).
|
||||
*/
|
||||
private ArrayList<Location> locations = new ArrayList<Location>();
|
||||
|
||||
/**
|
||||
* The number of location points (present even if the points themselves were
|
||||
* not loaded).
|
||||
*/
|
||||
private int numberOfPoints = 0;
|
||||
|
||||
private long id = -1;
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String mapId = "";
|
||||
private long startId = -1;
|
||||
private long stopId = -1;
|
||||
private String category = "";
|
||||
|
||||
private TripStatistics stats = new TripStatistics();
|
||||
|
||||
public Track() {
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(id);
|
||||
dest.writeString(name);
|
||||
dest.writeString(description);
|
||||
dest.writeString(mapId);
|
||||
dest.writeString(category);
|
||||
dest.writeLong(startId);
|
||||
dest.writeLong(stopId);
|
||||
dest.writeParcelable(stats, 0);
|
||||
|
||||
dest.writeInt(numberOfPoints);
|
||||
for (int i = 0; i < numberOfPoints; ++i) {
|
||||
dest.writeParcelable(locations.get(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Getters and setters:
|
||||
//---------------------
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getStartId() {
|
||||
return startId;
|
||||
}
|
||||
|
||||
public void setStartId(long startId) {
|
||||
this.startId = startId;
|
||||
}
|
||||
|
||||
public long getStopId() {
|
||||
return stopId;
|
||||
}
|
||||
|
||||
public void setStopId(long stopId) {
|
||||
this.stopId = stopId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getMapId() {
|
||||
return mapId;
|
||||
}
|
||||
|
||||
public void setMapId(String mapId) {
|
||||
this.mapId = mapId;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public int getNumberOfPoints() {
|
||||
return numberOfPoints;
|
||||
}
|
||||
|
||||
public void setNumberOfPoints(int numberOfPoints) {
|
||||
this.numberOfPoints = numberOfPoints;
|
||||
}
|
||||
|
||||
public void addLocation(Location l) {
|
||||
locations.add(l);
|
||||
}
|
||||
|
||||
public ArrayList<Location> getLocations() {
|
||||
return locations;
|
||||
}
|
||||
|
||||
public void setLocations(ArrayList<Location> locations) {
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
public TripStatistics getStatistics() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
public void setStatistics(TripStatistics stats) {
|
||||
this.stats = stats;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import static com.google.android.apps.mytracks.content.ContentTypeIds.*;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
/**
|
||||
* Defines the URI for the track points provider and the available column names
|
||||
* and content types.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public interface TrackPointsColumns extends BaseColumns {
|
||||
public static final Uri CONTENT_URI =
|
||||
Uri.parse("content://com.google.android.maps.mytracks/trackpoints");
|
||||
public static final String CONTENT_TYPE =
|
||||
"vnd.android.cursor.dir/vnd.google.trackpoint";
|
||||
public static final String CONTENT_ITEMTYPE =
|
||||
"vnd.android.cursor.item/vnd.google.trackpoint";
|
||||
public static final String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
/* All columns */
|
||||
public static final String TRACKID = "trackid";
|
||||
public static final String LATITUDE = "latitude";
|
||||
public static final String LONGITUDE = "longitude";
|
||||
public static final String ALTITUDE = "elevation";
|
||||
public static final String BEARING = "bearing";
|
||||
public static final String TIME = "time";
|
||||
public static final String ACCURACY = "accuracy";
|
||||
public static final String SPEED = "speed";
|
||||
|
||||
/** Columns that go into the backup. */
|
||||
public static final String[] BACKUP_COLUMNS =
|
||||
{ _ID, TRACKID, LATITUDE, LONGITUDE, ALTITUDE, BEARING, TIME,
|
||||
ACCURACY, SPEED };
|
||||
|
||||
/** Types of the columns that go into the backup. */
|
||||
public static final byte[] BACKUP_COLUMN_TYPES =
|
||||
{ LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID };
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import static com.google.android.apps.mytracks.content.ContentTypeIds.*;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
/**
|
||||
* Defines the URI for the tracks provider and the available column names
|
||||
* and content types.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public interface TracksColumns extends BaseColumns {
|
||||
|
||||
public static final Uri CONTENT_URI =
|
||||
Uri.parse("content://com.google.android.maps.mytracks/tracks");
|
||||
public static final String CONTENT_TYPE =
|
||||
"vnd.android.cursor.dir/vnd.google.track";
|
||||
public static final String CONTENT_ITEMTYPE =
|
||||
"vnd.android.cursor.item/vnd.google.track";
|
||||
public static final String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
/* All columns */
|
||||
public static final String NAME = "name";
|
||||
public static final String DESCRIPTION = "description";
|
||||
public static final String CATEGORY = "category";
|
||||
public static final String STARTID = "startid";
|
||||
public static final String STOPID = "stopid";
|
||||
public static final String STARTTIME = "starttime";
|
||||
public static final String STOPTIME = "stoptime";
|
||||
public static final String NUMPOINTS = "numpoints";
|
||||
public static final String TOTALDISTANCE = "totaldistance";
|
||||
public static final String TOTALTIME = "totaltime";
|
||||
public static final String MOVINGTIME = "movingtime";
|
||||
public static final String AVGSPEED = "avgspeed";
|
||||
public static final String AVGMOVINGSPEED = "avgmovingspeed";
|
||||
public static final String MAXSPEED = "maxspeed";
|
||||
public static final String MINELEVATION = "minelevation";
|
||||
public static final String MAXELEVATION = "maxelevation";
|
||||
public static final String ELEVATIONGAIN = "elevationgain";
|
||||
public static final String MINGRADE = "mingrade";
|
||||
public static final String MAXGRADE = "maxgrade";
|
||||
public static final String MINLAT = "minlat";
|
||||
public static final String MAXLAT = "maxlat";
|
||||
public static final String MINLON = "minlon";
|
||||
public static final String MAXLON = "maxlon";
|
||||
public static final String MAPID = "mapid";
|
||||
|
||||
public static final String[] BACKUP_COLUMNS = {
|
||||
_ID, NAME, DESCRIPTION, CATEGORY, STARTID, STOPID, STARTTIME, STOPTIME,
|
||||
NUMPOINTS, TOTALDISTANCE, TOTALTIME, MOVINGTIME, AVGSPEED, AVGMOVINGSPEED,
|
||||
MAXSPEED, MINELEVATION, MAXELEVATION, ELEVATIONGAIN, MINGRADE, MAXGRADE,
|
||||
MINLAT, MAXLAT, MINLON, MAXLON };
|
||||
public static final byte[] BACKUP_COLUMN_TYPES = {
|
||||
LONG_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID,
|
||||
LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID };
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
package com.google.android.apps.mytracks.content;
|
||||
parcelable Waypoint;
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* A way point. It has a location, meta data such as name, description,
|
||||
* category, and icon, plus it can store track statistics for a "sub-track".
|
||||
*
|
||||
* TODO: hashCode and equals
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public final class Waypoint implements Parcelable {
|
||||
|
||||
/**
|
||||
* Creator for a Waypoint object
|
||||
*/
|
||||
public static class Creator implements Parcelable.Creator<Waypoint> {
|
||||
|
||||
public Waypoint createFromParcel(Parcel source) {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.id = source.readLong();
|
||||
waypoint.name = source.readString();
|
||||
waypoint.description = source.readString();
|
||||
waypoint.category = source.readString();
|
||||
waypoint.icon = source.readString();
|
||||
waypoint.trackId = source.readLong();
|
||||
waypoint.type = source.readInt();
|
||||
waypoint.startId = source.readLong();
|
||||
waypoint.stopId = source.readLong();
|
||||
waypoint.stats = source.readParcelable(classLoader);
|
||||
byte hasLocation = source.readByte();
|
||||
if (hasLocation > 0) {
|
||||
waypoint.location = source.readParcelable(classLoader);
|
||||
}
|
||||
return waypoint;
|
||||
}
|
||||
|
||||
public Waypoint[] newArray(int size) {
|
||||
return new Waypoint[size];
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator CREATOR = new Creator();
|
||||
|
||||
public static final int TYPE_WAYPOINT = 0;
|
||||
public static final int TYPE_STATISTICS = 1;
|
||||
|
||||
private long id = -1;
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String category = "";
|
||||
private String icon = "";
|
||||
private long trackId = -1;
|
||||
private int type = 0;
|
||||
|
||||
private Location location;
|
||||
|
||||
/** Start track point id */
|
||||
private long startId = -1;
|
||||
/** Stop track point id */
|
||||
private long stopId = -1;
|
||||
|
||||
private TripStatistics stats = new TripStatistics();
|
||||
|
||||
/** The length of the track, without smoothing. */
|
||||
private double length;
|
||||
|
||||
/** The total duration of the track (not from the last waypoint) */
|
||||
private long duration;
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(id);
|
||||
dest.writeString(name);
|
||||
dest.writeString(description);
|
||||
dest.writeString(category);
|
||||
dest.writeString(icon);
|
||||
dest.writeLong(trackId);
|
||||
dest.writeInt(type);
|
||||
dest.writeLong(startId);
|
||||
dest.writeLong(stopId);
|
||||
dest.writeParcelable(stats, 0);
|
||||
dest.writeByte(location == null ? (byte) 0 : (byte) 1);
|
||||
if (location != null) {
|
||||
dest.writeParcelable(location, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Getters and setters:
|
||||
//---------------------
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setTrackId(long trackId) {
|
||||
this.trackId = trackId;
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getStartId() {
|
||||
return startId;
|
||||
}
|
||||
|
||||
public void setStartId(long startId) {
|
||||
this.startId = startId;
|
||||
}
|
||||
|
||||
public long getStopId() {
|
||||
return stopId;
|
||||
}
|
||||
|
||||
public void setStopId(long stopId) {
|
||||
this.stopId = stopId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public TripStatistics getStatistics() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
public void setStatistics(TripStatistics stats) {
|
||||
this.stats = stats;
|
||||
}
|
||||
|
||||
// WARNING: These fields are used for internal state keeping. You probably
|
||||
// want to look at getStatistics instead.
|
||||
|
||||
public double getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(double length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import static com.google.android.apps.mytracks.content.ContentTypeIds.*;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
/**
|
||||
* Defines the URI for the tracks provider and the available column names
|
||||
* and content types.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public interface WaypointsColumns extends BaseColumns {
|
||||
|
||||
public static final Uri CONTENT_URI =
|
||||
Uri.parse("content://com.google.android.maps.mytracks/waypoints");
|
||||
public static final String CONTENT_TYPE =
|
||||
"vnd.android.cursor.dir/vnd.google.waypoint";
|
||||
public static final String CONTENT_ITEMTYPE =
|
||||
"vnd.android.cursor.item/vnd.google.waypoint";
|
||||
public static final String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
/* All columns */
|
||||
public static final String NAME = "name";
|
||||
public static final String DESCRIPTION = "description";
|
||||
public static final String CATEGORY = "category";
|
||||
public static final String ICON = "icon";
|
||||
public static final String TRACKID = "trackid";
|
||||
public static final String TYPE = "type";
|
||||
public static final String LENGTH = "length";
|
||||
public static final String DURATION = "duration";
|
||||
public static final String STARTTIME = "starttime";
|
||||
public static final String STARTID = "startid";
|
||||
public static final String STOPID = "stopid";
|
||||
|
||||
public static final String LATITUDE = "latitude";
|
||||
public static final String LONGITUDE = "longitude";
|
||||
public static final String ALTITUDE = "elevation";
|
||||
public static final String BEARING = "bearing";
|
||||
public static final String TIME = "time";
|
||||
public static final String ACCURACY = "accuracy";
|
||||
public static final String SPEED = "speed";
|
||||
|
||||
public static final String TOTALDISTANCE = "totaldistance";
|
||||
public static final String TOTALTIME = "totaltime";
|
||||
public static final String MOVINGTIME = "movingtime";
|
||||
public static final String AVGSPEED = "avgspeed";
|
||||
public static final String AVGMOVINGSPEED = "avgmovingspeed";
|
||||
public static final String MAXSPEED = "maxspeed";
|
||||
public static final String MINELEVATION = "minelevation";
|
||||
public static final String MAXELEVATION = "maxelevation";
|
||||
public static final String ELEVATIONGAIN = "elevationgain";
|
||||
public static final String MINGRADE = "mingrade";
|
||||
public static final String MAXGRADE = "maxgrade";
|
||||
|
||||
public static final String[] BACKUP_COLUMNS = {
|
||||
_ID, TRACKID, NAME, DESCRIPTION, CATEGORY, ICON, TYPE, LENGTH, DURATION,
|
||||
STARTTIME, STARTID, STOPID, LATITUDE, LONGITUDE, ALTITUDE, BEARING, TIME,
|
||||
ACCURACY, SPEED, TOTALDISTANCE, TOTALTIME, MOVINGTIME, AVGSPEED,
|
||||
AVGMOVINGSPEED, MAXSPEED, MINELEVATION, MAXELEVATION, ELEVATIONGAIN,
|
||||
MINGRADE, MAXGRADE };
|
||||
public static final byte[] BACKUP_COLUMN_TYPES = {
|
||||
LONG_TYPE_ID, LONG_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID,
|
||||
STRING_TYPE_ID, STRING_TYPE_ID, INT_TYPE_ID, FLOAT_TYPE_ID, LONG_TYPE_ID,
|
||||
LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, FLOAT_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID };
|
||||
}
|
||||
@@ -342,8 +342,14 @@ public class GpxImporter extends DefaultHandler {
|
||||
|
||||
// create new location and set attributes
|
||||
Location loc = new Location(LocationManager.GPS_PROVIDER);
|
||||
loc.setLatitude(Double.parseDouble(latitude));
|
||||
loc.setLongitude(Double.parseDouble(longitude));
|
||||
try {
|
||||
loc.setLatitude(Double.parseDouble(latitude));
|
||||
loc.setLongitude(Double.parseDouble(longitude));
|
||||
} catch (NumberFormatException e) {
|
||||
String msg = createErrorMessage(
|
||||
"Unable to parse lat/long: " + latitude + "/" + longitude);
|
||||
throw new SAXException(msg, e);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
@@ -472,9 +478,14 @@ public class GpxImporter extends DefaultHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void onAltitudeElementEnd() {
|
||||
private void onAltitudeElementEnd() throws SAXException {
|
||||
if (location != null) {
|
||||
location.setAltitude(Double.parseDouble(content));
|
||||
try {
|
||||
location.setAltitude(Double.parseDouble(content));
|
||||
} catch (NumberFormatException e) {
|
||||
String msg = createErrorMessage("Unable to parse altitude: " + content);
|
||||
throw new SAXException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
||||
|
||||
import android.location.Location;
|
||||
@@ -141,6 +144,37 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
||||
pw.print(location.getAltitude());
|
||||
pw.println("</AltitudeMeters>");
|
||||
|
||||
if (location instanceof MyTracksLocation) {
|
||||
SensorDataSet sensorData = ((MyTracksLocation) location).getSensorDataSet();
|
||||
if (sensorData != null) {
|
||||
if (sensorData.hasHeartRate()
|
||||
&& sensorData.getHeartRate().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getHeartRate().hasValue()) {
|
||||
pw.print(" <HeartRateBpm>");
|
||||
pw.print("<Value>");
|
||||
pw.print(sensorData.getHeartRate().getValue());
|
||||
pw.print("</Value>");
|
||||
pw.println("</HeartRateBpm>");
|
||||
}
|
||||
if (sensorData.hasPower()
|
||||
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getPower().hasValue()) {
|
||||
pw.print(" <Extensions>");
|
||||
pw.print("<TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">");
|
||||
pw.print("<Watts>");
|
||||
pw.print(sensorData.getPower().getValue());
|
||||
pw.print("</Watts>");
|
||||
pw.println("</TPX></Extensions>");
|
||||
}
|
||||
if (sensorData.hasCadence()
|
||||
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getCadence().hasValue()) {
|
||||
pw.print(" <Cadence>");
|
||||
pw.print(sensorData.getCadence().getValue());
|
||||
pw.println("</Cadence>");
|
||||
}
|
||||
}
|
||||
}
|
||||
pw.println(" </Trackpoint>");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.backup;
|
||||
|
||||
import static com.google.android.apps.mytracks.content.ContentTypeIds.*;
|
||||
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
|
||||
public class BackupColumns {
|
||||
/** Columns that go into the backup. */
|
||||
public static final String[] POINTS_BACKUP_COLUMNS =
|
||||
{ TrackPointsColumns._ID, TrackPointsColumns.TRACKID, TrackPointsColumns.LATITUDE,
|
||||
TrackPointsColumns.LONGITUDE, TrackPointsColumns.ALTITUDE, TrackPointsColumns.BEARING,
|
||||
TrackPointsColumns.TIME, TrackPointsColumns.ACCURACY, TrackPointsColumns.SPEED };
|
||||
public static final byte[] POINTS_BACKUP_COLUMN_TYPES =
|
||||
{ LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID };
|
||||
|
||||
public static final String[] TRACKS_BACKUP_COLUMNS = {
|
||||
TracksColumns._ID, TracksColumns.NAME, TracksColumns.DESCRIPTION, TracksColumns.CATEGORY,
|
||||
TracksColumns.STARTID, TracksColumns.STOPID, TracksColumns.STARTTIME, TracksColumns.STOPTIME,
|
||||
TracksColumns.NUMPOINTS, TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME,
|
||||
TracksColumns.MOVINGTIME, TracksColumns.AVGSPEED, TracksColumns.AVGMOVINGSPEED,
|
||||
TracksColumns.MAXSPEED, TracksColumns.MINELEVATION, TracksColumns.MAXELEVATION,
|
||||
TracksColumns.ELEVATIONGAIN, TracksColumns.MINGRADE, TracksColumns.MAXGRADE,
|
||||
TracksColumns.MINLAT, TracksColumns.MAXLAT, TracksColumns.MINLON, TracksColumns.MAXLON };
|
||||
public static final byte[] TRACKS_BACKUP_COLUMN_TYPES = {
|
||||
LONG_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID,
|
||||
LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID };
|
||||
|
||||
public static final String[] WAYPOINTS_BACKUP_COLUMNS = {
|
||||
WaypointsColumns._ID, WaypointsColumns.TRACKID, WaypointsColumns.NAME,
|
||||
WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY, WaypointsColumns.ICON,
|
||||
WaypointsColumns.TYPE, WaypointsColumns.LENGTH, WaypointsColumns.DURATION,
|
||||
WaypointsColumns.STARTTIME, WaypointsColumns.STARTID, WaypointsColumns.STOPID,
|
||||
WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE, WaypointsColumns.ALTITUDE,
|
||||
WaypointsColumns.BEARING, WaypointsColumns.TIME, WaypointsColumns.ACCURACY,
|
||||
WaypointsColumns.SPEED, WaypointsColumns.TOTALDISTANCE, WaypointsColumns.TOTALTIME,
|
||||
WaypointsColumns.MOVINGTIME, WaypointsColumns.AVGSPEED, WaypointsColumns.AVGMOVINGSPEED,
|
||||
WaypointsColumns.MAXSPEED, WaypointsColumns.MINELEVATION, WaypointsColumns.MAXELEVATION,
|
||||
WaypointsColumns.ELEVATIONGAIN, WaypointsColumns.MINGRADE, WaypointsColumns.MAXGRADE };
|
||||
public static final byte[] WAYPOINTS_BACKUP_COLUMN_TYPES = {
|
||||
LONG_TYPE_ID, LONG_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID,
|
||||
STRING_TYPE_ID, STRING_TYPE_ID, INT_TYPE_ID, FLOAT_TYPE_ID, LONG_TYPE_ID,
|
||||
LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, FLOAT_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID };
|
||||
}
|
||||
@@ -153,16 +153,16 @@ class ExternalFileBackup {
|
||||
// Create all the auxiliary classes that will do the writing
|
||||
PreferenceBackupHelper preferencesHelper = new PreferenceBackupHelper();
|
||||
DatabaseDumper trackDumper = new DatabaseDumper(
|
||||
TracksColumns.BACKUP_COLUMNS,
|
||||
TracksColumns.BACKUP_COLUMN_TYPES,
|
||||
BackupColumns.TRACKS_BACKUP_COLUMNS,
|
||||
BackupColumns.TRACKS_BACKUP_COLUMN_TYPES,
|
||||
false);
|
||||
DatabaseDumper waypointDumper = new DatabaseDumper(
|
||||
WaypointsColumns.BACKUP_COLUMNS,
|
||||
WaypointsColumns.BACKUP_COLUMN_TYPES,
|
||||
BackupColumns.WAYPOINTS_BACKUP_COLUMNS,
|
||||
BackupColumns.WAYPOINTS_BACKUP_COLUMN_TYPES,
|
||||
false);
|
||||
DatabaseDumper pointDumper = new DatabaseDumper(
|
||||
TrackPointsColumns.BACKUP_COLUMNS,
|
||||
TrackPointsColumns.BACKUP_COLUMN_TYPES,
|
||||
BackupColumns.POINTS_BACKUP_COLUMNS,
|
||||
BackupColumns.POINTS_BACKUP_COLUMN_TYPES,
|
||||
false);
|
||||
|
||||
// Open the target for writing
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
|
||||
/**
|
||||
* MyTracks service.
|
||||
* This service is the process that actually records and manages tracks.
|
||||
*/
|
||||
interface ITrackRecordingService {
|
||||
|
||||
/**
|
||||
* Starts recording a new track.
|
||||
*
|
||||
* @return the track ID of the new track
|
||||
*/
|
||||
long startNewTrack();
|
||||
|
||||
/**
|
||||
* Checks and returns whether we're currently recording a track.
|
||||
*/
|
||||
boolean isRecording();
|
||||
|
||||
/**
|
||||
* Returns the track ID of the track currently being recorded, or -1 if none
|
||||
* is being recorded. This ID can then be used to read track data from the
|
||||
* content source.
|
||||
*/
|
||||
long getRecordingTrackId();
|
||||
|
||||
/**
|
||||
* Inserts a waypoint marker in the track being recorded.
|
||||
*
|
||||
* @param waypoint the waypoint to insert
|
||||
* @return the unique ID of the inserted marker
|
||||
*/
|
||||
long insertWaypointMarker(in Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* Inserts a statistics marker in the track being recorded.
|
||||
*
|
||||
* @param location the location at which to insert the marker
|
||||
* @return the unique ID of the inserted marker
|
||||
*/
|
||||
long insertStatisticsMarker(in Location location);
|
||||
|
||||
/**
|
||||
* Inserts a location in the track being recorded.
|
||||
*
|
||||
* When recording, locations detected by the GPS are already automatically
|
||||
* added to the track, so this should be used only for adding special points
|
||||
* or for testing.
|
||||
*
|
||||
* @param loc the location to insert
|
||||
*/
|
||||
void recordLocation(in Location loc);
|
||||
|
||||
/**
|
||||
* Stops recording the current track.
|
||||
*/
|
||||
void endCurrentTrack();
|
||||
|
||||
/**
|
||||
* Returns whether at least one track has been or is being recorded (and was
|
||||
* not deleted).
|
||||
*/
|
||||
boolean hasRecorded();
|
||||
|
||||
/**
|
||||
* Deletes all the stored tracks.
|
||||
*/
|
||||
void deleteAllTracks();
|
||||
}
|
||||
@@ -40,7 +40,6 @@ public class PreferenceManager implements OnSharedPreferenceChangeListener {
|
||||
private final String minRecordingIntervalKey;
|
||||
private final String minRequiredAccuracyKey;
|
||||
private final String recordingTrackKey;
|
||||
private final String signalSamplingFrequencyKey;
|
||||
private final String splitFrequencyKey;
|
||||
|
||||
public PreferenceManager(TrackRecordingService service) {
|
||||
@@ -72,8 +71,6 @@ public class PreferenceManager implements OnSharedPreferenceChangeListener {
|
||||
service.getString(R.string.min_required_accuracy_key);
|
||||
recordingTrackKey =
|
||||
service.getString(R.string.recording_track_key);
|
||||
signalSamplingFrequencyKey =
|
||||
service.getString(R.string.signal_sampling_frequency_key);
|
||||
splitFrequencyKey =
|
||||
service.getString(R.string.split_frequency_key);
|
||||
|
||||
@@ -163,10 +160,6 @@ public class PreferenceManager implements OnSharedPreferenceChangeListener {
|
||||
service.getSplitManager().setSplitFrequency(
|
||||
sharedPreferences.getInt(splitFrequencyKey, 0));
|
||||
}
|
||||
if (key == null || key.equals(signalSamplingFrequencyKey)) {
|
||||
service.getSignalManager().setFrequency(
|
||||
sharedPreferences.getInt(signalSamplingFrequencyKey, -1), service);
|
||||
}
|
||||
if (key == null || key.equals(metricUnitsKey)) {
|
||||
service.getSplitManager().setMetricUnits(
|
||||
sharedPreferences.getBoolean(metricUnitsKey, true));
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.telephony.CellLocation;
|
||||
import android.telephony.NeighboringCellInfo;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class to monitor the network signal strength.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SignalStrengthTask extends PhoneStateListener
|
||||
implements PeriodicTask {
|
||||
|
||||
private final Context context;
|
||||
private TelephonyManager manager;
|
||||
private int signalStrength = -1;
|
||||
|
||||
public SignalStrengthTask(Context c) {
|
||||
context = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
manager =
|
||||
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (manager == null) {
|
||||
Log.e(MyTracksConstants.TAG, "Cannot get telephony manager.");
|
||||
} else {
|
||||
manager.listen(this, getListenEvents());
|
||||
}
|
||||
}
|
||||
|
||||
protected int getListenEvents() {
|
||||
return PhoneStateListener.LISTEN_SIGNAL_STRENGTHS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSignalStrengthChanged(int asu) {
|
||||
this.signalStrength = asu;
|
||||
Log.d(MyTracksConstants.TAG, "Signal Strength: " + signalStrength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TrackRecordingService service) {
|
||||
Log.d(MyTracksConstants.TAG, "Adding signal marker");
|
||||
Location location = service.getLastLocation();
|
||||
if (manager == null) {
|
||||
Log.d(MyTracksConstants.TAG, "Adding signal marker: marker null");
|
||||
return;
|
||||
}
|
||||
if (location == null) {
|
||||
Log.d(MyTracksConstants.TAG, "Adding signal marker: location null");
|
||||
return;
|
||||
}
|
||||
Waypoint wpt = new Waypoint();
|
||||
wpt.setName("Signal Strength");
|
||||
wpt.setType(Waypoint.TYPE_WAYPOINT);
|
||||
wpt.setTrackId(service.getRecordingTrackId());
|
||||
int networkType = manager.getNetworkType();
|
||||
wpt.setIcon(getIcon(networkType));
|
||||
wpt.setLocation(location);
|
||||
wpt.setDescription(getDescription());
|
||||
long waypointId = service.insertWaypointMarker(wpt);
|
||||
if (waypointId >= 0) {
|
||||
Log.d(MyTracksConstants.TAG, "Added signal marker");
|
||||
} else {
|
||||
Log.e(MyTracksConstants.TAG, "Cannot insert waypoint marker?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human readable description for the network type.
|
||||
*
|
||||
* @param type The integer constant for the network type
|
||||
* @return A human readable description of the network type
|
||||
*/
|
||||
protected String getTypeAsString(int type) {
|
||||
switch (type) {
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
return "EDGE";
|
||||
case TelephonyManager.NETWORK_TYPE_GPRS:
|
||||
return "GPRS";
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS:
|
||||
return "UMTS";
|
||||
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a description for the current signal strength.
|
||||
*
|
||||
* @return A human readable description of the network state
|
||||
*/
|
||||
private String getDescription() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getStrengthAsString());
|
||||
|
||||
sb.append("Network Type: ");
|
||||
sb.append(getTypeAsString(manager.getNetworkType()));
|
||||
sb.append('\n');
|
||||
|
||||
sb.append("Operator: ");
|
||||
sb.append(manager.getNetworkOperatorName());
|
||||
sb.append(" / ");
|
||||
sb.append(manager.getNetworkOperator());
|
||||
sb.append('\n');
|
||||
|
||||
sb.append("Roaming: ");
|
||||
sb.append(manager.isNetworkRoaming());
|
||||
sb.append('\n');
|
||||
|
||||
List<NeighboringCellInfo> infos = manager.getNeighboringCellInfo();
|
||||
Log.i(MyTracksConstants.TAG, "Found " + infos.size() + " cells.");
|
||||
if (infos.size() > 0) {
|
||||
sb.append("Neighbors: ");
|
||||
for (NeighboringCellInfo info : infos) {
|
||||
sb.append(info.toString());
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
CellLocation cell = manager.getCellLocation();
|
||||
if (cell != null) {
|
||||
sb.append("Cell: ");
|
||||
sb.append(cell.toString());
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the url for the waypoint icon for the current network type.
|
||||
*
|
||||
* @param type The network type
|
||||
* @return A url to a image to use as the waypoint icon
|
||||
*/
|
||||
protected String getIcon(int type) {
|
||||
switch (type) {
|
||||
case TelephonyManager.NETWORK_TYPE_GPRS:
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
return "http://maps.google.com/mapfiles/ms/micons/green.png";
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS:
|
||||
return "http://maps.google.com/mapfiles/ms/micons/blue.png";
|
||||
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
|
||||
default:
|
||||
return "http://maps.google.com/mapfiles/ms/micons/red.png";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (manager != null) {
|
||||
manager.listen(this, PhoneStateListener.LISTEN_NONE);
|
||||
manager = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getStrengthAsString() {
|
||||
return "Strength: " + signalStrength + "\n";
|
||||
}
|
||||
|
||||
protected Context getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Factory for producing a proper {@link SignalStrengthTask} according to the
|
||||
* current API level.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class SignalStrengthTaskFactory {
|
||||
private final boolean hasModernSignalStrength;
|
||||
|
||||
SignalStrengthTaskFactory(ApiFeatures apiFeatures) {
|
||||
this.hasModernSignalStrength = apiFeatures.hasModernSignalStrength();
|
||||
}
|
||||
|
||||
public PeriodicTask create(Context context) {
|
||||
if (hasModernSignalStrength) {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"TrackRecordingService using modern signal strength api.");
|
||||
return new SignalStrengthTaskModern(context);
|
||||
} else {
|
||||
Log.w(MyTracksConstants.TAG,
|
||||
"TrackRecordingService using legacy signal strength api.");
|
||||
return new SignalStrengthTask(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.SignalStrength;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A class to monitor the network signal strength.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SignalStrengthTaskModern extends SignalStrengthTask {
|
||||
|
||||
private SignalStrength signalStrength = null;
|
||||
|
||||
public SignalStrengthTaskModern(Context c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getListenEvents() {
|
||||
return PhoneStateListener.LISTEN_SIGNAL_STRENGTHS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human readable description for the network type.
|
||||
*
|
||||
* @param type The integer constant for the network type
|
||||
* @return A human readable description of the network type
|
||||
*/
|
||||
@Override
|
||||
protected String getTypeAsString(int type) {
|
||||
switch (type) {
|
||||
case TelephonyManager.NETWORK_TYPE_1xRTT:
|
||||
return "1xRTT";
|
||||
case TelephonyManager.NETWORK_TYPE_CDMA:
|
||||
return "CDMA";
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
return "EDGE";
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_0:
|
||||
return "EVDO 0";
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_A:
|
||||
return "EVDO A";
|
||||
case TelephonyManager.NETWORK_TYPE_GPRS:
|
||||
return "GPRS";
|
||||
case TelephonyManager.NETWORK_TYPE_HSDPA:
|
||||
return "HSDPA";
|
||||
case TelephonyManager.NETWORK_TYPE_HSPA:
|
||||
return "HSPA";
|
||||
case TelephonyManager.NETWORK_TYPE_HSUPA:
|
||||
return "HSUPA";
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS:
|
||||
return "UMTS";
|
||||
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the url for the waypoint icon for the current network type.
|
||||
*
|
||||
* @param type The network type
|
||||
* @return A url to a image to use as the waypoint icon
|
||||
*/
|
||||
@Override
|
||||
protected String getIcon(int type) {
|
||||
switch (type) {
|
||||
case TelephonyManager.NETWORK_TYPE_1xRTT:
|
||||
case TelephonyManager.NETWORK_TYPE_CDMA:
|
||||
case TelephonyManager.NETWORK_TYPE_GPRS:
|
||||
case TelephonyManager.NETWORK_TYPE_EDGE:
|
||||
return "http://maps.google.com/mapfiles/ms/micons/green.png";
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_0:
|
||||
case TelephonyManager.NETWORK_TYPE_EVDO_A:
|
||||
case TelephonyManager.NETWORK_TYPE_HSDPA:
|
||||
case TelephonyManager.NETWORK_TYPE_HSPA:
|
||||
case TelephonyManager.NETWORK_TYPE_HSUPA:
|
||||
case TelephonyManager.NETWORK_TYPE_UMTS:
|
||||
return "http://maps.google.com/mapfiles/ms/micons/blue.png";
|
||||
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
|
||||
default:
|
||||
return "http://maps.google.com/mapfiles/ms/micons/red.png";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrengthAsString() {
|
||||
if (signalStrength == null) {
|
||||
return "Strength: " + getContext().getString(R.string.unknown) + "\n";
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (signalStrength.isGsm()) {
|
||||
appendSignal(signalStrength.getGsmSignalStrength(),
|
||||
R.string.gsm_strength,
|
||||
sb);
|
||||
maybeAppendSignal(signalStrength.getGsmBitErrorRate(),
|
||||
R.string.error_rate,
|
||||
sb);
|
||||
} else {
|
||||
appendSignal(signalStrength.getCdmaDbm(), R.string.cdma_strength, sb);
|
||||
appendSignal(signalStrength.getCdmaEcio() / 10.0, R.string.ecio, sb);
|
||||
appendSignal(signalStrength.getEvdoDbm(), R.string.evdo_strength, sb);
|
||||
appendSignal(signalStrength.getEvdoEcio() / 10.0, R.string.ecio, sb);
|
||||
appendSignal(signalStrength.getEvdoSnr(),
|
||||
R.string.signal_to_noise_ratio,
|
||||
sb);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void maybeAppendSignal(
|
||||
int signal, int signalFormat, StringBuffer sb) {
|
||||
if (signal > 0) {
|
||||
sb.append(getContext().getString(signalFormat, signal));
|
||||
}
|
||||
}
|
||||
|
||||
private void appendSignal(int signal, int signalFormat, StringBuffer sb) {
|
||||
sb.append(getContext().getString(signalFormat, signal));
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
private void appendSignal(double signal, int signalFormat, StringBuffer sb) {
|
||||
sb.append(getContext().getString(signalFormat, signal));
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
|
||||
Log.d(MyTracksConstants.TAG, "Signal Strength Modern: " + signalStrength);
|
||||
this.signalStrength = signalStrength;
|
||||
}
|
||||
}
|
||||
+215
-89
@@ -20,11 +20,16 @@ import static com.google.android.apps.mytracks.MyTracksConstants.RESUME_TRACK_EX
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorManager;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorManagerFactory;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
@@ -69,7 +74,7 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
"http://maps.google.com/mapfiles/ms/micons/ylw-pushpin.png";
|
||||
|
||||
static final int MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS = 3;
|
||||
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
private LocationManager locationManager;
|
||||
private WakeLock wakeLock;
|
||||
@@ -109,9 +114,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
* Status announcer executer.
|
||||
*/
|
||||
private PeriodicTaskExecuter announcementExecuter;
|
||||
private TaskExecuterManager signalManager;
|
||||
private SplitManager splitManager;
|
||||
|
||||
private SensorManager sensorManager;
|
||||
|
||||
private PreferenceManager prefManager;
|
||||
|
||||
/**
|
||||
@@ -215,7 +221,14 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
|
||||
// Insert the new location:
|
||||
try {
|
||||
Uri pointUri = providerUtils.insertTrackPoint(location, trackId);
|
||||
Location locationToInsert = location;
|
||||
if (sensorManager != null && sensorManager.isEnabled()) {
|
||||
SensorDataSet sd = sensorManager.getSensorDataSet();
|
||||
if (sd != null && sensorManager.isDataValid()) {
|
||||
locationToInsert = new MyTracksLocation(location, sd);
|
||||
}
|
||||
}
|
||||
Uri pointUri = providerUtils.insertTrackPoint(locationToInsert, trackId);
|
||||
int pointId = Integer.parseInt(pointUri.getLastPathSegment());
|
||||
|
||||
// Update the current track:
|
||||
@@ -411,7 +424,6 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
statsBuilder = new TripStatisticsBuilder(stats.getStartTime());
|
||||
setUpAnnouncer();
|
||||
|
||||
signalManager.restore();
|
||||
splitManager.restore();
|
||||
length = 0;
|
||||
lastValidLocation = null;
|
||||
@@ -525,11 +537,15 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
if (lastLocation != null) {
|
||||
distanceToLast = location.distanceTo(lastLocation);
|
||||
}
|
||||
boolean hasSensorData = sensorManager != null
|
||||
&& sensorManager.isEnabled()
|
||||
&& sensorManager.getSensorDataSet() != null
|
||||
&& sensorManager.isDataValid();
|
||||
|
||||
// If the user has been stationary for two recording just record the first
|
||||
// two and ignore the rest. This code will only have an effect if the
|
||||
// maxRecordingDistance = 0
|
||||
if (distanceToLast == 0) {
|
||||
if (distanceToLast == 0 && !hasSensorData) {
|
||||
if (isMoving) {
|
||||
Log.d(MyTracksConstants.TAG, "Found two identical locations.");
|
||||
isMoving = false;
|
||||
@@ -549,7 +565,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"Not recording. More than two identical locations.");
|
||||
}
|
||||
} else if (distanceToLastRecorded > minRecordingDistance) {
|
||||
} else if (distanceToLastRecorded > minRecordingDistance
|
||||
|| hasSensorData) {
|
||||
if (lastLocation != null && !isMoving) {
|
||||
// Last location was the last stationary location. Need to go back and
|
||||
// add it.
|
||||
@@ -629,14 +646,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
|
||||
splitManager = new SplitManager(this);
|
||||
|
||||
SignalStrengthTaskFactory strengthTaskFactory =
|
||||
new SignalStrengthTaskFactory(ApiFeatures.getInstance());
|
||||
signalManager =
|
||||
new TaskExecuterManager(-1, strengthTaskFactory.create(this), this);
|
||||
|
||||
sensorManager = SensorManagerFactory.getSensorManager(this);
|
||||
prefManager = new PreferenceManager(this);
|
||||
registerLocationListener();
|
||||
/**
|
||||
|
||||
/*
|
||||
* After 5 min, check every minute that location listener still is
|
||||
* registered and spit out additional debugging info to the logs:
|
||||
*/
|
||||
@@ -710,17 +724,27 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
showNotification();
|
||||
prefManager.shutdown();
|
||||
prefManager = null;
|
||||
binder = null;
|
||||
checkLocationListener.cancel();
|
||||
checkLocationListener = null;
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
unregisterLocationListener();
|
||||
shutdownAnnouncer();
|
||||
signalManager.shutdown();
|
||||
signalManager = null;
|
||||
splitManager.shutdown();
|
||||
splitManager = null;
|
||||
if (sensorManager != null) {
|
||||
sensorManager.onDestroy();
|
||||
sensorManager = null;
|
||||
}
|
||||
|
||||
// Make sure we have no indirect references to this service.
|
||||
locationManager = null;
|
||||
notificationManager = null;
|
||||
providerUtils = null;
|
||||
binder.detachFromService();
|
||||
binder = null;
|
||||
|
||||
// This should be the last operation.
|
||||
releaseWakeLock();
|
||||
|
||||
super.onDestroy();
|
||||
@@ -841,15 +865,25 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
throw new IllegalStateException(
|
||||
"Unable to insert waypoint marker while not recording!");
|
||||
}
|
||||
|
||||
if (waypoint.getLocation() != null) {
|
||||
waypoint.setLength(length);
|
||||
waypoint.setDuration(waypoint.getLocation().getTime()
|
||||
- statsBuilder.getStatistics().getStartTime());
|
||||
Uri uri = providerUtils.insertWaypoint(waypoint);
|
||||
return Long.parseLong(uri.getLastPathSegment());
|
||||
|
||||
if (waypoint.getLocation() == null) {
|
||||
if (lastValidLocation == null) {
|
||||
Log.w(MyTracksConstants.TAG, "Cannot insert waypoint with no location");
|
||||
return -1;
|
||||
}
|
||||
|
||||
waypoint.setLocation(lastValidLocation);
|
||||
}
|
||||
return -1;
|
||||
|
||||
if (waypoint.getTrackId() < 0) {
|
||||
waypoint.setTrackId(recordingTrackId);
|
||||
}
|
||||
|
||||
waypoint.setLength(length);
|
||||
waypoint.setDuration(waypoint.getLocation().getTime()
|
||||
- statsBuilder.getStatistics().getStartTime());
|
||||
Uri uri = providerUtils.insertWaypoint(waypoint);
|
||||
return Long.parseLong(uri.getLastPathSegment());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -896,82 +930,153 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
updateCurrentWaypoint();
|
||||
return Long.parseLong(uri.getLastPathSegment());
|
||||
}
|
||||
|
||||
|
||||
private ServiceBinder binder = new ServiceBinder(this);
|
||||
|
||||
/**
|
||||
* The ITrackRecordingService is defined through IDL.
|
||||
* TODO: There is a bug in Android that leaks Binder instances. This bug is
|
||||
* especially visible if we have a non-static class, as there is no way to
|
||||
* nullify reference to the outer class (the service).
|
||||
* A workaround is to use a static class and explicitly clear service
|
||||
* and detach it from the underlying Binder. With this approach, we minimize
|
||||
* the leak to 24 bytes per each service instance.
|
||||
*
|
||||
* For more details, see the following bug:
|
||||
* http://code.google.com/p/android/issues/detail?id=6426.
|
||||
*/
|
||||
private ITrackRecordingService.Stub binder =
|
||||
new ITrackRecordingService.Stub() {
|
||||
@Override
|
||||
public boolean isRecording() {
|
||||
return TrackRecordingService.this.isRecording();
|
||||
}
|
||||
private static class ServiceBinder extends ITrackRecordingService.Stub {
|
||||
private TrackRecordingService service;
|
||||
|
||||
public ServiceBinder(TrackRecordingService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the reference to the outer class to minimize the leak.
|
||||
*/
|
||||
public void detachFromService() {
|
||||
this.service = null;
|
||||
attachInterface(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecording() {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
return service.isRecording();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRecordingTrackId() {
|
||||
return recordingTrackId;
|
||||
}
|
||||
@Override
|
||||
public long getRecordingTrackId() {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
return service.recordingTrackId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRecorded() {
|
||||
return providerUtils.getLastTrackId() >= 0;
|
||||
}
|
||||
@Override
|
||||
public boolean hasRecorded() {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
return service.providerUtils.getLastTrackId() >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startNewTrack() {
|
||||
return TrackRecordingService.this.startNewTrack();
|
||||
}
|
||||
@Override
|
||||
public long startNewTrack() {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
return service.startNewTrack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given waypoint marker. Users can insert waypoint markers
|
||||
* to tag locations with a name, description, category etc.
|
||||
*
|
||||
* @param waypoint a waypoint
|
||||
* @return the unique id of the inserted marker
|
||||
*/
|
||||
@Override
|
||||
public long insertWaypointMarker(Waypoint waypoint) {
|
||||
return TrackRecordingService.this.insertWaypointMarker(waypoint);
|
||||
}
|
||||
/**
|
||||
* Insert the given waypoint marker. Users can insert waypoint markers
|
||||
* to tag locations with a name, description, category etc.
|
||||
*
|
||||
* @param waypoint a waypoint
|
||||
* @return the unique id of the inserted marker
|
||||
*/
|
||||
@Override
|
||||
public long insertWaypointMarker(Waypoint waypoint) {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
return service.insertWaypointMarker(waypoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a statistics marker. A statistics marker holds the stats for
|
||||
* the last segment up to this marker.
|
||||
*
|
||||
* @param location the location where to insert
|
||||
* @return the unique id of the inserted marker
|
||||
*/
|
||||
@Override
|
||||
public long insertStatisticsMarker(Location location) {
|
||||
return TrackRecordingService.this.insertStatisticsMarker(location);
|
||||
}
|
||||
/**
|
||||
* Insert a statistics marker. A statistics marker holds the stats for
|
||||
* the last segment up to this marker.
|
||||
*
|
||||
* @param location the location where to insert
|
||||
* @return the unique id of the inserted marker
|
||||
*/
|
||||
@Override
|
||||
public long insertStatisticsMarker(Location location) {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
return service.insertStatisticsMarker(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endCurrentTrack() {
|
||||
TrackRecordingService.this.endCurrentTrack();
|
||||
}
|
||||
@Override
|
||||
public void endCurrentTrack() {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAllTracks() {
|
||||
if (isRecording()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot delete all tracks while recording!");
|
||||
}
|
||||
providerUtils.deleteAllTracks();
|
||||
}
|
||||
@Override
|
||||
public void deleteAllTracks() {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
if (isRecording()) {
|
||||
throw new IllegalStateException("Cannot delete all tracks while recording!");
|
||||
}
|
||||
service.providerUtils.deleteAllTracks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordLocation(Location loc) {
|
||||
onLocationChanged(loc);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void recordLocation(Location loc) {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("The service has been already detached!");
|
||||
}
|
||||
service.onLocationChanged(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSensorData() {
|
||||
if (service.sensorManager == null) {
|
||||
Log.d(MyTracksConstants.TAG, "No sensor manager for data.");
|
||||
return null;
|
||||
}
|
||||
if (service.sensorManager.getSensorDataSet() == null) {
|
||||
Log.d(MyTracksConstants.TAG, "Sensor data set is null.");
|
||||
return null;
|
||||
}
|
||||
return service.sensorManager.getSensorDataSet().toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSensorState() {
|
||||
if (service.sensorManager == null) {
|
||||
Log.d(MyTracksConstants.TAG, "No sensor manager for data.");
|
||||
return Sensor.SensorState.NONE.getNumber();
|
||||
}
|
||||
return service.sensorManager.getSensorState().getNumber();
|
||||
}
|
||||
}
|
||||
|
||||
public long startNewTrack() {
|
||||
Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack");
|
||||
if (recordingTrackId != -1 || isRecording) {
|
||||
throw new IllegalStateException("A track is already in progress!");
|
||||
}
|
||||
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
acquireWakeLock();
|
||||
|
||||
@@ -996,14 +1101,20 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
showNotification();
|
||||
registerLocationListener();
|
||||
splitManager.restore();
|
||||
signalManager.restore();
|
||||
if (sensorManager != null) {
|
||||
sensorManager.onStartTrack();
|
||||
}
|
||||
|
||||
// Reset the number of auto-resume retries.
|
||||
setAutoResumeTrackRetries(
|
||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0), 0);
|
||||
// Persist the current recording track.
|
||||
prefManager.setRecordingTrack(recordingTrackId);
|
||||
|
||||
|
||||
// Notify the world that we're now recording.
|
||||
sendTrackBroadcast(
|
||||
R.string.track_started_broadcast_action, recordingTrackId);
|
||||
|
||||
return recordingTrackId;
|
||||
}
|
||||
|
||||
@@ -1033,8 +1144,27 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
"_id=" + recordingTrack.getId(), null);
|
||||
}
|
||||
showNotification();
|
||||
long recordedTrackId = recordingTrackId;
|
||||
prefManager.setRecordingTrack(recordingTrackId = -1);
|
||||
|
||||
if (sensorManager != null) {
|
||||
sensorManager.shutdown();
|
||||
}
|
||||
|
||||
releaseWakeLock();
|
||||
|
||||
// Notify the world that we're no longer recording.
|
||||
sendTrackBroadcast(
|
||||
R.string.track_stopped_broadcast_action, recordedTrackId);
|
||||
}
|
||||
|
||||
private void sendTrackBroadcast(int actionResId, long trackId) {
|
||||
Intent broadcastIntent =
|
||||
new Intent()
|
||||
.setAction(getString(actionResId))
|
||||
.putExtra(getString(R.string.track_id_broadcast_extra), trackId);
|
||||
sendBroadcast(broadcastIntent,
|
||||
getString(R.string.broadcast_notifications_permission));
|
||||
}
|
||||
|
||||
public TripStatistics getTripStatistics() {
|
||||
@@ -1110,8 +1240,4 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
public SplitManager getSplitManager() {
|
||||
return splitManager;
|
||||
}
|
||||
|
||||
public TaskExecuterManager getSignalManager() {
|
||||
return signalManager;
|
||||
}
|
||||
}
|
||||
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class does all the work for setting up and managing Bluetooth
|
||||
* connections with other devices. It has a thread that listens for incoming
|
||||
* connections, a thread for connecting with a device, and a thread for
|
||||
* performing data transmissions when connected.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class BluetoothConnectionManager {
|
||||
// Unique UUID for this application
|
||||
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||
|
||||
private MessageParser parser;
|
||||
|
||||
// Member fields
|
||||
private final BluetoothAdapter adapter;
|
||||
private final Handler handler;
|
||||
private ConnectThread connectThread;
|
||||
private ConnectedThread connectedThread;
|
||||
|
||||
private Sensor.SensorState state;
|
||||
|
||||
// Message types sent from the BluetoothSenorService Handler
|
||||
public static final int MESSAGE_STATE_CHANGE = 1;
|
||||
public static final int MESSAGE_READ = 2;
|
||||
public static final int MESSAGE_WRITE = 3;
|
||||
public static final int MESSAGE_DEVICE_NAME = 4;
|
||||
|
||||
// Key names received from the BluetoothSenorService Handler
|
||||
public static final String DEVICE_NAME = "device_name";
|
||||
|
||||
/**
|
||||
* Constructor. Prepares a new BluetoothSensor session.
|
||||
*
|
||||
* @param context The UI Activity Context
|
||||
* @param handler A Handler to send messages back to the UI Activity
|
||||
*/
|
||||
public BluetoothConnectionManager(Handler handler, MessageParser parser) {
|
||||
this.adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
this.state = Sensor.SensorState.NONE;
|
||||
this.handler = handler;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current state of the sensor connection
|
||||
*
|
||||
* @param state An integer defining the current connection state
|
||||
*/
|
||||
private synchronized void setState(Sensor.SensorState state) {
|
||||
// TODO pretty print this.
|
||||
Log.d(MyTracksConstants.TAG, "setState(" + state + ")");
|
||||
this.state = state;
|
||||
|
||||
// Give the new state to the Handler so the UI Activity can update
|
||||
handler.obtainMessage(MESSAGE_STATE_CHANGE, state.getNumber(), -1).sendToTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current connection state.
|
||||
*/
|
||||
public synchronized Sensor.SensorState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the sensor service. Specifically start AcceptThread to begin a session
|
||||
* in listening (server) mode. Called by the Activity onResume()
|
||||
*/
|
||||
public synchronized void start() {
|
||||
Log.d(MyTracksConstants.TAG, "BluetoothConnectionManager.start()");
|
||||
|
||||
// Cancel any thread attempting to make a connection
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
|
||||
// Cancel any thread currently running a connection
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
|
||||
setState(Sensor.SensorState.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the ConnectThread to initiate a connection to a remote device.
|
||||
*
|
||||
* @param device The BluetoothDevice to connect
|
||||
*/
|
||||
public synchronized void connect(BluetoothDevice device) {
|
||||
Log.d(MyTracksConstants.TAG, "connect to: " + device);
|
||||
|
||||
// Cancel any thread attempting to make a connection
|
||||
if (state == Sensor.SensorState.CONNECTING) {
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel any thread currently running a connection
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
|
||||
// Start the thread to connect with the given device
|
||||
connectThread = new ConnectThread(device);
|
||||
connectThread.start();
|
||||
setState(Sensor.SensorState.CONNECTING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the ConnectedThread to begin managing a Bluetooth connection
|
||||
*
|
||||
* @param socket The BluetoothSocket on which the connection was made
|
||||
* @param device The BluetoothDevice that has been connected
|
||||
*/
|
||||
public synchronized void connected(BluetoothSocket socket,
|
||||
BluetoothDevice device) {
|
||||
Log.d(MyTracksConstants.TAG, "connected");
|
||||
|
||||
// Cancel the thread that completed the connection
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
|
||||
// Cancel any thread currently running a connection
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
|
||||
// Start the thread to manage the connection and perform transmissions
|
||||
connectedThread = new ConnectedThread(socket);
|
||||
connectedThread.start();
|
||||
|
||||
// Send the name of the connected device back to the UI Activity
|
||||
Message msg = handler.obtainMessage(MESSAGE_DEVICE_NAME);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(DEVICE_NAME, device.getName());
|
||||
msg.setData(bundle);
|
||||
handler.sendMessage(msg);
|
||||
|
||||
setState(Sensor.SensorState.CONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all threads
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
Log.d(MyTracksConstants.TAG, "stop()");
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
setState(Sensor.SensorState.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the ConnectedThread in an unsynchronized manner
|
||||
*
|
||||
* @param out The bytes to write
|
||||
* @see ConnectedThread#write(byte[])
|
||||
*/
|
||||
public void write(byte[] out) {
|
||||
// Create temporary object
|
||||
ConnectedThread r;
|
||||
// Synchronize a copy of the ConnectedThread
|
||||
synchronized (this) {
|
||||
if (state != Sensor.SensorState.CONNECTED) {
|
||||
return;
|
||||
}
|
||||
r = connectedThread;
|
||||
}
|
||||
// Perform the write unsynchronized
|
||||
r.write(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the connection attempt failed and notify the UI Activity.
|
||||
*/
|
||||
private void connectionFailed() {
|
||||
setState(Sensor.SensorState.DISCONNECTED);
|
||||
Log.i(MyTracksConstants.TAG, "Bluetooth connection failed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the connection was lost and notify the UI Activity.
|
||||
*/
|
||||
private void connectionLost() {
|
||||
setState(Sensor.SensorState.DISCONNECTED);
|
||||
Log.i(MyTracksConstants.TAG, "Bluetooth connection lost.");
|
||||
}
|
||||
|
||||
/**
|
||||
* This thread runs while attempting to make an outgoing connection with a
|
||||
* device. It runs straight through; the connection either succeeds or fails.
|
||||
*/
|
||||
private class ConnectThread extends Thread {
|
||||
private final BluetoothSocket socket;
|
||||
private final BluetoothDevice device;
|
||||
|
||||
public ConnectThread(BluetoothDevice device) {
|
||||
setName("ConnectThread-" + device.getName());
|
||||
this.device = device;
|
||||
BluetoothSocket tmp = null;
|
||||
|
||||
// Get a BluetoothSocket for a connection with the
|
||||
// given BluetoothDevice
|
||||
try {
|
||||
tmp = getSocket();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "create() failed", e);
|
||||
}
|
||||
socket = tmp;
|
||||
}
|
||||
|
||||
private BluetoothSocket getSocket() throws IOException {
|
||||
try {
|
||||
Class<? extends BluetoothDevice> c = device.getClass();
|
||||
Method insecure = c.getMethod("createInsecureRfcommSocket",
|
||||
Integer.class);
|
||||
insecure.setAccessible(true);
|
||||
return (BluetoothSocket) insecure.invoke(device, 1);
|
||||
} catch (SecurityException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
}
|
||||
return device.createRfcommSocketToServiceRecord(SPP_UUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(MyTracksConstants.TAG, "BEGIN mConnectThread");
|
||||
|
||||
// Always cancel discovery because it will slow down a connection
|
||||
adapter.cancelDiscovery();
|
||||
|
||||
// Make a connection to the BluetoothSocket
|
||||
try {
|
||||
// This is a blocking call and will only return on a
|
||||
// successful connection or an exception
|
||||
socket.connect();
|
||||
} catch (IOException e) {
|
||||
connectionFailed();
|
||||
// Close the socket
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e2) {
|
||||
Log.e(MyTracksConstants.TAG,
|
||||
"unable to close() socket during connection failure", e2);
|
||||
}
|
||||
// Start the service over to restart listening mode
|
||||
BluetoothConnectionManager.this.start();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the ConnectThread because we're done
|
||||
synchronized (BluetoothConnectionManager.this) {
|
||||
connectThread = null;
|
||||
}
|
||||
|
||||
// Start the connected thread
|
||||
connected(socket, device);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "close() of connect socket failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This thread runs during a connection with a remote device. It handles all
|
||||
* incoming and outgoing transmissions.
|
||||
*/
|
||||
private class ConnectedThread extends Thread {
|
||||
private final BluetoothSocket btSocket;
|
||||
private final InputStream mmInStream;
|
||||
private final OutputStream mmOutStream;
|
||||
|
||||
public ConnectedThread(BluetoothSocket socket) {
|
||||
Log.d(MyTracksConstants.TAG, "create ConnectedThread");
|
||||
btSocket = socket;
|
||||
InputStream tmpIn = null;
|
||||
OutputStream tmpOut = null;
|
||||
|
||||
// Get the BluetoothSocket input and output streams
|
||||
try {
|
||||
tmpIn = socket.getInputStream();
|
||||
tmpOut = socket.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "temp sockets not created", e);
|
||||
}
|
||||
|
||||
mmInStream = tmpIn;
|
||||
mmOutStream = tmpOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(MyTracksConstants.TAG, "BEGIN mConnectedThread");
|
||||
byte[] buffer = new byte[parser.getFrameSize()];
|
||||
int bytes;
|
||||
int offset = 0;
|
||||
|
||||
// Keep listening to the InputStream while connected
|
||||
while (true) {
|
||||
try {
|
||||
// Read from the InputStream
|
||||
bytes = mmInStream.read(buffer, offset, parser.getFrameSize() - offset);
|
||||
|
||||
if (bytes < 0) {
|
||||
throw new IOException("EOF reached");
|
||||
}
|
||||
|
||||
offset += bytes;
|
||||
|
||||
if (offset != parser.getFrameSize()) {
|
||||
// partial frame received, call read() again to receive the rest
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if its a valid frame
|
||||
if (!parser.isValid(buffer)) {
|
||||
int index = parser.findNextAlignment(buffer);
|
||||
if (index > 0) {
|
||||
// re-align
|
||||
offset = parser.getFrameSize() - index;
|
||||
System.arraycopy(buffer, index, buffer, 0, offset);
|
||||
Log.w(MyTracksConstants.TAG, "Misaligned data, found new message at " +
|
||||
index + " recovering...");
|
||||
continue;
|
||||
}
|
||||
Log.w(MyTracksConstants.TAG, "Could not find valid data, dropping data");
|
||||
offset = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
|
||||
// Send the obtained bytes to the UI Activity
|
||||
handler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
|
||||
.sendToTarget();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "disconnected", e);
|
||||
connectionLost();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the connected OutStream.
|
||||
*
|
||||
* @param buffer The bytes to write
|
||||
*/
|
||||
public void write(byte[] buffer) {
|
||||
try {
|
||||
mmOutStream.write(buffer);
|
||||
|
||||
// Share the sent message back to the UI Activity
|
||||
handler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception during write", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
try {
|
||||
btSocket.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "close() of connect socket failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* Manage the connection to a bluetooth sensor.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class BluetoothSensorManager extends SensorManager {
|
||||
|
||||
// Local Bluetooth adapter
|
||||
private BluetoothAdapter bluetoothAdapter = null;
|
||||
|
||||
// Member object for the sensor threads and connections.
|
||||
private BluetoothConnectionManager connectionManager = null;
|
||||
|
||||
// Name of the connected device
|
||||
private String connectedDeviceName = null;
|
||||
|
||||
private Context context = null;
|
||||
|
||||
private Sensor.SensorDataSet sensorDataSet = null;
|
||||
|
||||
private MessageParser parser;
|
||||
|
||||
public BluetoothSensorManager(
|
||||
Context context, MessageParser parser) {
|
||||
this.context = context;
|
||||
this.parser = parser;
|
||||
// Get local Bluetooth adapter
|
||||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
// If BT is not available or not enabled quit.
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupSensor();
|
||||
}
|
||||
|
||||
private void setupSensor() {
|
||||
Log.d(MyTracksConstants.TAG, "setupSensor()");
|
||||
|
||||
// Initialize the BluetoothSensorAdapter to perform bluetooth connections.
|
||||
connectionManager = new BluetoothConnectionManager(messageHandler, parser);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
||||
}
|
||||
|
||||
public void setupChannel() {
|
||||
if (!isEnabled() || connectionManager == null) {
|
||||
Log.w(MyTracksConstants.TAG, "Disabled manager onStartTrack");
|
||||
return;
|
||||
}
|
||||
SharedPreferences prefs =
|
||||
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||
String address =
|
||||
prefs.getString(context.getString(R.string.bluetooth_sensor_key), null);
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
Log.w(MyTracksConstants.TAG, "Connecting to bluetooth sensor: " + address);
|
||||
// Get the BluetoothDevice object
|
||||
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
|
||||
// Attempt to connect to the device
|
||||
connectionManager.connect(device);
|
||||
|
||||
// Performing this check in onResume() covers the case in which BT was
|
||||
// not enabled during onStart(), so we were paused to enable it...
|
||||
// onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
|
||||
if (connectionManager != null) {
|
||||
// Only if the state is STATE_NONE, do we know that we haven't started
|
||||
// already
|
||||
if (connectionManager.getState() == Sensor.SensorState.NONE) {
|
||||
// Start the Bluetooth sensor services
|
||||
Log.w(MyTracksConstants.TAG, "Disabled manager onStartTrack");
|
||||
connectionManager.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
// Stop the Bluetooth sensor services
|
||||
if (connectionManager != null) {
|
||||
connectionManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public Sensor.SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
public Sensor.SensorState getSensorState() {
|
||||
return (connectionManager == null)
|
||||
? Sensor.SensorState.NONE
|
||||
: connectionManager.getState();
|
||||
}
|
||||
|
||||
// The Handler that gets information back from the BluetoothSensorService
|
||||
private final Handler messageHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case BluetoothConnectionManager.MESSAGE_STATE_CHANGE:
|
||||
// TODO should we update the SensorManager state var?
|
||||
Log.i(MyTracksConstants.TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_WRITE:
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_READ:
|
||||
byte[] readBuf = null;
|
||||
try {
|
||||
readBuf = (byte[]) msg.obj;
|
||||
sensorDataSet = parser.parseBuffer(readBuf);
|
||||
Log.d(MyTracksConstants.TAG, "MESSAGE_READ: " + sensorDataSet.toString());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
sensorDataSet = null;
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Got bad sensor data: " + new String(readBuf, 0, readBuf.length),
|
||||
iae);
|
||||
} catch (RuntimeException re) {
|
||||
sensorDataSet = null;
|
||||
Log.i(MyTracksConstants.TAG, "Unexpected exception on read.", re);
|
||||
}
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
|
||||
// save the connected device's name
|
||||
connectedDeviceName =
|
||||
msg.getData().getString(BluetoothConnectionManager.DEVICE_NAME);
|
||||
Toast.makeText(context.getApplicationContext(),
|
||||
"Connected to " + connectedDeviceName, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
/**
|
||||
* An interface for parsing a byte array to a SensorData object.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public interface MessageParser {
|
||||
|
||||
public int getFrameSize();
|
||||
|
||||
public Sensor.SensorDataSet parseBuffer(byte[] readBuff);
|
||||
|
||||
public boolean isValid(byte[] buffer);
|
||||
|
||||
public int findNextAlignment(byte[] buffer);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
/**
|
||||
* Manage the connection to a sensor.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public abstract class SensorManager {
|
||||
|
||||
/**
|
||||
* The maximum age where the data is considered valid.
|
||||
*/
|
||||
public static final long MAX_AGE = 5000;
|
||||
|
||||
/**
|
||||
* Time to wait after a time out to retry.
|
||||
*/
|
||||
public static final int RETRY_PERIOD = 30000;
|
||||
|
||||
private Sensor.SensorState sensorState = Sensor.SensorState.NONE;
|
||||
|
||||
private long sensorStateTimestamp = 0;
|
||||
|
||||
/**
|
||||
* A task to run periodically to check to see if connection was lost.
|
||||
*/
|
||||
private TimerTask checkSensorManager = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"SensorManager state: " + getSensorState());
|
||||
switch (getSensorState()) {
|
||||
case CONNECTING:
|
||||
long age = System.currentTimeMillis() - getSensorStateTimestamp();
|
||||
if (age > 2 * RETRY_PERIOD) {
|
||||
Log.i(MyTracksConstants.TAG, "Retrying connecting SensorManager.");
|
||||
setupChannel();
|
||||
}
|
||||
break;
|
||||
case DISCONNECTED:
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Re-registering disconnected SensoManager.");
|
||||
setupChannel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This timer invokes periodically the checkLocationListener timer task.
|
||||
*/
|
||||
private final Timer timer = new Timer();
|
||||
|
||||
/**
|
||||
* Is the sensor that this manages enabled.
|
||||
* @return true if the sensor is enabled
|
||||
*/
|
||||
public abstract boolean isEnabled();
|
||||
|
||||
/**
|
||||
* This is called when my tracks starts recording a new track.
|
||||
* This is the place to open connections to the sensor.
|
||||
*/
|
||||
public void onStartTrack() {
|
||||
setupChannel();
|
||||
timer.schedule(checkSensorManager, RETRY_PERIOD, RETRY_PERIOD);
|
||||
}
|
||||
|
||||
public abstract void setupChannel();
|
||||
|
||||
public void shutdown() {
|
||||
timer.cancel();
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when my tracks stops recording.
|
||||
* This is the place to shutdown any open connections.
|
||||
*/
|
||||
public abstract void onDestroy();
|
||||
|
||||
/**
|
||||
* Return the last sensor reading.
|
||||
* @return The last reading from the sensor.
|
||||
*/
|
||||
public abstract Sensor.SensorDataSet getSensorDataSet();
|
||||
|
||||
public void setSensorState(Sensor.SensorState sensorState) {
|
||||
this.sensorState = sensorState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current sensor state.
|
||||
* @return The current sensor state.
|
||||
*/
|
||||
public Sensor.SensorState getSensorState() {
|
||||
return sensorState;
|
||||
}
|
||||
|
||||
public long getSensorStateTimestamp() {
|
||||
return sensorStateTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the data is recent enough to be considered valid.
|
||||
*/
|
||||
public boolean isDataValid() {
|
||||
return (System.currentTimeMillis() - getSensorDataSet().getCreationTime()) < MAX_AGE;
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A factory of SensorManagers.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SensorManagerFactory {
|
||||
|
||||
private SensorManagerFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new sensor manager.
|
||||
* @param context Context to fetch system preferences.
|
||||
* @return The sensor manager that corresponds to the sensor type setting.
|
||||
*/
|
||||
public static SensorManager getSensorManager(Context context) {
|
||||
SharedPreferences prefs =
|
||||
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||
if (prefs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String sensor = prefs.getString(context.getString(R.string.sensor_type_key),
|
||||
null);
|
||||
Log.i(MyTracksConstants.TAG, "Creating sensor of type: " + sensor);
|
||||
|
||||
if (sensor == null) {
|
||||
return null;
|
||||
} else if (sensor.equals(context.getString(R.string.zephyr_sensor_type))) {
|
||||
return new ZephyrSensorManager(context);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* A collection of methods for message parsers.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SensorUtils {
|
||||
|
||||
private SensorUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract one unsigned short from a big endian byte array.
|
||||
* @param buffer the buffer to extract the short from
|
||||
* @param index the first byte to be interpreted as part of the short
|
||||
* @return The unsigned short at the given index in the buffer
|
||||
*/
|
||||
public static int unsignedShortToInt(byte[] buffer, int index) {
|
||||
int r = (buffer[index] & 0xFF) << 8;
|
||||
r |= buffer[index + 1] & 0xFF;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static String getStateAsString(Sensor.SensorState state, Context c) {
|
||||
switch (state) {
|
||||
case NONE:
|
||||
return c.getString(R.string.none);
|
||||
case CONNECTING:
|
||||
return c.getString(R.string.connecting);
|
||||
case CONNECTED:
|
||||
return c.getString(R.string.connected);
|
||||
case DISCONNECTED:
|
||||
return c.getString(R.string.disconnected);
|
||||
case SENDING:
|
||||
return c.getString(R.string.sending);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
/**
|
||||
* An implementation of a SensorData parser for Zephyr HRM.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ZephyrMessageParser implements MessageParser {
|
||||
|
||||
@Override
|
||||
public Sensor.SensorDataSet parseBuffer(byte[] buffer) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
sb.append(String.format("%02X", buffer[i]));
|
||||
}
|
||||
Log.w(MyTracksConstants.TAG, "Got zephyr data: " + sb);
|
||||
// The provided units are 1/16 strides per minute.
|
||||
int cadence = SensorUtils.unsignedShortToInt(buffer, 56);
|
||||
// Heart Rate
|
||||
Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder()
|
||||
.setValue(buffer[12] & 0xFF)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
// Cadence
|
||||
//.setCadence(cadence / 16)
|
||||
//.build();
|
||||
Sensor.SensorDataSet sds =
|
||||
Sensor.SensorDataSet.newBuilder()
|
||||
.setCreationTime(System.currentTimeMillis())
|
||||
.setHeartRate(b)
|
||||
.build();
|
||||
|
||||
return sds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(byte[] buffer) {
|
||||
// TODO crc etc.
|
||||
return buffer[0] == 0x02 && buffer[59] == 0x03;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrameSize() {
|
||||
return 60;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findNextAlignment(byte[] buffer) {
|
||||
// TODO test or understand this code.
|
||||
for (int i = 0; i < buffer.length - 1; i++) {
|
||||
if (buffer[i] == 0x03 && buffer[i+1] == 0x02) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class ZephyrSensorManager extends BluetoothSensorManager {
|
||||
|
||||
public ZephyrSensorManager(Context context) {
|
||||
super(context, new ZephyrMessageParser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.stats;
|
||||
|
||||
/**
|
||||
* A helper class that tracks a minimum and a maximum of a variable.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ExtremityMonitor {
|
||||
|
||||
/**
|
||||
* The smallest value seen so far.
|
||||
*/
|
||||
private double min;
|
||||
|
||||
/**
|
||||
* The largest value seen so far.
|
||||
*/
|
||||
private double max;
|
||||
|
||||
public ExtremityMonitor() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the min and the max with the new value.
|
||||
*
|
||||
* @param value the new value for the monitor
|
||||
* @return true if an extremity was found
|
||||
*/
|
||||
public boolean update(double value) {
|
||||
boolean changed = false;
|
||||
if (value < min) {
|
||||
min = value;
|
||||
changed = true;
|
||||
}
|
||||
if (value > max) {
|
||||
max = value;
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum value seen.
|
||||
*
|
||||
* @return The minimum value passed into the update() function
|
||||
*/
|
||||
public double getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum value seen.
|
||||
*
|
||||
* @return The maximum value passed into the update() function
|
||||
*/
|
||||
public double getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this object to it's initial state where the min and max are unknown.
|
||||
*/
|
||||
public void reset() {
|
||||
min = Double.POSITIVE_INFINITY;
|
||||
max = Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum and maximum values.
|
||||
*/
|
||||
public void set(double min, double max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum value.
|
||||
*/
|
||||
public void setMin(double min) {
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum value.
|
||||
*/
|
||||
public void setMax(double max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Min: " + min + " Max: " + max;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
package com.google.android.apps.mytracks.stats;
|
||||
parcelable TripStatistics;
|
||||
@@ -1,572 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.stats;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Statistical data about a trip.
|
||||
* The data in this class should be filled out by {@link TripStatisticsBuilder}.
|
||||
*
|
||||
* TODO: hashCode and equals
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TripStatistics implements Parcelable {
|
||||
|
||||
/**
|
||||
* The start time for the trip. This is system time which might not match gps
|
||||
* time.
|
||||
*/
|
||||
private long startTime = -1;
|
||||
|
||||
/**
|
||||
* The stop time for the trip. This is the system time which might not match
|
||||
* gps time.
|
||||
*/
|
||||
private long stopTime = -1;
|
||||
|
||||
/**
|
||||
* The total time that we believe the user was traveling in milliseconds.
|
||||
*/
|
||||
private long movingTime;
|
||||
|
||||
/**
|
||||
* The total time of the trip in milliseconds.
|
||||
* This is only updated when new points are received, so it may be stale.
|
||||
*/
|
||||
private long totalTime;
|
||||
|
||||
/**
|
||||
* The total distance in meters that the user traveled on this trip.
|
||||
*/
|
||||
private double totalDistance;
|
||||
|
||||
/**
|
||||
* The total elevation gained on this trip in meters.
|
||||
*/
|
||||
private double totalElevationGain;
|
||||
|
||||
/**
|
||||
* The maximum speed in meters/second reported that we believe to be a valid
|
||||
* speed.
|
||||
*/
|
||||
private double maxSpeed;
|
||||
|
||||
/**
|
||||
* The min and max latitude values seen in this trip.
|
||||
*/
|
||||
private final ExtremityMonitor latitudeExtremities = new ExtremityMonitor();
|
||||
|
||||
/**
|
||||
* The min and max longitude values seen in this trip.
|
||||
*/
|
||||
private final ExtremityMonitor longitudeExtremities = new ExtremityMonitor();
|
||||
|
||||
/**
|
||||
* The min and max elevation seen on this trip in meters.
|
||||
*/
|
||||
private final ExtremityMonitor elevationExtremities = new ExtremityMonitor();
|
||||
|
||||
/**
|
||||
* The minimum and maximum grade calculations on this trip.
|
||||
*/
|
||||
private final ExtremityMonitor gradeExtremities = new ExtremityMonitor();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public TripStatistics() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*
|
||||
* @param other another statistics data object to copy from
|
||||
*/
|
||||
public TripStatistics(TripStatistics other) {
|
||||
this.maxSpeed = other.maxSpeed;
|
||||
this.movingTime = other.movingTime;
|
||||
this.startTime = other.startTime;
|
||||
this.stopTime = other.stopTime;
|
||||
this.totalDistance = other.totalDistance;
|
||||
this.totalElevationGain = other.totalElevationGain;
|
||||
this.totalTime = other.totalTime;
|
||||
|
||||
this.latitudeExtremities.set(other.latitudeExtremities.getMin(),
|
||||
other.latitudeExtremities.getMax());
|
||||
this.longitudeExtremities.set(other.longitudeExtremities.getMin(),
|
||||
other.longitudeExtremities.getMax());
|
||||
this.elevationExtremities.set(other.elevationExtremities.getMin(),
|
||||
other.elevationExtremities.getMax());
|
||||
this.gradeExtremities.set(other.gradeExtremities.getMin(),
|
||||
other.gradeExtremities.getMax());
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines these statistics with those from another object.
|
||||
* This assumes that the time periods covered by each do not intersect.
|
||||
*
|
||||
* @param other the other waypoint
|
||||
*/
|
||||
public void merge(TripStatistics other) {
|
||||
startTime = Math.min(startTime, other.startTime);
|
||||
stopTime = Math.max(stopTime, other.stopTime);
|
||||
totalTime += other.totalTime;
|
||||
movingTime += other.movingTime;
|
||||
totalDistance += other.totalDistance;
|
||||
totalElevationGain += other.totalElevationGain;
|
||||
maxSpeed = Math.max(maxSpeed, other.maxSpeed);
|
||||
|
||||
latitudeExtremities.update(other.latitudeExtremities.getMax());
|
||||
latitudeExtremities.update(other.latitudeExtremities.getMin());
|
||||
longitudeExtremities.update(other.longitudeExtremities.getMax());
|
||||
longitudeExtremities.update(other.longitudeExtremities.getMin());
|
||||
elevationExtremities.update(other.elevationExtremities.getMax());
|
||||
elevationExtremities.update(other.elevationExtremities.getMin());
|
||||
gradeExtremities.update(other.gradeExtremities.getMax());
|
||||
gradeExtremities.update(other.gradeExtremities.getMin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time that this track started.
|
||||
*
|
||||
* @return The number of milliseconds since epoch to the time when this track
|
||||
* started
|
||||
*/
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time that this track stopped.
|
||||
*
|
||||
* @return The number of milliseconds since epoch to the time when this track
|
||||
* stopped
|
||||
*/
|
||||
public long getStopTime() {
|
||||
return stopTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total time that this track has been active.
|
||||
* This statistic is only updated when a new point is added to the statistics,
|
||||
* so it may be off. If you need to calculate the proper total time, use
|
||||
* {@link #getStartTime} with the current time.
|
||||
*
|
||||
* @return The total number of milliseconds the track was active
|
||||
*/
|
||||
public long getTotalTime() {
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total distance the user traveled.
|
||||
*
|
||||
* @return The total distance traveled in meters
|
||||
*/
|
||||
public double getTotalDistance() {
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the the average speed the user traveled.
|
||||
* This calculation only takes into account the displacement until the last
|
||||
* point that was accounted for in statistics.
|
||||
*
|
||||
* @return The average speed in m/s
|
||||
*/
|
||||
public double getAverageSpeed() {
|
||||
return totalDistance / ((double) totalTime / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the the average speed the user traveled when they were actively
|
||||
* moving.
|
||||
*
|
||||
* @return The average moving speed in m/s
|
||||
*/
|
||||
public double getAverageMovingSpeed() {
|
||||
return totalDistance / ((double) movingTime / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the the maximum speed for this track.
|
||||
*
|
||||
* @return The maximum speed in m/s
|
||||
*/
|
||||
public double getMaxSpeed() {
|
||||
return maxSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the moving time.
|
||||
*
|
||||
* @return The total number of milliseconds the user was moving
|
||||
*/
|
||||
public long getMovingTime() {
|
||||
return movingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total elevation gain for this trip. This is calculated as the sum
|
||||
* of all positive differences in the smoothed elevation.
|
||||
*
|
||||
* @return The elevation gain in meters for this trip
|
||||
*/
|
||||
public double getTotalElevationGain() {
|
||||
return totalElevationGain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the leftmost position (lowest longitude) of the track, in signed
|
||||
* decimal degrees.
|
||||
*/
|
||||
public int getLeft() {
|
||||
return (int) (longitudeExtremities.getMin() * 1E6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rightmost position (highest longitude) of the track, in signed
|
||||
* decimal degrees.
|
||||
*/
|
||||
public int getRight() {
|
||||
return (int) (longitudeExtremities.getMax() * 1E6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bottommost position (lowest latitude) of the track, in meters.
|
||||
*/
|
||||
public int getBottom() {
|
||||
return (int) (latitudeExtremities.getMin() * 1E6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the topmost position (highest latitude) of the track, in meters.
|
||||
*/
|
||||
public int getTop() {
|
||||
return (int) (latitudeExtremities.getMax() * 1E6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum elevation seen on this trip. This is calculated from the
|
||||
* smoothed elevation so this can actually be more than the current elevation.
|
||||
*
|
||||
* @return The smallest elevation reading for this trip in meters
|
||||
*/
|
||||
public double getMinElevation() {
|
||||
return elevationExtremities.getMin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum elevation seen on this trip. This is calculated from the
|
||||
* smoothed elevation so this can actually be less than the current elevation.
|
||||
*
|
||||
* @return The largest elevation reading for this trip in meters
|
||||
*/
|
||||
public double getMaxElevation() {
|
||||
return elevationExtremities.getMax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum grade for this trip.
|
||||
*
|
||||
* @return The maximum grade for this trip as a fraction
|
||||
*/
|
||||
public double getMaxGrade() {
|
||||
return gradeExtremities.getMax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum grade for this trip.
|
||||
*
|
||||
* @return The minimum grade for this trip as a fraction
|
||||
*/
|
||||
public double getMinGrade() {
|
||||
return gradeExtremities.getMin();
|
||||
}
|
||||
|
||||
// Setters - to be used when restoring state or loading from the DB
|
||||
|
||||
/**
|
||||
* Sets the start time for this trip.
|
||||
*
|
||||
* @param startTime the start time, in milliseconds since the epoch
|
||||
*/
|
||||
public void setStartTime(long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stop time for this trip.
|
||||
*
|
||||
* @param stopTime the stop time, in milliseconds since the epoch
|
||||
*/
|
||||
public void setStopTime(long stopTime) {
|
||||
this.stopTime = stopTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total moving time.
|
||||
*
|
||||
* @param movingTime the moving time in milliseconds
|
||||
*/
|
||||
public void setMovingTime(long movingTime) {
|
||||
this.movingTime = movingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total trip time.
|
||||
*
|
||||
* @param totalTime the total trip time in milliseconds
|
||||
*/
|
||||
public void setTotalTime(long totalTime) {
|
||||
this.totalTime = totalTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total trip distance.
|
||||
*
|
||||
* @param totalDistance the trip distance in meters
|
||||
*/
|
||||
public void setTotalDistance(double totalDistance) {
|
||||
this.totalDistance = totalDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total elevation variation during the trip.
|
||||
*
|
||||
* @param totalElevationGain the elevation variation in meters
|
||||
*/
|
||||
public void setTotalElevationGain(double totalElevationGain) {
|
||||
this.totalElevationGain = totalElevationGain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum speed reached during the trip.
|
||||
*
|
||||
* @param maxSpeed the maximum speed in meters per second
|
||||
*/
|
||||
public void setMaxSpeed(double maxSpeed) {
|
||||
this.maxSpeed = maxSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum elevation reached during the trip.
|
||||
*
|
||||
* @param elevation the minimum elevation in meters
|
||||
*/
|
||||
public void setMinElevation(double elevation) {
|
||||
elevationExtremities.setMin(elevation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum elevation reached during the trip.
|
||||
*
|
||||
* @param elevation the maximum elevation in meters
|
||||
*/
|
||||
public void setMaxElevation(double elevation) {
|
||||
elevationExtremities.setMax(elevation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum grade obtained during the trip.
|
||||
*
|
||||
* @param grade the grade as a fraction (-1.0 would mean vertical downwards)
|
||||
*/
|
||||
public void setMinGrade(double grade) {
|
||||
gradeExtremities.setMin(grade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum grade obtained during the trip).
|
||||
*
|
||||
* @param grade the grade as a fraction (1.0 would mean vertical upwards)
|
||||
*/
|
||||
public void setMaxGrade(double grade) {
|
||||
gradeExtremities.setMax(grade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bounding box for this trip.
|
||||
* The unit for all parameters is signed decimal degrees (degrees * 1E6).
|
||||
*
|
||||
* @param leftE6 the westmost longitude reached
|
||||
* @param topE6 the northmost latitude reached
|
||||
* @param rightE6 the eastmost longitude reached
|
||||
* @param bottomE6 the southmost latitude reached
|
||||
*/
|
||||
public void setBounds(int leftE6, int topE6, int rightE6, int bottomE6) {
|
||||
latitudeExtremities.set(bottomE6 / 1E6, topE6 / 1E6);
|
||||
longitudeExtremities.set(leftE6 / 1E6, rightE6 / 1E6);
|
||||
}
|
||||
|
||||
// Data manipulation methods
|
||||
|
||||
/**
|
||||
* Adds to the current total distance.
|
||||
*
|
||||
* @param distance the distance to add in meters
|
||||
*/
|
||||
void addTotalDistance(double distance) {
|
||||
totalDistance += distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to the total elevation variation.
|
||||
*
|
||||
* @param gain the elevation variation in meters
|
||||
*/
|
||||
void addTotalElevationGain(double gain) {
|
||||
totalElevationGain += gain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to the total moving time of the trip.
|
||||
*
|
||||
* @param time the time in milliseconds
|
||||
*/
|
||||
void addMovingTime(long time) {
|
||||
movingTime += time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accounts for a new latitude value for the bounding box.
|
||||
*
|
||||
* @param latitude the latitude value in signed decimal degrees
|
||||
*/
|
||||
void updateLatitudeExtremities(double latitude) {
|
||||
latitudeExtremities.update(latitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accounts for a new longitude value for the bounding box.
|
||||
*
|
||||
* @param longitude the longitude value in signed decimal degrees
|
||||
*/
|
||||
void updateLongitudeExtremities(double longitude) {
|
||||
longitudeExtremities.update(longitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accounts for a new elevation value for the bounding box.
|
||||
*
|
||||
* @param elevation the elevation value in meters
|
||||
*/
|
||||
void updateElevationExtremities(double elevation) {
|
||||
elevationExtremities.update(elevation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accounts for a new grade value.
|
||||
*
|
||||
* @param grade the grade value as a fraction
|
||||
*/
|
||||
void updateGradeExtremities(double grade) {
|
||||
gradeExtremities.update(grade);
|
||||
}
|
||||
|
||||
// String conversion
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TripStatistics { Start Time: " + getStartTime()
|
||||
+ "; Total Time: " + getTotalTime()
|
||||
+ "; Moving Time: " + getMovingTime()
|
||||
+ "; Total Distance: " + getTotalDistance()
|
||||
+ "; Elevation Gain: " + getTotalElevationGain()
|
||||
+ "; Min Elevation: " + getMinElevation()
|
||||
+ "; Max Elevation: " + getMaxElevation()
|
||||
+ "; Average Speed: " + getAverageMovingSpeed()
|
||||
+ "; Min Grade: " + getMinGrade()
|
||||
+ "; Max Grade: " + getMaxGrade()
|
||||
+ "}";
|
||||
}
|
||||
|
||||
// Parcelable interface and creator
|
||||
|
||||
/**
|
||||
* Creator of statistics data from parcels.
|
||||
*/
|
||||
public static class Creator
|
||||
implements Parcelable.Creator<TripStatistics> {
|
||||
|
||||
@Override
|
||||
public TripStatistics createFromParcel(Parcel source) {
|
||||
TripStatistics data = new TripStatistics();
|
||||
|
||||
data.startTime = source.readLong();
|
||||
data.movingTime = source.readLong();
|
||||
data.totalTime = source.readLong();
|
||||
data.totalDistance = source.readDouble();
|
||||
data.totalElevationGain = source.readDouble();
|
||||
data.maxSpeed = source.readDouble();
|
||||
|
||||
double minLat = source.readDouble();
|
||||
double maxLat = source.readDouble();
|
||||
data.latitudeExtremities.set(minLat, maxLat);
|
||||
|
||||
double minLong = source.readDouble();
|
||||
double maxLong = source.readDouble();
|
||||
data.longitudeExtremities.set(minLong, maxLong);
|
||||
|
||||
double minElev = source.readDouble();
|
||||
double maxElev = source.readDouble();
|
||||
data.elevationExtremities.set(minElev, maxElev);
|
||||
|
||||
double minGrade = source.readDouble();
|
||||
double maxGrade = source.readDouble();
|
||||
data.gradeExtremities.set(minGrade, maxGrade);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TripStatistics[] newArray(int size) {
|
||||
return new TripStatistics[size];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creator of {@link TripStatistics} from parcels.
|
||||
*/
|
||||
public static final Creator CREATOR = new Creator();
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(startTime);
|
||||
dest.writeLong(movingTime);
|
||||
dest.writeLong(totalTime);
|
||||
dest.writeDouble(totalDistance);
|
||||
dest.writeDouble(totalElevationGain);
|
||||
dest.writeDouble(maxSpeed);
|
||||
|
||||
dest.writeDouble(latitudeExtremities.getMin());
|
||||
dest.writeDouble(latitudeExtremities.getMax());
|
||||
dest.writeDouble(longitudeExtremities.getMin());
|
||||
dest.writeDouble(longitudeExtremities.getMax());
|
||||
dest.writeDouble(elevationExtremities.getMin());
|
||||
dest.writeDouble(elevationExtremities.getMax());
|
||||
dest.writeDouble(gradeExtremities.getMin());
|
||||
dest.writeDouble(gradeExtremities.getMax());
|
||||
}
|
||||
}
|
||||
@@ -102,10 +102,6 @@ public class ApiFeatures {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasModernSignalStrength() {
|
||||
return getApiLevel() >= 7;
|
||||
}
|
||||
|
||||
public boolean hasStrictMode() {
|
||||
return getApiLevel() >= 9;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with bluetooth devices.
|
||||
* This can be used safely even in systems that don't support bluetooth,
|
||||
* in which case a dummy implementation will be used.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public abstract class BluetoothDeviceUtils {
|
||||
public static final String ANY_DEVICE = "any";
|
||||
private static BluetoothDeviceUtils instance;
|
||||
|
||||
/**
|
||||
* Dummy implementation, for systems that don't support bluetooth.
|
||||
*/
|
||||
private static class DummyImpl extends BluetoothDeviceUtils {
|
||||
@Override
|
||||
public void populateDeviceLists(List<String> deviceNames, List<String> deviceAddresses) {
|
||||
// Do nothing - no devices to add
|
||||
}
|
||||
|
||||
@Override
|
||||
public BluetoothDevice findDeviceMatching(String targetDeviceAddress) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Real implementation, for systems that DO support bluetooth.
|
||||
*/
|
||||
private static class RealImpl extends BluetoothDeviceUtils {
|
||||
private final BluetoothAdapter bluetoothAdapter;
|
||||
|
||||
public RealImpl() {
|
||||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
if (bluetoothAdapter == null) {
|
||||
throw new IllegalStateException("Unable to get bluetooth adapter");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateDeviceLists(List<String> deviceNames, List<String> deviceAddresses) {
|
||||
ensureNotDiscovering();
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
BluetoothClass bluetoothClass = device.getBluetoothClass();
|
||||
if (bluetoothClass != null) {
|
||||
// Not really sure what we want, but I know what we don't want.
|
||||
switch(bluetoothClass.getMajorDeviceClass()) {
|
||||
case BluetoothClass.Device.Major.COMPUTER:
|
||||
case BluetoothClass.Device.Major.PHONE:
|
||||
break;
|
||||
default:
|
||||
deviceAddresses.add(device.getAddress());
|
||||
deviceNames.add(device.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BluetoothDevice findDeviceMatching(String targetDeviceAddress) {
|
||||
if (targetDeviceAddress.equals(ANY_DEVICE)) {
|
||||
return findAnyDevice();
|
||||
} else {
|
||||
return findDeviceByAddress(targetDeviceAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the first suitable bluetooth sensor.
|
||||
*/
|
||||
private BluetoothDevice findAnyDevice() {
|
||||
ensureNotDiscovering();
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
// Look for the first paired computer device
|
||||
if (isSuitableDevice(device)) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns a device with the given address, or null if it's not
|
||||
* a suitable sensor.
|
||||
*/
|
||||
private BluetoothDevice findDeviceByAddress(String targetDeviceAddress) {
|
||||
ensureNotDiscovering();
|
||||
|
||||
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(targetDeviceAddress);
|
||||
if (isSuitableDevice(device)) {
|
||||
return device;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the bluetooth adapter is not in discovery mode.
|
||||
*/
|
||||
private void ensureNotDiscovering() {
|
||||
// If it's in discovery mode, cancel that for now.
|
||||
bluetoothAdapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given device is a suitable sensor.
|
||||
*
|
||||
* @param device the device to check
|
||||
* @return true if it's suitable, false otherwise
|
||||
*/
|
||||
private boolean isSuitableDevice(BluetoothDevice device) {
|
||||
// Check that the device is bonded
|
||||
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the proper (singleton) instance of this class.
|
||||
*/
|
||||
public static BluetoothDeviceUtils getInstance() {
|
||||
if (instance == null) {
|
||||
if (!isBluetoothMethodSupported()) {
|
||||
Log.d(MyTracksConstants.TAG, "Using dummy bluetooth utils");
|
||||
instance = new DummyImpl();
|
||||
} else {
|
||||
Log.d(MyTracksConstants.TAG, "Using real bluetooth utils");
|
||||
try {
|
||||
instance = new RealImpl();
|
||||
} catch (IllegalStateException ise) {
|
||||
Log.w(MyTracksConstants.TAG, "Oops, I mean, using dummy bluetooth utils", ise);
|
||||
instance = new DummyImpl();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the given lists with the names and addresses of all suitable
|
||||
* bluetooth devices.
|
||||
*
|
||||
* @param deviceNames the list to populate with user-visible names
|
||||
* @param deviceAddresses the list to populate with device addresses
|
||||
*/
|
||||
public abstract void populateDeviceLists(List<String> deviceNames, List<String> deviceAddresses);
|
||||
|
||||
/**
|
||||
* Finds the bluetooth device with the given address.
|
||||
*
|
||||
* @param targetDeviceAddress the address of the device, or
|
||||
* {@link #ANY_DEVICE} for using the first suitable device
|
||||
* @return the device's descriptor, or null if not found
|
||||
*/
|
||||
public abstract BluetoothDevice findDeviceMatching(String targetDeviceAddress);
|
||||
|
||||
/**
|
||||
* @return whether the bluetooth method is supported on this device
|
||||
*/
|
||||
public static boolean isBluetoothMethodSupported() {
|
||||
return Integer.parseInt(Build.VERSION.SDK) >= 5;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user