forked from upstream-mirrors/OpenTracks
Merge heads
This commit is contained in:
@@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="40"
|
android:versionCode="41"
|
||||||
android:versionName="2.0"
|
android:versionName="2.0"
|
||||||
package="com.google.android.maps.mytracks" >
|
package="com.google.android.maps.mytracks" >
|
||||||
|
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ limitations under the License.
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sensor_state_last_sensor_time"
|
android:id="@+id/sensor_state_last_sensor_time"
|
||||||
style="@style/SensorStateValue" />
|
style="@style/SensorStateValue" />
|
||||||
<!-- Power -->
|
<!-- Heart rate -->
|
||||||
<TextView
|
<TextView
|
||||||
android:text="@string/sensor_state_power"
|
android:text="@string/sensor_state_heart_rate"
|
||||||
style="@style/TextMedium" />
|
style="@style/TextMedium" />
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sensor_state_power"
|
android:id="@+id/sensor_state_heart_rate"
|
||||||
style="@style/SensorStateValue" />
|
style="@style/SensorStateValue" />
|
||||||
<!-- Cadence -->
|
<!-- Cadence -->
|
||||||
<TextView
|
<TextView
|
||||||
@@ -48,12 +48,12 @@ limitations under the License.
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sensor_state_cadence"
|
android:id="@+id/sensor_state_cadence"
|
||||||
style="@style/SensorStateValue" />
|
style="@style/SensorStateValue" />
|
||||||
<!-- Heart rate -->
|
<!-- Power -->
|
||||||
<TextView
|
<TextView
|
||||||
android:text="@string/sensor_state_heart_rate"
|
android:text="@string/sensor_state_power"
|
||||||
style="@style/TextMedium" />
|
style="@style/TextMedium" />
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sensor_state_heart_rate"
|
android:id="@+id/sensor_state_power"
|
||||||
style="@style/SensorStateValue" />
|
style="@style/SensorStateValue" />
|
||||||
<!-- Battery level -->
|
<!-- Battery level -->
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ limitations under the License.
|
|||||||
<color name="speed_border">#FF0066FF</color>
|
<color name="speed_border">#FF0066FF</color>
|
||||||
<color name="speed_fill">#40C8E0FF</color>
|
<color name="speed_fill">#40C8E0FF</color>
|
||||||
|
|
||||||
|
<!-- Blue and a transparent blue -->
|
||||||
|
<color name="pace_border">#FF0066FF</color>
|
||||||
|
<color name="pace_fill">#40C8E0FF</color>
|
||||||
|
|
||||||
<!-- Red and a transparent red -->
|
<!-- Red and a transparent red -->
|
||||||
<color name="heartrate_border">#FFCC3300</color>
|
<color name="heartrate_border">#FFCC3300</color>
|
||||||
<color name="heartrate_fill">#40E88A70</color>
|
<color name="heartrate_fill">#40E88A70</color>
|
||||||
|
|||||||
@@ -451,6 +451,10 @@ limitations under the License.
|
|||||||
%2$.1f speed in miles per hour">
|
%2$.1f speed in miles per hour">
|
||||||
Average speed: %1$.2f km/h (%2$.1f mi/h)
|
Average speed: %1$.2f km/h (%2$.1f mi/h)
|
||||||
</string>
|
</string>
|
||||||
|
<string name="description_elevation_imperial"
|
||||||
|
translation_description="Elevation in imperial units, feet.">
|
||||||
|
Elevation (ft)
|
||||||
|
</string>
|
||||||
<string name="description_elevation_gain"
|
<string name="description_elevation_gain"
|
||||||
translation_description="Elevation gain description.
|
translation_description="Elevation gain description.
|
||||||
|
|
||||||
@@ -458,6 +462,10 @@ limitations under the License.
|
|||||||
%2$d elevation in feet">
|
%2$d elevation in feet">
|
||||||
Elevation gain: %1$d m (%2$d ft)
|
Elevation gain: %1$d m (%2$d ft)
|
||||||
</string>
|
</string>
|
||||||
|
<string name="description_elevation_metric"
|
||||||
|
translation_description="Elevation in metric units, meters.">
|
||||||
|
Elevation (m)
|
||||||
|
</string>
|
||||||
<string name="description_fastest_pace"
|
<string name="description_fastest_pace"
|
||||||
translation_description="Fastest pace description.
|
translation_description="Fastest pace description.
|
||||||
|
|
||||||
@@ -489,10 +497,6 @@ limitations under the License.
|
|||||||
translation_description="Label for location speed in meters per second.">
|
translation_description="Label for location speed in meters per second.">
|
||||||
Speed (m/s)
|
Speed (m/s)
|
||||||
</string>
|
</string>
|
||||||
<string name="description_location_time"
|
|
||||||
translation_description="Label for location time.">
|
|
||||||
Time
|
|
||||||
</string>
|
|
||||||
<string name="description_max_elevation"
|
<string name="description_max_elevation"
|
||||||
translation_description="Max elevation description.
|
translation_description="Max elevation description.
|
||||||
|
|
||||||
@@ -532,6 +536,14 @@ limitations under the License.
|
|||||||
%1$s time. E.g., '1:08:59'">
|
%1$s time. E.g., '1:08:59'">
|
||||||
Moving time: %1$s
|
Moving time: %1$s
|
||||||
</string>
|
</string>
|
||||||
|
<string name="description_pace_imperial"
|
||||||
|
translation_description="Pace in imperial units, hours per mile.">
|
||||||
|
Pace (hr/mi)
|
||||||
|
</string>
|
||||||
|
<string name="description_pace_metric"
|
||||||
|
translation_description="Pace in metric units, hours per kilometer.">
|
||||||
|
Pace (hr/km)
|
||||||
|
</string>
|
||||||
<string name="description_recorded_time"
|
<string name="description_recorded_time"
|
||||||
translation_description="Recorded time description.
|
translation_description="Recorded time description.
|
||||||
|
|
||||||
@@ -554,6 +566,18 @@ limitations under the License.
|
|||||||
translation_description="Label for power with its unit watt (W).">
|
translation_description="Label for power with its unit watt (W).">
|
||||||
Power (W)
|
Power (W)
|
||||||
</string>
|
</string>
|
||||||
|
<string name="description_speed_imperial"
|
||||||
|
translation_description="Speed in imperial units, miles per hour.">
|
||||||
|
Speed (mi/hr)
|
||||||
|
</string>
|
||||||
|
<string name="description_speed_metric"
|
||||||
|
translation_description="Speed in metric units, kilometers per hour.">
|
||||||
|
Speed (km/hr)
|
||||||
|
</string>
|
||||||
|
<string name="description_time"
|
||||||
|
translation_description="Label for time.">
|
||||||
|
Time
|
||||||
|
</string>
|
||||||
<string name="description_track_point"
|
<string name="description_track_point"
|
||||||
translation_description="Label for a track point">
|
translation_description="Label for a track point">
|
||||||
Point
|
Point
|
||||||
@@ -1721,6 +1745,12 @@ limitations under the License.
|
|||||||
%1$d: sensor id number. E.g., '1'.">
|
%1$d: sensor id number. E.g., '1'.">
|
||||||
Paired with: %1$d.
|
Paired with: %1$d.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="settings_sensor_bluetooth_connected"
|
||||||
|
translation_description="The message when connected to a bluetooth sensor.
|
||||||
|
|
||||||
|
%1$s: sensor name. E.g., 'polar bluetooth'.">
|
||||||
|
Connected to %1$s
|
||||||
|
</string>
|
||||||
<string name="settings_sensor_bluetooth_options"
|
<string name="settings_sensor_bluetooth_options"
|
||||||
translation_description="In the 'Sensor' settings, the section header to configure the Bluetooth
|
translation_description="In the 'Sensor' settings, the section header to configure the Bluetooth
|
||||||
options.
|
options.
|
||||||
@@ -1898,26 +1928,6 @@ limitations under the License.
|
|||||||
of inclination to the horizontal, also known as slope or incline.">
|
of inclination to the horizontal, also known as slope or incline.">
|
||||||
Grade
|
Grade
|
||||||
</string>
|
</string>
|
||||||
<string name="settings_stats_rate_pace_imperial"
|
|
||||||
translation_description="In the 'Stats' settings, the option to display pace, with example in
|
|
||||||
imperial units.">
|
|
||||||
Pace (hr/mi)
|
|
||||||
</string>
|
|
||||||
<string name="settings_stats_rate_pace_metric"
|
|
||||||
translation_description="In the 'Stats' settings, the option to display pace, with example in
|
|
||||||
metric units.">
|
|
||||||
Pace (hr/km)
|
|
||||||
</string>
|
|
||||||
<string name="settings_stats_rate_speed_imperial"
|
|
||||||
translation_description="In the 'Stats' settings, the option to display speed, with example in
|
|
||||||
imperial units.">
|
|
||||||
Speed (mi/hr)
|
|
||||||
</string>
|
|
||||||
<string name="settings_stats_rate_speed_metric"
|
|
||||||
translation_description="In the 'Stats' settings, the option to display speed, with example in
|
|
||||||
metric units.">
|
|
||||||
Speed (km/hr)
|
|
||||||
</string>
|
|
||||||
<string name="settings_stats_rate_title"
|
<string name="settings_stats_rate_title"
|
||||||
translation_description="In the 'Stats' settings, the option to change the preferred rate,
|
translation_description="In the 'Stats' settings, the option to change the preferred rate,
|
||||||
either speed (mi/hr) or pace (hr/mi).">
|
either speed (mi/hr) or pace (hr/mi).">
|
||||||
|
|||||||
@@ -13,293 +13,252 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.android.apps.mytracks;
|
package com.google.android.apps.mytracks;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.stats.ExtremityMonitor;
|
import com.google.android.apps.mytracks.stats.ExtremityMonitor;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Paint.Align;
|
||||||
import android.graphics.Paint.Style;
|
import android.graphics.Paint.Style;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class encapsulates meta data about one series of values for a chart.
|
* This class encapsulates the meta data for one series of the chart values.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class ChartValueSeries {
|
public class ChartValueSeries {
|
||||||
|
|
||||||
private final ExtremityMonitor monitor = new ExtremityMonitor();
|
private static final float STROKE_WIDTH = 2f;
|
||||||
private final NumberFormat format;
|
|
||||||
private final Path path = new Path();
|
private final int absoluteMin;
|
||||||
|
private final int absoluteMax;
|
||||||
|
private final int[] intervalValues;
|
||||||
|
private final int metricTitleId;
|
||||||
|
private final int imperialTitleId;
|
||||||
private final Paint fillPaint;
|
private final Paint fillPaint;
|
||||||
private final Paint strokePaint;
|
private final Paint strokePaint;
|
||||||
private final Paint labelPaint;
|
private final Paint titlePaint;
|
||||||
private final ZoomSettings zoomSettings;
|
private final Paint markerPaint;
|
||||||
|
private final ExtremityMonitor extremityMonitor;
|
||||||
private String title;
|
private final NumberFormat numberFormat;
|
||||||
private double min;
|
private final Path path;
|
||||||
private double max = 1.0;
|
|
||||||
private int effectiveMax;
|
private int interval = 1;
|
||||||
private int effectiveMin;
|
private int minMarkerValue = 0;
|
||||||
private double spread;
|
private int maxMarkerValue = interval * ChartView.Y_AXIS_INTERVALS;
|
||||||
private int interval;
|
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class controls how effective min/max values of a {@link ChartValueSeries} are calculated.
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @param absoluteMin the absolute min value
|
||||||
|
* @param absoluteMax the absolute max value
|
||||||
|
* @param intervalValues the list of interval values
|
||||||
|
* @param metricTitleId the metric title id
|
||||||
|
* @param imperialTitleId the imperial title id
|
||||||
|
* @param fillColor the fill color
|
||||||
|
* @param strokeColor the stroke color
|
||||||
*/
|
*/
|
||||||
public static class ZoomSettings {
|
public ChartValueSeries(Context context, int absoluteMin, int absoluteMax, int[] intervalValues,
|
||||||
private int intervals;
|
int metricTitleId, int imperialTitleId, int fillColor, int strokeColor) {
|
||||||
private final int absoluteMin;
|
this.absoluteMin = absoluteMin;
|
||||||
private final int absoluteMax;
|
this.absoluteMax = absoluteMax;
|
||||||
private final int[] zoomLevels;
|
this.intervalValues = intervalValues;
|
||||||
|
this.metricTitleId = metricTitleId;
|
||||||
|
this.imperialTitleId = imperialTitleId;
|
||||||
|
|
||||||
public ZoomSettings(int intervals, int[] zoomLevels) {
|
|
||||||
this.intervals = intervals;
|
|
||||||
this.absoluteMin = Integer.MAX_VALUE;
|
|
||||||
this.absoluteMax = Integer.MIN_VALUE;
|
|
||||||
this.zoomLevels = zoomLevels;
|
|
||||||
checkArgs();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ZoomSettings(int intervals, int absoluteMin, int absoluteMax, int[] zoomLevels) {
|
|
||||||
this.intervals = intervals;
|
|
||||||
this.absoluteMin = absoluteMin;
|
|
||||||
this.absoluteMax = absoluteMax;
|
|
||||||
this.zoomLevels = zoomLevels;
|
|
||||||
checkArgs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkArgs() {
|
|
||||||
if (intervals <= 0 || zoomLevels == null || zoomLevels.length == 0) {
|
|
||||||
throw new IllegalArgumentException("Expecing positive intervals and non-empty zoom levels");
|
|
||||||
}
|
|
||||||
for (int i = 1; i < zoomLevels.length; ++i) {
|
|
||||||
if (zoomLevels[i] <= zoomLevels[i - 1]) {
|
|
||||||
throw new IllegalArgumentException("Expecting zoom levels in ascending order");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIntervals() {
|
|
||||||
return intervals;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAbsoluteMin() {
|
|
||||||
return absoluteMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAbsoluteMax() {
|
|
||||||
return absoluteMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] getZoomLevels() {
|
|
||||||
return zoomLevels;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the interval between markings given the min and max values.
|
|
||||||
* This function attempts to find the smallest zoom level that fits [min,max] after rounding
|
|
||||||
* it to the current zoom level.
|
|
||||||
*
|
|
||||||
* @param min the minimum value in the series
|
|
||||||
* @param max the maximum value in the series
|
|
||||||
* @return the calculated interval for the given range
|
|
||||||
*/
|
|
||||||
public int calculateInterval(double min, double max) {
|
|
||||||
min = Math.min(min, absoluteMin);
|
|
||||||
max = Math.max(max, absoluteMax);
|
|
||||||
for (int i = 0; i < zoomLevels.length; ++i) {
|
|
||||||
int zoomLevel = zoomLevels[i];
|
|
||||||
int roundedMin = (int)(min / zoomLevel) * zoomLevel;
|
|
||||||
if (roundedMin > min) {
|
|
||||||
roundedMin -= zoomLevel;
|
|
||||||
}
|
|
||||||
double interval = (max - roundedMin) / intervals;
|
|
||||||
if (zoomLevel >= interval) {
|
|
||||||
return zoomLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return zoomLevels[zoomLevels.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new chart value series.
|
|
||||||
*
|
|
||||||
* @param context The context for the chart
|
|
||||||
* @param fillColor The paint for filling the chart
|
|
||||||
* @param strokeColor The paint for stroking the outside the chart, optional
|
|
||||||
* @param zoomSettings The settings related to zooming
|
|
||||||
* @param titleId The title ID
|
|
||||||
*
|
|
||||||
* TODO: Get rid of Context and inject appropriate values instead.
|
|
||||||
*/
|
|
||||||
public ChartValueSeries(
|
|
||||||
Context context, int fillColor, int strokeColor, ZoomSettings zoomSettings, int titleId) {
|
|
||||||
this.format = NumberFormat.getIntegerInstance();
|
|
||||||
fillPaint = new Paint();
|
fillPaint = new Paint();
|
||||||
fillPaint.setStyle(Style.FILL);
|
fillPaint.setStyle(Style.FILL);
|
||||||
fillPaint.setColor(context.getResources().getColor(fillColor));
|
fillPaint.setColor(context.getResources().getColor(fillColor));
|
||||||
fillPaint.setAntiAlias(true);
|
fillPaint.setAntiAlias(true);
|
||||||
if (strokeColor != -1) {
|
|
||||||
strokePaint = new Paint();
|
strokePaint = new Paint();
|
||||||
strokePaint.setStyle(Style.STROKE);
|
strokePaint.setStyle(Style.STROKE);
|
||||||
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
||||||
strokePaint.setAntiAlias(true);
|
strokePaint.setAntiAlias(true);
|
||||||
// Make a copy of the stroke paint with the default thickness.
|
|
||||||
labelPaint = new Paint(strokePaint);
|
float scale = context.getResources().getDisplayMetrics().density;
|
||||||
strokePaint.setStrokeWidth(2f);
|
|
||||||
} else {
|
// Make copies of the stroke paint with the default thickness
|
||||||
strokePaint = null;
|
titlePaint = new Paint(strokePaint);
|
||||||
labelPaint = fillPaint;
|
titlePaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE * scale);
|
||||||
}
|
titlePaint.setTextAlign(Align.CENTER);
|
||||||
this.zoomSettings = zoomSettings;
|
|
||||||
this.title = context.getString(titleId);
|
markerPaint = new Paint(strokePaint);
|
||||||
|
markerPaint.setTextSize(ChartView.SMALL_TEXT_SIZE * scale);
|
||||||
|
markerPaint.setTextAlign(Align.RIGHT);
|
||||||
|
|
||||||
|
// Set stroke paint thickness
|
||||||
|
strokePaint.setStrokeWidth(STROKE_WIDTH);
|
||||||
|
|
||||||
|
extremityMonitor = new ExtremityMonitor();
|
||||||
|
numberFormat = NumberFormat.getIntegerInstance();
|
||||||
|
path = new Path();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the path of the chart
|
* Sets the series enabled value.
|
||||||
|
*
|
||||||
|
* @param enabled true to enable
|
||||||
*/
|
*/
|
||||||
public void drawPath(Canvas c) {
|
public void setEnabled(boolean enabled) {
|
||||||
c.drawPath(path, fillPaint);
|
this.enabled = enabled;
|
||||||
if (strokePaint != null) {
|
|
||||||
c.drawPath(path, strokePaint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets this series
|
* Returns true if the series is enabled.
|
||||||
*/
|
|
||||||
public void reset() {
|
|
||||||
monitor.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates this series with a new value
|
|
||||||
*/
|
|
||||||
public void update(double d) {
|
|
||||||
monitor.update(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The interval between markers
|
|
||||||
*/
|
|
||||||
public int getInterval() {
|
|
||||||
return interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines what the min and max of the chart will be.
|
|
||||||
* This will round down and up the min and max respectively.
|
|
||||||
*/
|
|
||||||
public void updateDimension() {
|
|
||||||
if (monitor.getMax() == Double.NEGATIVE_INFINITY) {
|
|
||||||
min = 0;
|
|
||||||
max = 1;
|
|
||||||
} else {
|
|
||||||
min = monitor.getMin();
|
|
||||||
max = monitor.getMax();
|
|
||||||
}
|
|
||||||
min = Math.min(min, zoomSettings.getAbsoluteMin());
|
|
||||||
max = Math.max(max, zoomSettings.getAbsoluteMax());
|
|
||||||
|
|
||||||
this.interval = zoomSettings.calculateInterval(min, max);
|
|
||||||
// Round it up.
|
|
||||||
effectiveMax = ((int) (max / interval)) * interval + interval;
|
|
||||||
// Round it down.
|
|
||||||
effectiveMin = ((int) (min / interval)) * interval;
|
|
||||||
if (min < 0) {
|
|
||||||
effectiveMin -= interval;
|
|
||||||
}
|
|
||||||
spread = effectiveMax - effectiveMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The length of the longest string from the series
|
|
||||||
*/
|
|
||||||
public int getMaxLabelLength() {
|
|
||||||
String minS = format.format(effectiveMin);
|
|
||||||
String maxS = format.format(getMax());
|
|
||||||
return Math.max(minS.length(), maxS.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The rounded down minimum value
|
|
||||||
*/
|
|
||||||
public int getMin() {
|
|
||||||
return effectiveMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The rounded up maximum value
|
|
||||||
*/
|
|
||||||
public int getMax() {
|
|
||||||
return effectiveMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The difference between the min and max values in the series
|
|
||||||
*/
|
|
||||||
public double getSpread() {
|
|
||||||
return spread;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The number format for this series
|
|
||||||
*/
|
|
||||||
NumberFormat getFormat() {
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The path for this series
|
|
||||||
*/
|
|
||||||
Path getPath() {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The paint for this series
|
|
||||||
*/
|
|
||||||
Paint getPaint() {
|
|
||||||
return strokePaint == null ? fillPaint : strokePaint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Paint getLabelPaint() {
|
|
||||||
return labelPaint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The title of the series
|
|
||||||
*/
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return is this series enabled
|
|
||||||
*/
|
*/
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the series enabled flag.
|
* Returns true if the series has data.
|
||||||
*/
|
*/
|
||||||
public void setEnabled(boolean enabled) {
|
public boolean hasData() {
|
||||||
this.enabled = enabled;
|
return extremityMonitor.hasData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasData() {
|
/**
|
||||||
return monitor.hasData();
|
* Updates the series with a new value.
|
||||||
|
*
|
||||||
|
* @param value the new value
|
||||||
|
*/
|
||||||
|
public void update(double value) {
|
||||||
|
extremityMonitor.update(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the path.
|
||||||
|
*/
|
||||||
|
public Path getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the path on canvas.
|
||||||
|
*
|
||||||
|
* @param canvas the canvas
|
||||||
|
*/
|
||||||
|
public void drawPath(Canvas canvas) {
|
||||||
|
canvas.drawPath(path, fillPaint);
|
||||||
|
canvas.drawPath(path, strokePaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the y axis dimension.
|
||||||
|
*/
|
||||||
|
public void updateDimension() {
|
||||||
|
double min = hasData() ? extremityMonitor.getMin() : 0.0;
|
||||||
|
double max = hasData() ? extremityMonitor.getMax() : 1.0;
|
||||||
|
min = Math.max(min, absoluteMin);
|
||||||
|
max = Math.min(max, absoluteMax);
|
||||||
|
interval = getInterval(min, max);
|
||||||
|
minMarkerValue = getMinMarkerValue(min, interval);
|
||||||
|
maxMarkerValue = minMarkerValue + interval * ChartView.Y_AXIS_INTERVALS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the interval value.
|
||||||
|
*
|
||||||
|
* @param min the min value
|
||||||
|
* @param max the max value
|
||||||
|
*/
|
||||||
|
private int getInterval(double min, double max) {
|
||||||
|
for (int i = 0; i < intervalValues.length; i++) {
|
||||||
|
int intervalValue = intervalValues[i];
|
||||||
|
int minValue = getMinMarkerValue(min, intervalValue);
|
||||||
|
double targetInterval = (max - minValue) / ChartView.Y_AXIS_INTERVALS;
|
||||||
|
if (intervalValue >= targetInterval) {
|
||||||
|
return intervalValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return the largest interval
|
||||||
|
return intervalValues[intervalValues.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the min marker value.
|
||||||
|
*
|
||||||
|
* @param min the min series value
|
||||||
|
* @param intervalValue the interval value
|
||||||
|
*/
|
||||||
|
private int getMinMarkerValue(double min, int intervalValue) {
|
||||||
|
// Round down to the nearest intervalValue
|
||||||
|
int value = ((int) (min / intervalValue)) * intervalValue;
|
||||||
|
// value > min if min is negative
|
||||||
|
if (value > min) {
|
||||||
|
return value - intervalValue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the interval value.
|
||||||
|
*/
|
||||||
|
public int getInterval() {
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the min marker value.
|
||||||
|
*/
|
||||||
|
public int getMinMarkerValue() {
|
||||||
|
return minMarkerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the max marker value.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
int getMaxMarkerValue() {
|
||||||
|
return maxMarkerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title id.
|
||||||
|
*/
|
||||||
|
public int getTitleId(boolean metricUnits) {
|
||||||
|
return metricUnits ? metricTitleId : imperialTitleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title paint.
|
||||||
|
*/
|
||||||
|
public Paint getTitlePaint() {
|
||||||
|
return titlePaint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the marker paint.
|
||||||
|
*/
|
||||||
|
public Paint getMarkerPaint() {
|
||||||
|
return markerPaint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the largest marker.
|
||||||
|
*/
|
||||||
|
public String getLargestMarker() {
|
||||||
|
String minMarker = numberFormat.format(getMinMarkerValue());
|
||||||
|
String maxMarker = numberFormat.format(getMaxMarkerValue());
|
||||||
|
return minMarker.length() >= maxMarker.length() ? minMarker : maxMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a marker value.
|
||||||
|
*
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
public String formatMarker(int value) {
|
||||||
|
return numberFormat.format(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -172,8 +172,7 @@ public class ImportActivity extends Activity {
|
|||||||
if (success) {
|
if (success) {
|
||||||
showDialog(DIALOG_RESULT_ID);
|
showDialog(DIALOG_RESULT_ID);
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, getString(R.string.sd_card_import_error), Toast.LENGTH_LONG)
|
Toast.makeText(this, R.string.sd_card_import_error, Toast.LENGTH_LONG).show();
|
||||||
.show();
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ public class MarkerDetailActivity extends AbstractMyTracksActivity {
|
|||||||
Intent intent = IntentUtils.newIntent(this, MarkerListActivity.class)
|
Intent intent = IntentUtils.newIntent(this, MarkerListActivity.class)
|
||||||
.putExtra(MarkerListActivity.EXTRA_TRACK_ID, waypoint.getTrackId());
|
.putExtra(MarkerListActivity.EXTRA_TRACK_ID, waypoint.getTrackId());
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class SensorStateActivity extends AbstractMyTracksActivity {
|
|||||||
*/
|
*/
|
||||||
private void stopTempSensorManager() {
|
private void stopTempSensorManager() {
|
||||||
if (tempSensorManager != null) {
|
if (tempSensorManager != null) {
|
||||||
SensorManagerFactory.getInstance().releaseSensorManager(tempSensorManager);
|
SensorManagerFactory.releaseTempSensorManager();
|
||||||
tempSensorManager = null;
|
tempSensorManager = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ public class SensorStateActivity extends AbstractMyTracksActivity {
|
|||||||
Sensor.SensorDataSet sensorDataSet = null;
|
Sensor.SensorDataSet sensorDataSet = null;
|
||||||
|
|
||||||
if (tempSensorManager == null) {
|
if (tempSensorManager == null) {
|
||||||
tempSensorManager = SensorManagerFactory.getInstance().getSensorManager(this);
|
tempSensorManager = SensorManagerFactory.getTempSensorManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempSensorManager != null) {
|
if (tempSensorManager != null) {
|
||||||
@@ -199,19 +199,19 @@ public class SensorStateActivity extends AbstractMyTracksActivity {
|
|||||||
|
|
||||||
String lastSensorTime = sensorDataSet == null ? getString(R.string.value_unknown)
|
String lastSensorTime = sensorDataSet == null ? getString(R.string.value_unknown)
|
||||||
: getLastSensorTime(sensorDataSet);
|
: getLastSensorTime(sensorDataSet);
|
||||||
String power = sensorDataSet == null ? getString(R.string.value_unknown)
|
|
||||||
: getPower(sensorDataSet);
|
|
||||||
String cadence = sensorDataSet == null ? getString(R.string.value_unknown)
|
|
||||||
: getCadence(sensorDataSet);
|
|
||||||
String heartRate = sensorDataSet == null ? getString(R.string.value_unknown)
|
String heartRate = sensorDataSet == null ? getString(R.string.value_unknown)
|
||||||
: getHeartRate(sensorDataSet);
|
: getHeartRate(sensorDataSet);
|
||||||
|
String cadence = sensorDataSet == null ? getString(R.string.value_unknown)
|
||||||
|
: getCadence(sensorDataSet);
|
||||||
|
String power = sensorDataSet == null ? getString(R.string.value_unknown)
|
||||||
|
: getPower(sensorDataSet);
|
||||||
String battery = sensorDataSet == null ? getString(R.string.value_unknown)
|
String battery = sensorDataSet == null ? getString(R.string.value_unknown)
|
||||||
: getBattery(sensorDataSet);
|
: getBattery(sensorDataSet);
|
||||||
|
|
||||||
((TextView) findViewById(R.id.sensor_state_last_sensor_time)).setText(lastSensorTime);
|
((TextView) findViewById(R.id.sensor_state_last_sensor_time)).setText(lastSensorTime);
|
||||||
((TextView) findViewById(R.id.sensor_state_power)).setText(power);
|
|
||||||
((TextView) findViewById(R.id.sensor_state_cadence)).setText(cadence);
|
|
||||||
((TextView) findViewById(R.id.sensor_state_heart_rate)).setText(heartRate);
|
((TextView) findViewById(R.id.sensor_state_heart_rate)).setText(heartRate);
|
||||||
|
((TextView) findViewById(R.id.sensor_state_cadence)).setText(cadence);
|
||||||
|
((TextView) findViewById(R.id.sensor_state_power)).setText(power);
|
||||||
((TextView) findViewById(R.id.sensor_state_battery)).setText(battery);
|
((TextView) findViewById(R.id.sensor_state_battery)).setText(battery);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,19 +225,20 @@ public class SensorStateActivity extends AbstractMyTracksActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the power.
|
* Gets the heart rate.
|
||||||
*
|
*
|
||||||
* @param sensorDataSet sensor data set
|
* @param sensorDataSet sensor data set
|
||||||
*/
|
*/
|
||||||
private String getPower(Sensor.SensorDataSet sensorDataSet) {
|
private String getHeartRate(Sensor.SensorDataSet sensorDataSet) {
|
||||||
String value;
|
String value;
|
||||||
if (sensorDataSet.hasPower() && sensorDataSet.getPower().hasValue()
|
if (sensorDataSet.hasHeartRate() && sensorDataSet.getHeartRate().hasValue()
|
||||||
&& sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING) {
|
&& sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING) {
|
||||||
value = getString(R.string.sensor_state_power_value, sensorDataSet.getPower().getValue());
|
value = getString(
|
||||||
|
R.string.sensor_state_heart_rate_value, sensorDataSet.getHeartRate().getValue());
|
||||||
} else {
|
} else {
|
||||||
value = SensorUtils.getStateAsString(
|
value = SensorUtils.getStateAsString(
|
||||||
sensorDataSet.hasPower() ? sensorDataSet.getPower().getState() : Sensor.SensorState.NONE,
|
sensorDataSet.hasHeartRate() ? sensorDataSet.getHeartRate().getState()
|
||||||
this);
|
: Sensor.SensorState.NONE, this);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -261,20 +262,19 @@ public class SensorStateActivity extends AbstractMyTracksActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the heart rate.
|
* Gets the power.
|
||||||
*
|
*
|
||||||
* @param sensorDataSet sensor data set
|
* @param sensorDataSet sensor data set
|
||||||
*/
|
*/
|
||||||
private String getHeartRate(Sensor.SensorDataSet sensorDataSet) {
|
private String getPower(Sensor.SensorDataSet sensorDataSet) {
|
||||||
String value;
|
String value;
|
||||||
if (sensorDataSet.hasHeartRate() && sensorDataSet.getHeartRate().hasValue()
|
if (sensorDataSet.hasPower() && sensorDataSet.getPower().hasValue()
|
||||||
&& sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING) {
|
&& sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING) {
|
||||||
value = getString(
|
value = getString(R.string.sensor_state_power_value, sensorDataSet.getPower().getValue());
|
||||||
R.string.sensor_state_heart_rate_value, sensorDataSet.getHeartRate().getValue());
|
|
||||||
} else {
|
} else {
|
||||||
value = SensorUtils.getStateAsString(
|
value = SensorUtils.getStateAsString(
|
||||||
sensorDataSet.hasHeartRate() ? sensorDataSet.getHeartRate().getState()
|
sensorDataSet.hasPower() ? sensorDataSet.getPower().getState() : Sensor.SensorState.NONE,
|
||||||
: Sensor.SensorState.NONE, this);
|
this);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
|
|||||||
protected void onHomeSelected() {
|
protected void onHomeSelected() {
|
||||||
Intent intent = IntentUtils.newIntent(this, TrackListActivity.class);
|
Intent intent = IntentUtils.newIntent(this, TrackListActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ class DataSourcesWrapperImpl implements DataSourcesWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let the user know we have only an approximate location:
|
// Let the user know we have only an approximate location:
|
||||||
Toast.makeText(context, context.getString(toastResId), Toast.LENGTH_LONG).show();
|
Toast.makeText(context, toastResId, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,15 +77,15 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
private boolean reportSpeed = true;
|
private boolean reportSpeed = true;
|
||||||
|
|
||||||
private boolean chartByDistance = true;
|
private boolean chartByDistance = true;
|
||||||
private boolean[] chartShow = new boolean[] {true, true, true, true, true};
|
private boolean[] chartShow = new boolean[] {true, true, true, true, true, true};
|
||||||
|
|
||||||
// UI elements
|
// UI elements
|
||||||
private ChartView chartView;
|
private ChartView chartView;
|
||||||
private ZoomControls zoomControls;
|
private ZoomControls zoomControls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A runnable that will remove the spinner (if any), enable/disable zoom
|
* A runnable that will enable/disable zoom controls and orange pointer as
|
||||||
* controls and orange pointer as appropriate and redraw.
|
* appropriate and redraw.
|
||||||
*/
|
*/
|
||||||
private final Runnable updateChart = new Runnable() {
|
private final Runnable updateChart = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -215,7 +215,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onNewTrackPoint(Location location) {
|
public void onNewTrackPoint(Location location) {
|
||||||
if (LocationUtils.isValidLocation(location)) {
|
if (LocationUtils.isValidLocation(location)) {
|
||||||
double[] data = new double[6];
|
double[] data = new double[ChartView.NUM_SERIES + 1];
|
||||||
fillDataPoint(location, data);
|
fillDataPoint(location, data);
|
||||||
pendingPoints.add(data);
|
pendingPoints.add(data);
|
||||||
}
|
}
|
||||||
@@ -280,7 +280,11 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
reportSpeed = speed;
|
reportSpeed = speed;
|
||||||
chartView.setReportSpeed(speed, getActivity());
|
chartView.setReportSpeed(reportSpeed);
|
||||||
|
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
||||||
|
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
||||||
|
setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed);
|
||||||
|
setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed);
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -306,8 +310,13 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
R.string.chart_show_elevation_key, PreferencesUtils.CHART_SHOW_ELEVATION_DEFAULT))) {
|
R.string.chart_show_elevation_key, PreferencesUtils.CHART_SHOW_ELEVATION_DEFAULT))) {
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
if (setSeriesEnabled(ChartView.SPEED_SERIES, PreferencesUtils.getBoolean(
|
|
||||||
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT))) {
|
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
||||||
|
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
||||||
|
if (setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed)) {
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed)) {
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
if (setSeriesEnabled(ChartView.POWER_SERIES, PreferencesUtils.getBoolean(
|
if (setSeriesEnabled(ChartView.POWER_SERIES, PreferencesUtils.getBoolean(
|
||||||
@@ -405,13 +414,14 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a location, fill in a data point, an array of double[6]. <br>
|
* Given a location, fill in a data point, an array of double[]. <br>
|
||||||
* data[0] = time/distance <br>
|
* data[0] = time/distance <br>
|
||||||
* data[1] = elevation <br>
|
* data[1] = elevation <br>
|
||||||
* data[2] = speed <br>
|
* data[2] = speed <br>
|
||||||
* data[3] = power <br>
|
* data[3] = pace <br>
|
||||||
* data[4] = cadence <br>
|
* data[4] = heart rate <br>
|
||||||
* data[5] = heart rate <br>
|
* data[5] = cadence <br>
|
||||||
|
* data[6] = power <br>
|
||||||
*
|
*
|
||||||
* @param location the location
|
* @param location the location
|
||||||
* @param data the data point to fill in, can be null
|
* @param data the data point to fill in, can be null
|
||||||
@@ -421,9 +431,10 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
double timeOrDistance = Double.NaN;
|
double timeOrDistance = Double.NaN;
|
||||||
double elevation = Double.NaN;
|
double elevation = Double.NaN;
|
||||||
double speed = Double.NaN;
|
double speed = Double.NaN;
|
||||||
double power = Double.NaN;
|
double pace = Double.NaN;
|
||||||
|
double heartRate = Double.NaN;
|
||||||
double cadence = Double.NaN;
|
double cadence = Double.NaN;
|
||||||
double heartRate = Double.NaN;
|
double power = Double.NaN;
|
||||||
|
|
||||||
// TODO: Use TripStatisticsBuilder
|
// TODO: Use TripStatisticsBuilder
|
||||||
if (chartByDistance) {
|
if (chartByDistance) {
|
||||||
@@ -460,36 +471,36 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
if (!metricUnits) {
|
if (!metricUnits) {
|
||||||
speed *= UnitConversions.KM_TO_MI;
|
speed *= UnitConversions.KM_TO_MI;
|
||||||
}
|
}
|
||||||
if (!reportSpeed) {
|
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||||
speed = speed == 0 ? 0.0 : 60.0 / speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location instanceof MyTracksLocation
|
if (location instanceof MyTracksLocation
|
||||||
&& ((MyTracksLocation) location).getSensorDataSet() != null) {
|
&& ((MyTracksLocation) location).getSensorDataSet() != null) {
|
||||||
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
||||||
if (sensorDataSet.hasPower() && sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING
|
if (sensorDataSet.hasHeartRate()
|
||||||
&& sensorDataSet.getPower().hasValue()) {
|
&& sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING
|
||||||
power = sensorDataSet.getPower().getValue();
|
&& sensorDataSet.getHeartRate().hasValue()) {
|
||||||
|
heartRate = sensorDataSet.getHeartRate().getValue();
|
||||||
}
|
}
|
||||||
if (sensorDataSet.hasCadence()
|
if (sensorDataSet.hasCadence()
|
||||||
&& sensorDataSet.getCadence().getState() == Sensor.SensorState.SENDING
|
&& sensorDataSet.getCadence().getState() == Sensor.SensorState.SENDING
|
||||||
&& sensorDataSet.getCadence().hasValue()) {
|
&& sensorDataSet.getCadence().hasValue()) {
|
||||||
cadence = sensorDataSet.getCadence().getValue();
|
cadence = sensorDataSet.getCadence().getValue();
|
||||||
}
|
}
|
||||||
if (sensorDataSet.hasHeartRate()
|
if (sensorDataSet.hasPower()
|
||||||
&& sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING
|
&& sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING
|
||||||
&& sensorDataSet.getHeartRate().hasValue()) {
|
&& sensorDataSet.getPower().hasValue()) {
|
||||||
heartRate = sensorDataSet.getHeartRate().getValue();
|
power = sensorDataSet.getPower().getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
data[0] = timeOrDistance;
|
data[0] = timeOrDistance;
|
||||||
data[1] = elevation;
|
data[1] = elevation;
|
||||||
data[2] = speed;
|
data[2] = speed;
|
||||||
data[3] = power;
|
data[3] = pace;
|
||||||
data[4] = cadence;
|
data[4] = heartRate;
|
||||||
data[5] = heartRate;
|
data[5] = cadence;
|
||||||
|
data[6] = power;
|
||||||
}
|
}
|
||||||
lastLocation = location;
|
lastLocation = location;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
|
|||||||
context.getString(R.string.description_location_bearing),
|
context.getString(R.string.description_location_bearing),
|
||||||
context.getString(R.string.description_location_accuracy),
|
context.getString(R.string.description_location_accuracy),
|
||||||
context.getString(R.string.description_location_speed),
|
context.getString(R.string.description_location_speed),
|
||||||
context.getString(R.string.description_location_time));
|
context.getString(R.string.description_time));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -143,7 +143,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
|
|||||||
context.getString(R.string.description_location_bearing),
|
context.getString(R.string.description_location_bearing),
|
||||||
context.getString(R.string.description_location_accuracy),
|
context.getString(R.string.description_location_accuracy),
|
||||||
context.getString(R.string.description_location_speed),
|
context.getString(R.string.description_location_speed),
|
||||||
context.getString(R.string.description_location_time),
|
context.getString(R.string.description_time),
|
||||||
context.getString(R.string.description_sensor_power),
|
context.getString(R.string.description_sensor_power),
|
||||||
context.getString(R.string.description_sensor_cadence),
|
context.getString(R.string.description_sensor_cadence),
|
||||||
context.getString(R.string.description_sensor_heart_rate),
|
context.getString(R.string.description_sensor_heart_rate),
|
||||||
|
|||||||
+6
-4
@@ -70,8 +70,9 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
// See http://support.google.com/fusiontables/bin/answer.py?hl=en&answer=185991
|
// See http://support.google.com/fusiontables/bin/answer.py?hl=en&answer=185991
|
||||||
private static final String MARKER_TYPE_START = "large_green";
|
private static final String MARKER_TYPE_START = "large_green";
|
||||||
private static final String MARKER_TYPE_END = "large_red";
|
private static final String MARKER_TYPE_END = "large_red";
|
||||||
private static final String MARKER_TYPE_WAYPOINT = "large_yellow";
|
private static final String MARKER_TYPE_WAYPOINT = "large_blue";
|
||||||
|
private static final String MARKER_TYPE_STATISTICS = "large_yellow";
|
||||||
|
|
||||||
private static final String TAG = SendFusionTablesAsyncTask.class.getSimpleName();
|
private static final String TAG = SendFusionTablesAsyncTask.class.getSimpleName();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
@@ -336,8 +337,9 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
// track).
|
// track).
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
Waypoint wpt = myTracksProviderUtils.createWaypoint(cursor);
|
Waypoint wpt = myTracksProviderUtils.createWaypoint(cursor);
|
||||||
if (!createNewPoint(
|
String type = wpt.getType() == Waypoint.TYPE_STATISTICS ? MARKER_TYPE_STATISTICS
|
||||||
wpt.getName(), wpt.getDescription(), wpt.getLocation(), MARKER_TYPE_WAYPOINT)) {
|
: MARKER_TYPE_WAYPOINT;
|
||||||
|
if (!createNewPoint(wpt.getName(), wpt.getDescription(), wpt.getLocation(), type)) {
|
||||||
Log.d(TAG, "Upload waypoints failed");
|
Log.d(TAG, "Upload waypoints failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -44,7 +44,8 @@ import android.widget.TextView;
|
|||||||
public class UploadResultActivity extends FragmentActivity {
|
public class UploadResultActivity extends FragmentActivity {
|
||||||
|
|
||||||
private static final String TAG = UploadResultActivity.class.getSimpleName();
|
private static final String TAG = UploadResultActivity.class.getSimpleName();
|
||||||
private static final int DIALOG_RESULT_ID = 0;
|
@VisibleForTesting
|
||||||
|
static final int DIALOG_RESULT_ID = 0;
|
||||||
|
|
||||||
private SendRequest sendRequest;
|
private SendRequest sendRequest;
|
||||||
private String shareUrl;
|
private String shareUrl;
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ public class TrackRecordingService extends Service {
|
|||||||
unregisterLocationListener();
|
unregisterLocationListener();
|
||||||
shutdownTaskExecutors();
|
shutdownTaskExecutors();
|
||||||
if (sensorManager != null) {
|
if (sensorManager != null) {
|
||||||
SensorManagerFactory.getInstance().releaseSensorManager(sensorManager);
|
SensorManagerFactory.releaseSystemSensorManager();
|
||||||
sensorManager = null;
|
sensorManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,7 +595,7 @@ public class TrackRecordingService extends Service {
|
|||||||
length = 0;
|
length = 0;
|
||||||
showNotification();
|
showNotification();
|
||||||
registerLocationListener();
|
registerLocationListener();
|
||||||
sensorManager = SensorManagerFactory.getInstance().getSensorManager(this);
|
sensorManager = SensorManagerFactory.getSystemSensorManager(this);
|
||||||
|
|
||||||
// Reset the number of auto-resume retries.
|
// Reset the number of auto-resume retries.
|
||||||
setAutoResumeTrackRetries(0);
|
setAutoResumeTrackRetries(0);
|
||||||
@@ -724,7 +724,7 @@ public class TrackRecordingService extends Service {
|
|||||||
boolean hasSensorData = sensorManager != null
|
boolean hasSensorData = sensorManager != null
|
||||||
&& sensorManager.isEnabled()
|
&& sensorManager.isEnabled()
|
||||||
&& sensorManager.getSensorDataSet() != null
|
&& sensorManager.getSensorDataSet() != null
|
||||||
&& sensorManager.isDataValid();
|
&& sensorManager.isSensorDataSetValid();
|
||||||
|
|
||||||
// If the user has been stationary for two recording just record the first
|
// 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
|
// two and ignore the rest. This code will only have an effect if the
|
||||||
@@ -823,7 +823,7 @@ public class TrackRecordingService extends Service {
|
|||||||
Location locationToInsert = location;
|
Location locationToInsert = location;
|
||||||
if (sensorManager != null && sensorManager.isEnabled()) {
|
if (sensorManager != null && sensorManager.isEnabled()) {
|
||||||
SensorDataSet sd = sensorManager.getSensorDataSet();
|
SensorDataSet sd = sensorManager.getSensorDataSet();
|
||||||
if (sd != null && sensorManager.isDataValid()) {
|
if (sd != null && sensorManager.isSensorDataSetValid()) {
|
||||||
locationToInsert = new MyTracksLocation(location, sd);
|
locationToInsert = new MyTracksLocation(location, sd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1042,7 +1042,7 @@ public class TrackRecordingService extends Service {
|
|||||||
PreferencesUtils.setLong(this, R.string.recording_track_id_key, recordingTrackId);
|
PreferencesUtils.setLong(this, R.string.recording_track_id_key, recordingTrackId);
|
||||||
|
|
||||||
if (sensorManager != null) {
|
if (sensorManager != null) {
|
||||||
SensorManagerFactory.getInstance().releaseSensorManager(sensorManager);
|
SensorManagerFactory.releaseSystemSensorManager();
|
||||||
sensorManager = null;
|
sensorManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+129
-247
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
package com.google.android.apps.mytracks.services.sensors;
|
package com.google.android.apps.mytracks.services.sensors;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.content.Sensor;
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
|
import com.google.android.apps.mytracks.content.Sensor.SensorState;
|
||||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
@@ -30,382 +30,264 @@ import android.util.Log;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class does all the work for setting up and managing Bluetooth
|
* Manages bluetooth connection. It has a thread for connecting with a bluetooth
|
||||||
* connections with other devices. It has a thread that listens for incoming
|
* device and a thread for performing data transmission when connected.
|
||||||
* connections, a thread for connecting with a device, and a thread for
|
*
|
||||||
* performing data transmissions when connected.
|
|
||||||
*
|
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class BluetoothConnectionManager {
|
public class BluetoothConnectionManager {
|
||||||
|
|
||||||
// Unique Bluetooth UUID for My Tracks
|
// My Tracks UUID
|
||||||
public static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
public static final UUID MY_TRACKS_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||||
|
|
||||||
private MessageParser parser;
|
// Message types sent to hander
|
||||||
|
public static final int MESSAGE_DEVICE_NAME = 1;
|
||||||
|
public static final int MESSAGE_READ = 2;
|
||||||
|
|
||||||
// Member fields
|
// Key for storing the device name
|
||||||
private final BluetoothAdapter adapter;
|
public static final String KEY_DEVICE_NAME = "device_name";
|
||||||
|
|
||||||
|
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||||
|
|
||||||
|
private final BluetoothAdapter bluetoothAdapter;
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
|
private final MessageParser messageParser;
|
||||||
|
private SensorState sensorState;
|
||||||
|
|
||||||
private ConnectThread connectThread;
|
private ConnectThread connectThread;
|
||||||
private ConnectedThread connectedThread;
|
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.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param handler A Handler to send messages back to the UI Activity
|
* @param bluetoothAdapter the bluetooth adapter
|
||||||
* @param parser A message parser
|
* @param handler a hander for sending messages back to the UI activity
|
||||||
|
* @param messageParser a message parser
|
||||||
*/
|
*/
|
||||||
public BluetoothConnectionManager(Handler handler, MessageParser parser) {
|
public BluetoothConnectionManager(
|
||||||
this.adapter = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter bluetoothAdapter, Handler handler, MessageParser messageParser) {
|
||||||
this.state = Sensor.SensorState.NONE;
|
this.bluetoothAdapter = bluetoothAdapter;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.parser = parser;
|
this.messageParser = messageParser;
|
||||||
|
this.sensorState = SensorState.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current state of the sensor connection
|
* Gets the sensor state.
|
||||||
*
|
|
||||||
* @param state An integer defining the current connection state
|
|
||||||
*/
|
*/
|
||||||
private synchronized void setState(Sensor.SensorState state) {
|
public synchronized SensorState getSensorState() {
|
||||||
// TODO pretty print this.
|
return sensorState;
|
||||||
Log.d(Constants.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.
|
* Sets the sensor state.
|
||||||
|
*
|
||||||
|
* @param sensorState the sensor state
|
||||||
*/
|
*/
|
||||||
public synchronized Sensor.SensorState getState() {
|
private synchronized void setState(Sensor.SensorState sensorState) {
|
||||||
return state;
|
this.sensorState = sensorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the sensor service. Specifically start AcceptThread to begin a session
|
* Resets the bluetooth connection manager.
|
||||||
* in listening (server) mode. Called by the Activity onResume()
|
|
||||||
*/
|
*/
|
||||||
public synchronized void start() {
|
public synchronized void reset() {
|
||||||
Log.d(Constants.TAG, "BluetoothConnectionManager.start()");
|
cancelThreads();
|
||||||
|
|
||||||
// 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);
|
setState(Sensor.SensorState.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the ConnectThread to initiate a connection to a remote device.
|
* Cancels all the threads.
|
||||||
*
|
|
||||||
* @param device The BluetoothDevice to connect
|
|
||||||
*/
|
*/
|
||||||
public synchronized void connect(BluetoothDevice device) {
|
private void cancelThreads() {
|
||||||
Log.d(Constants.TAG, "connect to: " + device);
|
if (connectThread != null) {
|
||||||
|
connectThread.cancel();
|
||||||
// Cancel any thread attempting to make a connection
|
connectThread = null;
|
||||||
if (state == Sensor.SensorState.CONNECTING) {
|
|
||||||
if (connectThread != null) {
|
|
||||||
connectThread.cancel();
|
|
||||||
connectThread = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel any thread currently running a connection
|
|
||||||
if (connectedThread != null) {
|
if (connectedThread != null) {
|
||||||
connectedThread.cancel();
|
connectedThread.cancel();
|
||||||
connectedThread = null;
|
connectedThread = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start the thread to connect with the given device
|
/**
|
||||||
connectThread = new ConnectThread(device);
|
* Connects to a bluetooth device.
|
||||||
|
*
|
||||||
|
* @param bluetoothDevice the bluetooth device
|
||||||
|
*/
|
||||||
|
public synchronized void connect(BluetoothDevice bluetoothDevice) {
|
||||||
|
Log.d(TAG, "connect to: " + bluetoothDevice);
|
||||||
|
cancelThreads();
|
||||||
|
|
||||||
|
connectThread = new ConnectThread(bluetoothDevice);
|
||||||
connectThread.start();
|
connectThread.start();
|
||||||
setState(Sensor.SensorState.CONNECTING);
|
setState(Sensor.SensorState.CONNECTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the ConnectedThread to begin managing a Bluetooth connection
|
* Starts the ConnectedThread to read data.
|
||||||
*
|
*
|
||||||
* @param socket The BluetoothSocket on which the connection was made
|
* @param bluetoothSocket the bluetooth socket
|
||||||
* @param device The BluetoothDevice that has been connected
|
* @param bluetoothDevice the bluetooth device
|
||||||
*/
|
*/
|
||||||
public synchronized void connected(BluetoothSocket socket,
|
private synchronized void connected(
|
||||||
BluetoothDevice device) {
|
BluetoothSocket bluetoothSocket, BluetoothDevice bluetoothDevice) {
|
||||||
Log.d(Constants.TAG, "connected");
|
cancelThreads();
|
||||||
|
|
||||||
// Cancel the thread that completed the connection
|
connectedThread = new ConnectedThread(bluetoothSocket);
|
||||||
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();
|
connectedThread.start();
|
||||||
|
|
||||||
// Send the name of the connected device back to the UI Activity
|
// Send the device name to the handler
|
||||||
Message msg = handler.obtainMessage(MESSAGE_DEVICE_NAME);
|
Message message = handler.obtainMessage(MESSAGE_DEVICE_NAME);
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString(DEVICE_NAME, device.getName());
|
bundle.putString(KEY_DEVICE_NAME, bluetoothDevice.getName());
|
||||||
msg.setData(bundle);
|
message.setData(bundle);
|
||||||
handler.sendMessage(msg);
|
handler.sendMessage(message);
|
||||||
|
|
||||||
setState(Sensor.SensorState.CONNECTED);
|
setState(Sensor.SensorState.CONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop all threads
|
* A thread to connect to a bluetooth device.
|
||||||
*/
|
|
||||||
public synchronized void stop() {
|
|
||||||
Log.d(Constants.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(Constants.TAG, "Bluetooth connection failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate that the connection was lost and notify the UI Activity.
|
|
||||||
*/
|
|
||||||
private void connectionLost() {
|
|
||||||
setState(Sensor.SensorState.DISCONNECTED);
|
|
||||||
Log.i(Constants.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 class ConnectThread extends Thread {
|
||||||
private final BluetoothSocket socket;
|
private final BluetoothSocket bluetoothSocket;
|
||||||
private final BluetoothDevice device;
|
private final BluetoothDevice bluetoothDevice;
|
||||||
|
|
||||||
public ConnectThread(BluetoothDevice device) {
|
public ConnectThread(BluetoothDevice device) {
|
||||||
setName("ConnectThread-" + device.getName());
|
setName("ConnectThread-" + device.getName());
|
||||||
this.device = device;
|
this.bluetoothDevice = device;
|
||||||
BluetoothSocket tmp = null;
|
BluetoothSocket tmp = null;
|
||||||
|
|
||||||
// Get a BluetoothSocket for a connection with the
|
|
||||||
// given BluetoothDevice
|
|
||||||
try {
|
try {
|
||||||
tmp = ApiAdapterFactory.getApiAdapter().getBluetoothSocket(device);
|
tmp = ApiAdapterFactory.getApiAdapter().getBluetoothSocket(device);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "create() failed", e);
|
Log.e(TAG, "Unable to get blueooth socket.", e);
|
||||||
}
|
}
|
||||||
socket = tmp;
|
bluetoothSocket = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.d(Constants.TAG, "BEGIN mConnectThread");
|
if (bluetoothAdapter == null) {
|
||||||
|
BluetoothConnectionManager.this.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Cancel discovery to prevent slow down
|
||||||
|
bluetoothAdapter.cancelDiscovery();
|
||||||
|
|
||||||
// Always cancel discovery because it will slow down a connection
|
|
||||||
adapter.cancelDiscovery();
|
|
||||||
|
|
||||||
// Make a connection to the BluetoothSocket
|
|
||||||
try {
|
try {
|
||||||
// This is a blocking call and will only return on a
|
bluetoothSocket.connect();
|
||||||
// successful connection or an exception
|
} catch (IOException connectException) {
|
||||||
socket.connect();
|
Log.i(TAG, "Unable to connect.", connectException);
|
||||||
} catch (IOException e) {
|
setState(Sensor.SensorState.DISCONNECTED);
|
||||||
connectionFailed();
|
|
||||||
// Close the socket
|
|
||||||
try {
|
try {
|
||||||
socket.close();
|
bluetoothSocket.close();
|
||||||
} catch (IOException e2) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG,
|
Log.e(TAG, "Unable to close blueooth socket.", e);
|
||||||
"unable to close() socket during connection failure", e2);
|
|
||||||
}
|
}
|
||||||
// Start the service over to restart listening mode
|
// Reset the bluetooth connection manager
|
||||||
BluetoothConnectionManager.this.start();
|
BluetoothConnectionManager.this.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the ConnectThread because we're done
|
// Reset the ConnectThread since we are done
|
||||||
synchronized (BluetoothConnectionManager.this) {
|
synchronized (BluetoothConnectionManager.this) {
|
||||||
connectThread = null;
|
connectThread = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the connected thread
|
// Start the connected thread
|
||||||
connected(socket, device);
|
connected(bluetoothSocket, bluetoothDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels this thread.
|
||||||
|
*/
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
try {
|
try {
|
||||||
socket.close();
|
bluetoothSocket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "close() of connect socket failed", e);
|
Log.e(TAG, "Unable to close bluetooth socket.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This thread runs during a connection with a remote device. It handles all
|
* This thread handles data transmission when connected.
|
||||||
* incoming and outgoing transmissions.
|
|
||||||
*/
|
*/
|
||||||
private class ConnectedThread extends Thread {
|
private class ConnectedThread extends Thread {
|
||||||
private final BluetoothSocket btSocket;
|
private final BluetoothSocket bluetoothSSocket;
|
||||||
private final InputStream mmInStream;
|
private final InputStream inputStream;
|
||||||
private final OutputStream mmOutStream;
|
|
||||||
|
|
||||||
public ConnectedThread(BluetoothSocket socket) {
|
public ConnectedThread(BluetoothSocket bluetoothSocket) {
|
||||||
Log.d(Constants.TAG, "create ConnectedThread");
|
this.bluetoothSSocket = bluetoothSocket;
|
||||||
btSocket = socket;
|
InputStream tmp = null;
|
||||||
InputStream tmpIn = null;
|
|
||||||
OutputStream tmpOut = null;
|
|
||||||
|
|
||||||
// Get the BluetoothSocket input and output streams
|
|
||||||
try {
|
try {
|
||||||
tmpIn = socket.getInputStream();
|
tmp = bluetoothSocket.getInputStream();
|
||||||
tmpOut = socket.getOutputStream();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "temp sockets not created", e);
|
Log.e(TAG, "Unable to get input stream.", e);
|
||||||
}
|
}
|
||||||
|
inputStream = tmp;
|
||||||
mmInStream = tmpIn;
|
|
||||||
mmOutStream = tmpOut;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.i(Constants.TAG, "BEGIN mConnectedThread");
|
byte[] buffer = new byte[messageParser.getFrameSize()];
|
||||||
byte[] buffer = new byte[parser.getFrameSize()];
|
int bytes; // bytes read
|
||||||
int bytes;
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
// Keep listening to the InputStream while connected
|
// Keep listening to the inputStream while connected
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
// Read from the InputStream
|
// Read from the inputStream
|
||||||
bytes = mmInStream.read(buffer, offset, parser.getFrameSize() - offset);
|
bytes = inputStream.read(buffer, offset, messageParser.getFrameSize() - offset);
|
||||||
|
|
||||||
if (bytes < 0) {
|
if (bytes == -1) { throw new IOException("EOF reached."); }
|
||||||
throw new IOException("EOF reached");
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += bytes;
|
offset += bytes;
|
||||||
|
|
||||||
if (offset != parser.getFrameSize()) {
|
if (offset != messageParser.getFrameSize()) {
|
||||||
// partial frame received, call read() again to receive the rest
|
// Partial frame received. Call read again to receive the rest.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if its a valid frame
|
if (!messageParser.isValid(buffer)) {
|
||||||
if (!parser.isValid(buffer)) {
|
int index = messageParser.findNextAlignment(buffer);
|
||||||
int index = parser.findNextAlignment(buffer);
|
if (index == -1) {
|
||||||
if (index > 0) {
|
Log.w(TAG, "Could not find any valid data. Drop data.");
|
||||||
// re-align
|
offset = 0;
|
||||||
offset = parser.getFrameSize() - index;
|
|
||||||
System.arraycopy(buffer, index, buffer, 0, offset);
|
|
||||||
Log.w(Constants.TAG, "Misaligned data, found new message at " +
|
|
||||||
index + " recovering...");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Log.w(Constants.TAG, "Could not find valid data, dropping data");
|
Log.w(TAG, "Misaligned data. Found new message at " + index + ". Recovering...");
|
||||||
offset = 0;
|
offset = messageParser.getFrameSize() - index;
|
||||||
|
System.arraycopy(buffer, index, buffer, 0, offset);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
// Send copy of the obtained bytes to the UI Activity.
|
// Send a copy of the obtained bytes to the handler to avoid memory
|
||||||
// Avoids memory inconsistency issues.
|
// inconsistency issues
|
||||||
handler.obtainMessage(MESSAGE_READ, bytes, -1, buffer.clone())
|
handler.obtainMessage(MESSAGE_READ, bytes, -1, buffer.clone()).sendToTarget();
|
||||||
.sendToTarget();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "disconnected", e);
|
Log.i(TAG, "Bluetooth connection lost.", e);
|
||||||
connectionLost();
|
setState(Sensor.SensorState.DISCONNECTED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write to the connected OutStream.
|
* Cancels this thread.
|
||||||
*
|
|
||||||
* @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(Constants.TAG, "Exception during write", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
try {
|
try {
|
||||||
btSocket.close();
|
bluetoothSSocket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "close() of connect socket failed", e);
|
Log.e(TAG, "Unable to close bluetooth socket.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+86
-124
@@ -13,12 +13,11 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.android.apps.mytracks.services.sensors;
|
package com.google.android.apps.mytracks.services.sensors;
|
||||||
|
|
||||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||||
|
import com.google.android.apps.mytracks.content.Sensor.SensorState;
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.content.Sensor;
|
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
@@ -34,66 +33,31 @@ import android.widget.Toast;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage the connection to a bluetooth sensor.
|
* Bluetooth sensor manager.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class BluetoothSensorManager extends SensorManager {
|
public class BluetoothSensorManager extends SensorManager {
|
||||||
|
|
||||||
// Local Bluetooth adapter
|
|
||||||
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
|
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
|
||||||
|
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||||
// 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;
|
|
||||||
// If BT is not available or not enabled quit.
|
|
||||||
if (!isEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupSensor();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSensor() {
|
|
||||||
Log.d(Constants.TAG, "setupSensor()");
|
|
||||||
|
|
||||||
// Initialize the BluetoothSensorAdapter to perform bluetooth connections.
|
|
||||||
connectionManager = new BluetoothConnectionManager(messageHandler, parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Code for assigning the local bluetooth adapter
|
* Gets the default bluetooth adapter.
|
||||||
*
|
|
||||||
* @return The default bluetooth adapter, if one is available, NULL if it isn't.
|
|
||||||
*/
|
*/
|
||||||
private static BluetoothAdapter getDefaultBluetoothAdapter() {
|
private static BluetoothAdapter getDefaultBluetoothAdapter() {
|
||||||
// Check if the calling thread is the main application thread,
|
// If from the main application thread, return directly
|
||||||
// if it is, do it directly.
|
|
||||||
if (Thread.currentThread().equals(Looper.getMainLooper().getThread())) {
|
if (Thread.currentThread().equals(Looper.getMainLooper().getThread())) {
|
||||||
return BluetoothAdapter.getDefaultAdapter();
|
return BluetoothAdapter.getDefaultAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the calling thread, isn't the main application thread,
|
// Get the default adapter from the main application thread
|
||||||
// then get the main application thread to return the default adapter.
|
|
||||||
final ArrayList<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>(1);
|
final ArrayList<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>(1);
|
||||||
final Object mutex = new Object();
|
final Object mutex = new Object();
|
||||||
|
|
||||||
Handler handler = new Handler(Looper.getMainLooper());
|
Handler handler = new Handler(Looper.getMainLooper());
|
||||||
handler.post(new Runnable() {
|
handler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
adapters.add(BluetoothAdapter.getDefaultAdapter());
|
adapters.add(BluetoothAdapter.getDefaultAdapter());
|
||||||
synchronized (mutex) {
|
synchronized (mutex) {
|
||||||
@@ -101,7 +65,7 @@ public class BluetoothSensorManager extends SensorManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
while (adapters.isEmpty()) {
|
while (adapters.isEmpty()) {
|
||||||
synchronized (mutex) {
|
synchronized (mutex) {
|
||||||
try {
|
try {
|
||||||
@@ -111,106 +75,104 @@ public class BluetoothSensorManager extends SensorManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adapters.get(0) == null) {
|
if (adapters.get(0) == null) {
|
||||||
Log.w(TAG, "No bluetooth adapter found!");
|
Log.w(TAG, "No bluetooth adapter found.");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return adapters.get(0);
|
return adapters.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final MessageParser messageParser;
|
||||||
|
private final BluetoothConnectionManager bluetoothConnectionManager;
|
||||||
|
private SensorDataSet sensorDataSet = null;
|
||||||
|
|
||||||
|
// Handler that gets information back from the bluetoothConnectionManager
|
||||||
|
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message message) {
|
||||||
|
switch (message.what) {
|
||||||
|
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
|
||||||
|
String deviceName = message.getData()
|
||||||
|
.getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
|
||||||
|
Toast.makeText(context,
|
||||||
|
context.getString(R.string.settings_sensor_bluetooth_connected, deviceName),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case BluetoothConnectionManager.MESSAGE_READ:
|
||||||
|
try {
|
||||||
|
byte[] readBuf = (byte[]) message.obj;
|
||||||
|
sensorDataSet = messageParser.parseBuffer(readBuf);
|
||||||
|
Log.d(TAG, "MESSAGE_READ: " + sensorDataSet);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
sensorDataSet = null;
|
||||||
|
Log.i(TAG, "Unexpected exception on read", e);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
sensorDataSet = null;
|
||||||
|
Log.i(TAG, "Unexpected exception on read.", e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @param messageParser the message parser
|
||||||
|
*/
|
||||||
|
public BluetoothSensorManager(Context context, MessageParser messageParser) {
|
||||||
|
this.context = context;
|
||||||
|
this.messageParser = messageParser;
|
||||||
|
bluetoothConnectionManager = new BluetoothConnectionManager(
|
||||||
|
bluetoothAdapter, messageHandler, messageParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupChannel() {
|
@Override
|
||||||
if (!isEnabled() || connectionManager == null) {
|
protected void setUpChannel() {
|
||||||
Log.w(Constants.TAG, "Disabled manager onStartTrack");
|
if (!isEnabled()) {
|
||||||
|
Log.w(TAG, "Bluetooth not enabled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String address = PreferencesUtils.getString(
|
String address = PreferencesUtils.getString(
|
||||||
context, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
context, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||||
if (PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(address)) {
|
if (PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(address)) {
|
||||||
|
Log.w(TAG, "No blueooth address.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.w(Constants.TAG, "Connecting to bluetooth sensor: " + address);
|
Log.w(TAG, "Connecting to bluetooth address: " + address);
|
||||||
// Get the BluetoothDevice object
|
|
||||||
BluetoothDevice device;
|
BluetoothDevice device;
|
||||||
try {
|
try {
|
||||||
device = bluetoothAdapter.getRemoteDevice(address);
|
device = bluetoothAdapter.getRemoteDevice(address);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
Log.d(Constants.TAG, "Invalid address " + address, e);
|
Log.d(TAG, "Unable to get remote device for: " + address, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Attempt to connect to the device
|
bluetoothConnectionManager.connect(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(Constants.TAG, "Disabled manager onStartTrack");
|
|
||||||
connectionManager.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy() {
|
@Override
|
||||||
// Stop the Bluetooth sensor services
|
protected void tearDownChannel() {
|
||||||
if (connectionManager != null) {
|
bluetoothConnectionManager.reset();
|
||||||
connectionManager.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sensor.SensorDataSet getSensorDataSet() {
|
@Override
|
||||||
|
public SensorState getSensorState() {
|
||||||
|
return bluetoothConnectionManager.getSensorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SensorDataSet getSensorDataSet() {
|
||||||
return sensorDataSet;
|
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(Looper.getMainLooper()) {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case BluetoothConnectionManager.MESSAGE_STATE_CHANGE:
|
|
||||||
// TODO should we update the SensorManager state var?
|
|
||||||
Log.i(Constants.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(Constants.TAG, "MESSAGE_READ: " + sensorDataSet.toString());
|
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
sensorDataSet = null;
|
|
||||||
Log.i(Constants.TAG,
|
|
||||||
"Got bad sensor data: " + new String(readBuf, 0, readBuf.length),
|
|
||||||
iae);
|
|
||||||
} catch (RuntimeException re) {
|
|
||||||
sensorDataSet = null;
|
|
||||||
Log.i(Constants.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -13,14 +13,16 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.android.apps.mytracks.services.sensors;
|
package com.google.android.apps.mytracks.services.sensors;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PolarSensorManager - A sensor manager for Polar heart rate monitors.
|
* A Polar heart rate sensor manager.
|
||||||
|
*
|
||||||
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class PolarSensorManager extends BluetoothSensorManager {
|
public class PolarSensorManager extends BluetoothSensorManager {
|
||||||
public PolarSensorManager(Context context) {
|
public PolarSensorManager(Context context) {
|
||||||
super(context, new PolarMessageParser());
|
super(context, new PolarMessageParser());
|
||||||
|
|||||||
@@ -13,126 +13,118 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.android.apps.mytracks.services.sensors;
|
package com.google.android.apps.mytracks.services.sensors;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||||
|
import com.google.android.apps.mytracks.content.Sensor.SensorState;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.content.Sensor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage the connection to a sensor.
|
* Manage the connection to a sensor.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public abstract class SensorManager {
|
public abstract class SensorManager {
|
||||||
|
|
||||||
/**
|
private static final String TAG = SensorManager.class.getSimpleName();
|
||||||
* The maximum age where the data is considered valid.
|
private static final long MAX_SENSOR_DATE_SET_AGE = 5000;
|
||||||
*/
|
private static final long MAX_SENSOR_STATE_AGE = 30000;
|
||||||
public static final long MAX_AGE = 5000;
|
private static final int RETRY_PERIOD = 30000;
|
||||||
|
|
||||||
|
private SensorState sensorState = SensorState.NONE;
|
||||||
|
private long sensorStateTimestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time to wait after a time out to retry.
|
* A time task to check sensor connection.
|
||||||
*/
|
*/
|
||||||
public static final int RETRY_PERIOD = 30000;
|
private TimerTask checkSensorConnectionTimeTask = new TimerTask() {
|
||||||
|
@Override
|
||||||
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() {
|
public void run() {
|
||||||
Log.i(Constants.TAG,
|
|
||||||
"SensorManager state: " + getSensorState());
|
|
||||||
switch (getSensorState()) {
|
switch (getSensorState()) {
|
||||||
case CONNECTING:
|
case CONNECTING:
|
||||||
long age = System.currentTimeMillis() - getSensorStateTimestamp();
|
if (System.currentTimeMillis() - sensorStateTimestamp > MAX_SENSOR_STATE_AGE) {
|
||||||
if (age > 2 * RETRY_PERIOD) {
|
Log.i(TAG, "Retry setUpChannel");
|
||||||
Log.i(Constants.TAG, "Retrying connecting SensorManager.");
|
setUpChannel();
|
||||||
setupChannel();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case NONE:
|
||||||
case DISCONNECTED:
|
case DISCONNECTED:
|
||||||
Log.i(Constants.TAG,
|
setUpChannel();
|
||||||
"Re-registering disconnected SensoManager.");
|
break;
|
||||||
setupChannel();
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* This timer invokes periodically the checkLocationListener timer task.
|
|
||||||
*/
|
|
||||||
private final Timer timer = new Timer();
|
private final Timer timer = new Timer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the sensor that this manages enabled.
|
* Returns true if the sensor is enabled.
|
||||||
* @return true if the sensor is enabled
|
|
||||||
*/
|
*/
|
||||||
public abstract boolean isEnabled();
|
public abstract boolean isEnabled();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when my tracks starts recording a new track.
|
* Sets up the sensor channel.
|
||||||
* This is the place to open connections to the sensor.
|
|
||||||
*/
|
*/
|
||||||
public void onStartTrack() {
|
protected abstract void setUpChannel();
|
||||||
setupChannel();
|
|
||||||
timer.schedule(checkSensorManager, RETRY_PERIOD, RETRY_PERIOD);
|
/**
|
||||||
|
* Tears down the sensor channel.
|
||||||
|
*/
|
||||||
|
protected abstract void tearDownChannel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sensor data set.
|
||||||
|
*/
|
||||||
|
public abstract SensorDataSet getSensorDataSet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the sensor.
|
||||||
|
*/
|
||||||
|
public void startSensor() {
|
||||||
|
setUpChannel();
|
||||||
|
timer.schedule(checkSensorConnectionTimeTask, RETRY_PERIOD, RETRY_PERIOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to set up any necessary connections to underlying
|
* Stops the sensor.
|
||||||
* sensor hardware.
|
|
||||||
*/
|
*/
|
||||||
protected abstract void setupChannel();
|
public void stopSensor() {
|
||||||
|
|
||||||
public void shutdown() {
|
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
onDestroy();
|
tearDownChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when my tracks stops recording.
|
* Sets the sensor state.
|
||||||
* This is the place to shutdown any open connections.
|
*
|
||||||
|
* @param sensorState the sensor state
|
||||||
*/
|
*/
|
||||||
public abstract void onDestroy();
|
public void setSensorState(SensorState sensorState) {
|
||||||
|
sensorStateTimestamp = System.currentTimeMillis();
|
||||||
/**
|
|
||||||
* 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;
|
this.sensorState = sensorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current sensor state.
|
* Gets the sensor state.
|
||||||
* @return The current sensor state.
|
|
||||||
*/
|
*/
|
||||||
public Sensor.SensorState getSensorState() {
|
public SensorState getSensorState() {
|
||||||
return sensorState;
|
return sensorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSensorStateTimestamp() {
|
|
||||||
return sensorStateTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if the data is recent enough to be considered valid.
|
* Returns true if the sensor data set is valid.
|
||||||
*/
|
*/
|
||||||
public boolean isDataValid() {
|
public boolean isSensorDataSetValid() {
|
||||||
return (System.currentTimeMillis() - getSensorDataSet().getCreationTime()) < MAX_AGE;
|
SensorDataSet sensorDataSet = getSensorDataSet();
|
||||||
|
if (sensorDataSet == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (System.currentTimeMillis() - sensorDataSet.getCreationTime()) < MAX_SENSOR_DATE_SET_AGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-62
@@ -13,101 +13,99 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.android.apps.mytracks.services.sensors;
|
package com.google.android.apps.mytracks.services.sensors;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.services.sensors.ant.AntDirectSensorManager;
|
import com.google.android.apps.mytracks.services.sensors.ant.AntDirectSensorManager;
|
||||||
import com.google.android.apps.mytracks.services.sensors.ant.AntSrmBridgeSensorManager;
|
import com.google.android.apps.mytracks.services.sensors.ant.AntSrmBridgeSensorManager;
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory of SensorManagers.
|
* A factory of {@link SensorManager}.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class SensorManagerFactory {
|
public class SensorManagerFactory {
|
||||||
|
|
||||||
private String activeSensorType;
|
private static SensorManager systemSensorManager = null;
|
||||||
private SensorManager activeSensorManager;
|
private static SensorManager tempSensorManager = null;
|
||||||
private int refCount;
|
|
||||||
|
|
||||||
private static SensorManagerFactory instance = new SensorManagerFactory();
|
private SensorManagerFactory() {}
|
||||||
|
|
||||||
private SensorManagerFactory() {
|
/**
|
||||||
|
* Gets the system sensor manager.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
*/
|
||||||
|
public static SensorManager getSystemSensorManager(Context context) {
|
||||||
|
releaseTempSensorManager();
|
||||||
|
releaseSystemSensorManager();
|
||||||
|
systemSensorManager = getSensorManager(context);
|
||||||
|
if (systemSensorManager != null) {
|
||||||
|
systemSensorManager.startSensor();
|
||||||
|
}
|
||||||
|
return systemSensorManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the factory instance.
|
* Releases the system sensor manager.
|
||||||
*/
|
*/
|
||||||
public static SensorManagerFactory getInstance() {
|
public static void releaseSystemSensorManager() {
|
||||||
return instance;
|
if (systemSensorManager != null) {
|
||||||
|
systemSensorManager.stopSensor();
|
||||||
|
}
|
||||||
|
systemSensorManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get and start a new sensor manager.
|
* Gets the temp sensor manager.
|
||||||
* @param context Context to fetch system preferences.
|
*
|
||||||
* @return The sensor manager that corresponds to the sensor type setting.
|
* @param context
|
||||||
*/
|
*/
|
||||||
public SensorManager getSensorManager(Context context) {
|
public static SensorManager getTempSensorManager(Context context) {
|
||||||
context = context.getApplicationContext();
|
releaseTempSensorManager();
|
||||||
|
if (systemSensorManager != null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
tempSensorManager = getSensorManager(context);
|
||||||
|
if (tempSensorManager != null) {
|
||||||
|
tempSensorManager.startSensor();
|
||||||
|
}
|
||||||
|
return tempSensorManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the temp sensor manager.
|
||||||
|
*/
|
||||||
|
public static void releaseTempSensorManager() {
|
||||||
|
if (tempSensorManager != null) {
|
||||||
|
tempSensorManager.stopSensor();
|
||||||
|
}
|
||||||
|
tempSensorManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sensor manager.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
*/
|
||||||
|
private static SensorManager getSensorManager(Context context) {
|
||||||
String sensorTypeValueNone = context.getString(R.string.sensor_type_value_none);
|
String sensorTypeValueNone = context.getString(R.string.sensor_type_value_none);
|
||||||
String sensorType = PreferencesUtils.getString(
|
String sensorType = PreferencesUtils.getString(
|
||||||
context, R.string.sensor_type_key, sensorTypeValueNone);
|
context, R.string.sensor_type_key, sensorTypeValueNone);
|
||||||
Log.i(Constants.TAG, "Creating sensor of type: " + sensorType);
|
|
||||||
|
|
||||||
if (sensorTypeValueNone.equals(sensorType)) {
|
|
||||||
reset();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (sensorType.equals(activeSensorType)) {
|
|
||||||
Log.i(Constants.TAG, "Returning existing sensor manager.");
|
|
||||||
refCount++;
|
|
||||||
return activeSensorManager;
|
|
||||||
}
|
|
||||||
reset();
|
|
||||||
|
|
||||||
if (sensorType.equals(context.getString(R.string.sensor_type_value_ant))) {
|
if (sensorType.equals(context.getString(R.string.sensor_type_value_ant))) {
|
||||||
activeSensorManager = new AntDirectSensorManager(context);
|
return new AntDirectSensorManager(context);
|
||||||
} else if (sensorType.equals(context.getString(R.string.sensor_type_value_srm_ant_bridge))) {
|
} else if (sensorType.equals(context.getString(R.string.sensor_type_value_srm_ant_bridge))) {
|
||||||
activeSensorManager = new AntSrmBridgeSensorManager(context);
|
return new AntSrmBridgeSensorManager(context);
|
||||||
} else if (sensorType.equals(context.getString(R.string.sensor_type_value_zephyr))) {
|
} else if (sensorType.equals(context.getString(R.string.sensor_type_value_zephyr))) {
|
||||||
activeSensorManager = new ZephyrSensorManager(context);
|
return new ZephyrSensorManager(context);
|
||||||
} else if (sensorType.equals(context.getString(R.string.sensor_type_value_polar))) {
|
} else if (sensorType.equals(context.getString(R.string.sensor_type_value_polar))) {
|
||||||
activeSensorManager = new PolarSensorManager(context);
|
return new PolarSensorManager(context);
|
||||||
} else {
|
|
||||||
Log.w(Constants.TAG, "Unable to find sensor type: " + sensorType);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
activeSensorType = sensorType;
|
return null;
|
||||||
refCount = 1;
|
|
||||||
activeSensorManager.onStartTrack();
|
|
||||||
return activeSensorManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finish using a sensor manager.
|
|
||||||
*/
|
|
||||||
public void releaseSensorManager(SensorManager sensorManager) {
|
|
||||||
Log.i(Constants.TAG, "releaseSensorManager: " + activeSensorType + " " + refCount);
|
|
||||||
if (sensorManager != activeSensorManager) {
|
|
||||||
Log.e(Constants.TAG, "invalid parameter to releaseSensorManager");
|
|
||||||
}
|
|
||||||
if (--refCount > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reset() {
|
|
||||||
activeSensorType = null;
|
|
||||||
if (activeSensorManager != null) {
|
|
||||||
activeSensorManager.shutdown();
|
|
||||||
}
|
|
||||||
activeSensorManager = null;
|
|
||||||
refCount = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -13,14 +13,19 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.android.apps.mytracks.services.sensors;
|
package com.google.android.apps.mytracks.services.sensors;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Zephyr heart rate sensor manager.
|
||||||
|
*
|
||||||
|
* @author Jimmy Shih
|
||||||
|
*/
|
||||||
public class ZephyrSensorManager extends BluetoothSensorManager {
|
public class ZephyrSensorManager extends BluetoothSensorManager {
|
||||||
|
|
||||||
public ZephyrSensorManager(Context context) {
|
public ZephyrSensorManager(Context context) {
|
||||||
super(context, new ZephyrMessageParser());
|
super(context, new ZephyrMessageParser());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -133,7 +133,7 @@ public abstract class AntSensorManager extends SensorManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
protected void tearDownChannel() {
|
||||||
Log.i(TAG, "destroying AntSensorManager");
|
Log.i(TAG, "destroying AntSensorManager");
|
||||||
|
|
||||||
cleanAntInterface();
|
cleanAntInterface();
|
||||||
@@ -161,7 +161,7 @@ public abstract class AntSensorManager extends SensorManager {
|
|||||||
* {@link #setupAntSensorChannels}.
|
* {@link #setupAntSensorChannels}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected final void setupChannel() {
|
protected void setUpChannel() {
|
||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,11 +71,9 @@ public class StatsSettingsActivity extends AbstractSettingsActivity {
|
|||||||
getString(R.string.report_speed_key));
|
getString(R.string.report_speed_key));
|
||||||
boolean metric = PreferencesUtils.getBoolean(
|
boolean metric = PreferencesUtils.getBoolean(
|
||||||
this, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
this, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
||||||
reportSpeedCheckBoxPreference.setSummaryOn(
|
reportSpeedCheckBoxPreference.setSummaryOn(metric ? getString(R.string.description_speed_metric)
|
||||||
metric ? getString(R.string.settings_stats_rate_speed_metric)
|
: getString(R.string.description_speed_imperial));
|
||||||
: getString(R.string.settings_stats_rate_speed_imperial));
|
reportSpeedCheckBoxPreference.setSummaryOff(metric ? getString(R.string.description_pace_metric)
|
||||||
reportSpeedCheckBoxPreference.setSummaryOff(
|
: getString(R.string.description_pace_imperial));
|
||||||
metric ? getString(R.string.settings_stats_rate_pace_metric)
|
|
||||||
: getString(R.string.settings_stats_rate_pace_imperial));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ public class Api10Adapter extends Api9Adapter {
|
|||||||
public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException {
|
public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException {
|
||||||
try {
|
try {
|
||||||
return bluetoothDevice.createInsecureRfcommSocketToServiceRecord(
|
return bluetoothDevice.createInsecureRfcommSocketToServiceRecord(
|
||||||
BluetoothConnectionManager.SPP_UUID);
|
BluetoothConnectionManager.MY_TRACKS_UUID);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||||
}
|
}
|
||||||
return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.SPP_UUID);
|
return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.MY_TRACKS_UUID);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public class Api7Adapter implements ApiAdapter {
|
|||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
Log.d(Constants.TAG, "Unable to create insecure connection", e);
|
||||||
}
|
}
|
||||||
return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.SPP_UUID);
|
return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.MY_TRACKS_UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,36 +15,34 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.apps.mytracks;
|
package com.google.android.apps.mytracks;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.ChartValueSeries.ZoomSettings;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.graphics.Paint.Style;
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Tests {@link ChartValueSeries}.
|
||||||
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class ChartValueSeriesTest extends AndroidTestCase {
|
public class ChartValueSeriesTest extends AndroidTestCase {
|
||||||
private ChartValueSeries series;
|
private ChartValueSeries series;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
series = new ChartValueSeries(getContext(),
|
series = new ChartValueSeries(getContext(),
|
||||||
|
Integer.MIN_VALUE,
|
||||||
|
Integer.MAX_VALUE,
|
||||||
|
new int[] {100, 1000 },
|
||||||
|
R.string.description_elevation_metric,
|
||||||
|
R.string.description_elevation_imperial,
|
||||||
R.color.elevation_fill,
|
R.color.elevation_fill,
|
||||||
R.color.elevation_border,
|
R.color.elevation_border);
|
||||||
new ZoomSettings(5, new int[] {100}),
|
|
||||||
R.string.stats_elevation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInitialConditions() {
|
public void testInitialConditions() {
|
||||||
assertEquals(0, series.getInterval());
|
assertEquals(1, series.getInterval());
|
||||||
assertEquals(1, series.getMaxLabelLength());
|
assertEquals(0, series.getMinMarkerValue());
|
||||||
assertEquals(0, series.getMin());
|
assertEquals(5, series.getMaxMarkerValue());
|
||||||
assertEquals(0, series.getMax());
|
|
||||||
assertEquals(0.0, series.getSpread());
|
|
||||||
assertEquals(Style.STROKE, series.getPaint().getStyle());
|
|
||||||
assertEquals(getContext().getString(R.string.stats_elevation),
|
|
||||||
series.getTitle());
|
|
||||||
assertTrue(series.isEnabled());
|
assertTrue(series.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,87 +50,31 @@ public class ChartValueSeriesTest extends AndroidTestCase {
|
|||||||
series.setEnabled(false);
|
series.setEnabled(false);
|
||||||
assertFalse(series.isEnabled());
|
assertFalse(series.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSmallUpdates() {
|
public void testSmallUpdates() {
|
||||||
series.update(0);
|
series.update(0);
|
||||||
series.update(10);
|
series.update(10);
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
assertEquals(100, series.getInterval());
|
assertEquals(100, series.getInterval());
|
||||||
assertEquals(3, series.getMaxLabelLength());
|
assertEquals(0, series.getMinMarkerValue());
|
||||||
assertEquals(0, series.getMin());
|
assertEquals(500, series.getMaxMarkerValue());
|
||||||
assertEquals(100, series.getMax());
|
|
||||||
assertEquals(100.0, series.getSpread());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBigUpdates() {
|
public void testBigUpdates() {
|
||||||
series.update(0);
|
series.update(0);
|
||||||
series.update(901);
|
series.update(901);
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
assertEquals(100, series.getInterval());
|
assertEquals(1000, series.getInterval());
|
||||||
assertEquals(5, series.getMaxLabelLength());
|
assertEquals(0, series.getMinMarkerValue());
|
||||||
assertEquals(0, series.getMin());
|
assertEquals(5000, series.getMaxMarkerValue());
|
||||||
assertEquals(1000, series.getMax());
|
|
||||||
assertEquals(1000.0, series.getSpread());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNotZeroBasedUpdates() {
|
public void testNotZeroBasedUpdates() {
|
||||||
series.update(500);
|
series.update(220);
|
||||||
series.update(1401);
|
series.update(250);
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
assertEquals(100, series.getInterval());
|
assertEquals(100, series.getInterval());
|
||||||
assertEquals(5, series.getMaxLabelLength());
|
assertEquals(200, series.getMinMarkerValue());
|
||||||
assertEquals(500, series.getMin());
|
assertEquals(700, series.getMaxMarkerValue());
|
||||||
assertEquals(1500, series.getMax());
|
|
||||||
assertEquals(1000.0, series.getSpread());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testZoomSettings_invalidArgs() {
|
|
||||||
try {
|
|
||||||
new ZoomSettings(0, new int[] {10, 50, 100});
|
|
||||||
fail("Expected IllegalArgumentException");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// OK.
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
new ZoomSettings(1, null);
|
|
||||||
fail("Expected IllegalArgumentException");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// OK.
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
new ZoomSettings(1, new int[] {});
|
|
||||||
fail("Expected IllegalArgumentException");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// OK.
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
new ZoomSettings(1, new int[] {1, 3, 2});
|
|
||||||
fail("Expected IllegalArgumentException");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// OK.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testZoomSettings_minAligned() {
|
|
||||||
ZoomSettings settings = new ZoomSettings(5, new int[] {10, 50, 100});
|
|
||||||
assertEquals(10, settings.calculateInterval(0, 15));
|
|
||||||
assertEquals(10, settings.calculateInterval(0, 50));
|
|
||||||
assertEquals(50, settings.calculateInterval(0, 111));
|
|
||||||
assertEquals(50, settings.calculateInterval(0, 250));
|
|
||||||
assertEquals(100, settings.calculateInterval(0, 251));
|
|
||||||
assertEquals(100, settings.calculateInterval(0, 10000));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testZoomSettings_minNotAligned() {
|
|
||||||
ZoomSettings settings = new ZoomSettings(5, new int[] {10, 50, 100});
|
|
||||||
assertEquals(50, settings.calculateInterval(5, 55));
|
|
||||||
assertEquals(10, settings.calculateInterval(10, 60));
|
|
||||||
assertEquals(50, settings.calculateInterval(7, 250));
|
|
||||||
assertEquals(100, settings.calculateInterval(7, 257));
|
|
||||||
assertEquals(100, settings.calculateInterval(11, 10000));
|
|
||||||
|
|
||||||
// A regression test.
|
|
||||||
settings = new ZoomSettings(5, new int[] {5, 10, 20});
|
|
||||||
assertEquals(10, settings.calculateInterval(-37.14, -11.89));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-36
@@ -52,31 +52,32 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
|
|
||||||
// No input.
|
// No input.
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
double[] point = fillDataPointTestHelper(myTracksLocation);
|
||||||
assertEquals(Double.NaN, point[3]);
|
assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
assertEquals(Double.NaN, point[4]);
|
assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
assertEquals(Double.NaN, point[5]);
|
assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||||
|
|
||||||
// Input incorrect state.
|
// Input incorrect state.
|
||||||
// Creates SensorData.
|
// Creates SensorData.
|
||||||
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(20).setState(Sensor.SensorState.NONE);
|
|
||||||
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(20).setState(Sensor.SensorState.NONE);
|
|
||||||
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
||||||
.setValue(20).setState(Sensor.SensorState.NONE);
|
.setValue(100).setState(Sensor.SensorState.NONE);
|
||||||
|
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(101).setState(Sensor.SensorState.NONE);
|
||||||
|
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(102).setState(Sensor.SensorState.NONE);
|
||||||
|
|
||||||
// Creates SensorDataSet.
|
// Creates SensorDataSet.
|
||||||
SensorDataSet sensorDataSet = myTracksLocation.getSensorDataSet();
|
SensorDataSet sensorDataSet = myTracksLocation.getSensorDataSet();
|
||||||
sensorDataSet = sensorDataSet.toBuilder()
|
sensorDataSet = sensorDataSet.toBuilder()
|
||||||
.setPower(powerData)
|
|
||||||
.setCadence(cadenceData)
|
|
||||||
.setHeartRate(heartRateData)
|
.setHeartRate(heartRateData)
|
||||||
|
.setCadence(cadenceData)
|
||||||
|
.setPower(powerData)
|
||||||
.build();
|
.build();
|
||||||
myTracksLocation.setSensorData(sensorDataSet);
|
myTracksLocation.setSensorData(sensorDataSet);
|
||||||
// Test.
|
// Test.
|
||||||
point = fillDataPointTestHelper(myTracksLocation);
|
point = fillDataPointTestHelper(myTracksLocation);
|
||||||
assertEquals(Double.NaN, point[3]);
|
assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
assertEquals(Double.NaN, point[4]);
|
assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
assertEquals(Double.NaN, point[5]);
|
assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,31 +88,31 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||||
// No input.
|
// No input.
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
double[] point = fillDataPointTestHelper(myTracksLocation);
|
||||||
assertEquals(Double.NaN, point[3]);
|
assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
assertEquals(Double.NaN, point[4]);
|
assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
assertEquals(Double.NaN, point[5]);
|
assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||||
|
|
||||||
// Creates SensorData.
|
// Creates SensorData.
|
||||||
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(20).setState(Sensor.SensorState.SENDING);
|
|
||||||
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(20).setState(Sensor.SensorState.SENDING);
|
|
||||||
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
||||||
.setValue(20).setState(Sensor.SensorState.SENDING);
|
.setValue(100).setState(Sensor.SensorState.SENDING);
|
||||||
|
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(101).setState(Sensor.SensorState.SENDING);
|
||||||
|
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(102).setState(Sensor.SensorState.SENDING);
|
||||||
|
|
||||||
// Creates SensorDataSet.
|
// Creates SensorDataSet.
|
||||||
SensorDataSet sensorDataSet = myTracksLocation.getSensorDataSet();
|
SensorDataSet sensorDataSet = myTracksLocation.getSensorDataSet();
|
||||||
sensorDataSet = sensorDataSet.toBuilder()
|
sensorDataSet = sensorDataSet.toBuilder()
|
||||||
.setPower(powerData)
|
|
||||||
.setCadence(cadenceData)
|
|
||||||
.setHeartRate(heartRateData)
|
.setHeartRate(heartRateData)
|
||||||
|
.setCadence(cadenceData)
|
||||||
|
.setPower(powerData)
|
||||||
.build();
|
.build();
|
||||||
myTracksLocation.setSensorData(sensorDataSet);
|
myTracksLocation.setSensorData(sensorDataSet);
|
||||||
// Test.
|
// Test.
|
||||||
point = fillDataPointTestHelper(myTracksLocation);
|
point = fillDataPointTestHelper(myTracksLocation);
|
||||||
assertEquals(20.0, point[3]);
|
assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
assertEquals(20.0, point[4]);
|
assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
assertEquals(20.0, point[5]);
|
assertEquals(102.0, point[ChartView.POWER_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -230,7 +231,7 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
* parameter. Then only one value INITIALLONGTITUDE in buffer.
|
* parameter. Then only one value INITIALLONGTITUDE in buffer.
|
||||||
*/
|
*/
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
||||||
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[1]);
|
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send another value to buffer, now there are two values, INITIALALTITUDE
|
* Send another value to buffer, now there are two values, INITIALALTITUDE
|
||||||
@@ -239,8 +240,8 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||||
myTracksLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
myTracksLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||||
point = fillDataPointTestHelper(myTracksLocation2);
|
point = fillDataPointTestHelper(myTracksLocation2);
|
||||||
assertEquals(
|
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
|
||||||
(TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point[1]);
|
point[ChartView.ELEVATION_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,7 +261,7 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||||
myTracksLocation1.setSpeed(129);
|
myTracksLocation1.setSpeed(129);
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
||||||
assertEquals(0.0, point[2]);
|
assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tests the logic when both metricUnits and reportSpeed are true.This
|
* Tests the logic when both metricUnits and reportSpeed are true.This
|
||||||
@@ -275,7 +276,7 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
myTracksLocation2.setTime(myTracksLocation1.getTime() + 222);
|
myTracksLocation2.setTime(myTracksLocation1.getTime() + 222);
|
||||||
myTracksLocation2.setSpeed(130);
|
myTracksLocation2.setSpeed(130);
|
||||||
point = fillDataPointTestHelper(myTracksLocation2);
|
point = fillDataPointTestHelper(myTracksLocation2);
|
||||||
assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[2]);
|
assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,7 +288,8 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||||
myTracksLocation.setSpeed(132);
|
myTracksLocation.setSpeed(132);
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
double[] point = fillDataPointTestHelper(myTracksLocation);
|
||||||
assertEquals(132.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[2]);
|
assertEquals(132.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI,
|
||||||
|
point[ChartView.SPEED_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -299,8 +301,8 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||||
myTracksLocation.setSpeed(134);
|
myTracksLocation.setSpeed(134);
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
double[] point = fillDataPointTestHelper(myTracksLocation);
|
||||||
assertEquals(HOURS_PER_UNIT / (134.0 * UnitConversions.MS_TO_KMH), point[2]);
|
assertEquals(
|
||||||
|
HOURS_PER_UNIT / (134.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -313,7 +315,7 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||||
myTracksLocation.setSpeed(0);
|
myTracksLocation.setSpeed(0);
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
double[] point = fillDataPointTestHelper(myTracksLocation);
|
||||||
assertEquals(0.0, point[2]);
|
assertEquals(0.0, point[ChartView.PACE_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -323,7 +325,7 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
* @return data of this location
|
* @return data of this location
|
||||||
*/
|
*/
|
||||||
private double[] fillDataPointTestHelper(Location location) {
|
private double[] fillDataPointTestHelper(Location location) {
|
||||||
double[] point = new double[6];
|
double[] point = new double[ChartView.NUM_SERIES + 1];
|
||||||
chartFragment.fillDataPoint(location, point);
|
chartFragment.fillDataPoint(location, point);
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -46,6 +46,7 @@ public class UploadResultActivityTest
|
|||||||
*/
|
*/
|
||||||
public void testAllSuccess() {
|
public void testAllSuccess() {
|
||||||
initialActivity(true, true, true, true, true, true);
|
initialActivity(true, true, true, true, true, true);
|
||||||
|
uploadResultActivity.showDialog(UploadResultActivity.DIALOG_RESULT_ID);
|
||||||
Dialog dialog = uploadResultActivity.getDialog();
|
Dialog dialog = uploadResultActivity.getDialog();
|
||||||
TextView textView = (TextView) dialog.findViewById(R.id.upload_result_success_footer);
|
TextView textView = (TextView) dialog.findViewById(R.id.upload_result_success_footer);
|
||||||
assertTrue(textView.isShown());
|
assertTrue(textView.isShown());
|
||||||
@@ -57,6 +58,7 @@ public class UploadResultActivityTest
|
|||||||
public void testAllFailed() {
|
public void testAllFailed() {
|
||||||
// Send all kinds but all failed.
|
// Send all kinds but all failed.
|
||||||
initialActivity(true, true, true, false, false, false);
|
initialActivity(true, true, true, false, false, false);
|
||||||
|
uploadResultActivity.showDialog(UploadResultActivity.DIALOG_RESULT_ID);
|
||||||
Dialog dialog = uploadResultActivity.getDialog();
|
Dialog dialog = uploadResultActivity.getDialog();
|
||||||
TextView textView = (TextView) dialog.findViewById(R.id.upload_result_error_footer);
|
TextView textView = (TextView) dialog.findViewById(R.id.upload_result_error_footer);
|
||||||
assertTrue(textView.isShown());
|
assertTrue(textView.isShown());
|
||||||
@@ -72,6 +74,7 @@ public class UploadResultActivityTest
|
|||||||
*/
|
*/
|
||||||
public void testPartialSuccess() {
|
public void testPartialSuccess() {
|
||||||
initialActivity(true, false, true, true, false, false);
|
initialActivity(true, false, true, true, false, false);
|
||||||
|
uploadResultActivity.showDialog(UploadResultActivity.DIALOG_RESULT_ID);
|
||||||
Dialog dialog = uploadResultActivity.getDialog();
|
Dialog dialog = uploadResultActivity.getDialog();
|
||||||
TextView textView = (TextView) dialog.findViewById(R.id.upload_result_error_footer);
|
TextView textView = (TextView) dialog.findViewById(R.id.upload_result_error_footer);
|
||||||
assertTrue(textView.isShown());
|
assertTrue(textView.isShown());
|
||||||
|
|||||||
+3
-3
@@ -23,7 +23,7 @@ public class SensorManagerFactoryTest extends AndroidTestCase {
|
|||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testDefaultSettings() throws Exception {
|
public void testDefaultSettings() throws Exception {
|
||||||
assertNull(SensorManagerFactory.getInstance().getSensorManager(getContext()));
|
assertNull(SensorManagerFactory.getSystemSensorManager(getContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -38,9 +38,9 @@ public class SensorManagerFactoryTest extends AndroidTestCase {
|
|||||||
|
|
||||||
private void assertClassForName(Class<?> c, int i) {
|
private void assertClassForName(Class<?> c, int i) {
|
||||||
PreferencesUtils.setString(getContext(), R.string.sensor_type_key, getContext().getString(i));
|
PreferencesUtils.setString(getContext(), R.string.sensor_type_key, getContext().getString(i));
|
||||||
SensorManager sm = SensorManagerFactory.getInstance().getSensorManager(getContext());
|
SensorManager sm = SensorManagerFactory.getSystemSensorManager(getContext());
|
||||||
assertNotNull(sm);
|
assertNotNull(sm);
|
||||||
assertTrue(c.isInstance(sm));
|
assertTrue(c.isInstance(sm));
|
||||||
SensorManagerFactory.getInstance().releaseSensorManager(sm);
|
SensorManagerFactory.releaseSystemSensorManager();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user