mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-03 09:53:06 +02:00
Merging sensor and library changes together.
This commit is contained in:
@@ -7,5 +7,6 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="src" path="MyTracksLib_src"/>
|
||||
<classpathentry exported="true" kind="lib" path="/MyTracksLib/libs/protobuf-java-2.3.0-lite.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@@ -83,6 +83,9 @@ limitations under the License.
|
||||
android:label="@string/statistics"
|
||||
android:exported="false" />
|
||||
|
||||
<activity android:name="com.google.android.apps.mytracks.SensorStateActivity"
|
||||
android:label="@string/sensor_state" />
|
||||
|
||||
<activity android:name="com.google.android.apps.mytracks.ChartActivity"
|
||||
android:label="@string/elevation_label"
|
||||
android:exported="false" />
|
||||
@@ -113,6 +116,8 @@ limitations under the License.
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
|
||||
<!-- Permissions for uploading tracks -->
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -103,6 +103,21 @@ limitations under the License.
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/speed"/>
|
||||
<CheckBox android:id="@+id/chart_settings_power"
|
||||
android:checked="true"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/power"/>
|
||||
<CheckBox android:id="@+id/chart_settings_cadence"
|
||||
android:checked="true"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/cadence"/>
|
||||
<CheckBox android:id="@+id/chart_settings_heart_rate"
|
||||
android:checked="true"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/heart_rate"/>
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2010 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent">
|
||||
|
||||
<ScrollView
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent">
|
||||
|
||||
<TableLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="1,4"
|
||||
android:layout_width="fill_parent">
|
||||
|
||||
<!-- Layout the sensor state on one line. -->
|
||||
<TableRow>
|
||||
<TextView
|
||||
android:layout_span="5"
|
||||
android:text="@string/sensor_state" />
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:id="@+id/sensor_state_register"
|
||||
android:layout_column="1"
|
||||
android:layout_span="5"
|
||||
style="@style/StatsRegister" />
|
||||
</TableRow>
|
||||
|
||||
<!-- Layout the sensor data age on one line. -->
|
||||
<TableRow>
|
||||
<TextView
|
||||
android:layout_span="5"
|
||||
android:text="@string/sensor_data_time" />
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:id="@+id/sensor_data_time_register"
|
||||
android:layout_column="1"
|
||||
android:layout_span="5"
|
||||
style="@style/StatsRegister" />
|
||||
</TableRow>
|
||||
|
||||
<!-- Power meter -->
|
||||
<TableRow
|
||||
android:padding="1dip">
|
||||
<TextView
|
||||
android:layout_column="1"
|
||||
android:layout_span="3"
|
||||
android:text="@string/power" />
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
style="@style/StatsRegister.Big"
|
||||
android:id="@+id/power_state_register"
|
||||
android:layout_column="1"
|
||||
android:layout_span="4" />
|
||||
<TextView
|
||||
style="@style/StatsUnitSolo" />
|
||||
android:layout_column="5"
|
||||
android:text="@string/watts" />
|
||||
</TableRow>
|
||||
|
||||
<!-- Cadence -->
|
||||
<TableRow
|
||||
android:padding="1dip">
|
||||
<TextView
|
||||
android:layout_column="1"
|
||||
android:layout_span="3"
|
||||
android:text="@string/cadence" />
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
style="@style/StatsRegister.Big"
|
||||
android:id="@+id/cadence_state_register"
|
||||
android:layout_column="4" />
|
||||
<TextView
|
||||
style="@style/StatsUnitSolo" />
|
||||
android:layout_column="2"
|
||||
android:text="@string/revolutions_per_minute" />
|
||||
</TableRow>
|
||||
|
||||
<!-- Heart Rate -->
|
||||
<TableRow
|
||||
android:padding="1dip">
|
||||
<TextView
|
||||
android:layout_column="1"
|
||||
android:layout_span="3"
|
||||
android:text="@string/heart_rate" />
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
style="@style/StatsRegister.Big"
|
||||
android:id="@+id/heart_rate_register"
|
||||
android:layout_column="1"
|
||||
android:layout_span="4" />
|
||||
<TextView
|
||||
style="@style/StatsUnitSolo"
|
||||
android:layout_column="5"
|
||||
android:text="@string/bpm" />
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</RelativeLayout>
|
||||
+48
-225
@@ -42,15 +42,10 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
style="@style/StatsRegister.Big"
|
||||
android:id="@+id/speed_register"
|
||||
android:layout_column="1"
|
||||
android:layout_marginBottom="-13dip"
|
||||
android:layout_marginTop="-13dip"
|
||||
android:layout_span="4"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="50sp" />
|
||||
android:layout_span="4"/>
|
||||
<LinearLayout
|
||||
android:gravity="bottom"
|
||||
android:layout_column="5"
|
||||
@@ -62,24 +57,12 @@ limitations under the License.
|
||||
android:layout_width="fill_parent" />
|
||||
<TextView
|
||||
android:id="@+id/speed_unit_label_top"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dip"
|
||||
android:layout_marginTop="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_height="3dip"
|
||||
android:layout_width="fill_parent" />
|
||||
style="@style/StatsDivider" />
|
||||
<TextView
|
||||
android:id="@+id/speed_unit_label_bottom"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dip"
|
||||
android:layout_marginTop="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
|
||||
@@ -100,43 +83,25 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
style="@style/StatsRegister"
|
||||
android:id="@+id/total_distance_register"
|
||||
android:layout_column="1"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
android:layout_column="1" />
|
||||
<TextView
|
||||
android:gravity="left"
|
||||
android:id="@+id/total_distance_unit_label"
|
||||
android:layout_column="2"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/mile"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnitSolo" />
|
||||
<!-- TODO 4/08/2009
|
||||
The following is a bad hack to get a spacer the same size as the
|
||||
text. Ideally this should just be a View, however I don't know how to
|
||||
set the view height properly. fill_parent on this element causes as
|
||||
NullPointerException. This same filler is repeated on each line.-->
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_column="3"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="5dip"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsSpacer"
|
||||
android:layout_column="3"/>
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/max_speed_register"
|
||||
android:layout_column="4"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister"
|
||||
android:layout_column="4" />
|
||||
<LinearLayout
|
||||
android:gravity="bottom"
|
||||
android:layout_column="5"
|
||||
@@ -145,24 +110,12 @@ limitations under the License.
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/max_speed_unit_label_top"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-1dip"
|
||||
android:layout_marginTop="-1dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_height="3dip"
|
||||
android:layout_width="fill_parent" />
|
||||
style="@style/StatsDivider" />
|
||||
<TextView
|
||||
android:id="@+id/max_speed_unit_label_bottom"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-1dip"
|
||||
android:layout_marginTop="-1dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
|
||||
@@ -182,33 +135,19 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/total_time_register"
|
||||
android:layout_column="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_span="2"
|
||||
android:layout_width="fill_parent"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_column="3"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="5dip"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsSpacer"
|
||||
android:layout_column="3"/>
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/average_speed_register"
|
||||
android:layout_column="4"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<LinearLayout
|
||||
android:gravity="bottom"
|
||||
android:layout_column="5"
|
||||
@@ -217,24 +156,12 @@ limitations under the License.
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/average_speed_unit_label_top"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-1dip"
|
||||
android:layout_marginTop="-1dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_height="3dip"
|
||||
android:layout_width="fill_parent" />
|
||||
style="@style/StatsDivider" />
|
||||
<TextView
|
||||
android:id="@+id/average_speed_unit_label_bottom"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-1dip"
|
||||
android:layout_marginTop="-1dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
|
||||
@@ -254,33 +181,18 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/moving_time_register"
|
||||
android:layout_column="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_span="2"
|
||||
android:layout_width="fill_parent"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_column="3"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="5dip"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsSpacer"
|
||||
android:layout_column="3"/>
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/average_moving_speed_register"
|
||||
android:layout_column="4"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<LinearLayout
|
||||
android:gravity="bottom"
|
||||
android:layout_column="5"
|
||||
@@ -289,24 +201,14 @@ limitations under the License.
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/average_moving_speed_unit_label_top"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-1dip"
|
||||
android:layout_marginTop="-1dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
style="@style/StatsDivider" />
|
||||
android:layout_height="3dip"
|
||||
android:layout_width="fill_parent" />
|
||||
<TextView
|
||||
android:id="@+id/average_moving_speed_unit_label_bottom"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-1dip"
|
||||
android:layout_marginTop="-1dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnit" />
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
|
||||
@@ -326,46 +228,24 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/elevation_register"
|
||||
android:layout_column="1"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<TextView
|
||||
android:gravity="left"
|
||||
android:id="@+id/elevation_unit_label"
|
||||
android:layout_column="2"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/feet"
|
||||
android:textColor="@color/stat_label_text"/>
|
||||
style="@style/StatsUnitSolo" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_column="3"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="5dip"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsSpacer"
|
||||
android:layout_column="3"/>
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/elevation_gain_register"
|
||||
android:layout_column="4"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/elevation_gain_unit_label"
|
||||
android:layout_column="5"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/feet"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnitSolo" />
|
||||
</TableRow>
|
||||
|
||||
<!-- Min and max elevation -->
|
||||
@@ -384,46 +264,24 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/min_elevation_register"
|
||||
android:layout_column="1"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<TextView
|
||||
android:gravity="left"
|
||||
android:id="@+id/min_elevation_unit_label"
|
||||
android:layout_column="2"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/feet"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnitSolo" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_column="3"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="5dip"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsSpacer"
|
||||
android:layout_column="3"/>
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/max_elevation_register"
|
||||
android:layout_column="4"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/max_elevation_unit_label"
|
||||
android:layout_column="5"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:text="@string/feet"
|
||||
android:textColor="@color/stat_label_text" />
|
||||
style="@style/StatsUnitSolo" />
|
||||
</TableRow>
|
||||
|
||||
<!-- Min and max grade -->
|
||||
@@ -442,36 +300,18 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/min_grade_register"
|
||||
android:layout_column="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_span="2"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
android:layout_column="3"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="5dip"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsSpacer"
|
||||
android:layout_column="3"/>
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/max_grade_register"
|
||||
android:layout_column="4"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_span="2"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="40sp" />
|
||||
style="@style/StatsRegister" />
|
||||
</TableRow>
|
||||
|
||||
<!-- Latitude and Longitude -->
|
||||
@@ -488,36 +328,19 @@ limitations under the License.
|
||||
<TableRow
|
||||
android:background="@color/stat_label_background">
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/latitude_register"
|
||||
android:layout_column="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-5dip"
|
||||
android:layout_marginTop="-5dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_span="2"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="25sp" />
|
||||
style="@style/StatsRegister.Small" />
|
||||
<TextView
|
||||
android:background="@color/black"
|
||||
style="@style/StatsSpacer"
|
||||
android:layout_column="3"
|
||||
android:layout_marginBottom="-9dip"
|
||||
android:layout_marginTop="-9dip"
|
||||
android:layout_width="5dip"
|
||||
android:textSize="25sp" />
|
||||
<TextView
|
||||
android:gravity="right"
|
||||
android:id="@+id/longitude_register"
|
||||
android:layout_column="4"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-5dip"
|
||||
android:layout_marginTop="-5dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_span="2"
|
||||
android:text="@string/unknown"
|
||||
android:textColor="@color/stat_label_text"
|
||||
android:textSize="25sp" />
|
||||
style="@style/StatsRegister.Small" />
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
|
||||
@@ -22,10 +22,14 @@
|
||||
android:title="@string/settings"/>
|
||||
|
||||
<item android:id="@+id/menu_aggregated_stats"
|
||||
android:icon="@android:drawable/ic_menu_info_details"
|
||||
android:orderInCategory="1002"
|
||||
android:title="@string/aggregated_stats"/>
|
||||
|
||||
<item android:id="@+id/menu_sensor_state"
|
||||
android:icon="@drawable/menu_sensor_status"
|
||||
android:orderInCategory="1000"
|
||||
android:title="@string/sensor_state"/>
|
||||
|
||||
<item android:id="@+id/menu_help"
|
||||
android:icon="@android:drawable/ic_menu_info_details"
|
||||
android:orderInCategory="1003"
|
||||
|
||||
@@ -390,4 +390,16 @@ limitations under the License.
|
||||
<item>CSV</item>
|
||||
<item>TCX</item>
|
||||
</array>
|
||||
|
||||
<array name="sensor_type_options">
|
||||
<item>None</item>
|
||||
<item>Zephyr Bluetooth</item>
|
||||
</array>
|
||||
|
||||
<!-- DO NOT TRANSLATE -->
|
||||
<array name="sensor_type_values">
|
||||
<item>@string/none</item>
|
||||
<item>@string/zephyr_sensor_type</item>
|
||||
</array>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -15,17 +15,22 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<color name="gold">#FFffd700</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="lightblue">#FF00FFFF</color>
|
||||
<color name="blue">#FF0000FF</color>
|
||||
<color name="blue_transparent">#400000FF</color>
|
||||
<color name="red">#FFFF0000</color>
|
||||
<color name="green">#FF00AA00</color>
|
||||
<color name="gray">#FFAAAAAA</color>
|
||||
<color name="darkgray">#FF444444</color>
|
||||
<color name="gold">#FFffd700</color>
|
||||
<color name="gray">#FFAAAAAA</color>
|
||||
<color name="green">#FF00AA00</color>
|
||||
<color name="lightblue">#FF00FFFF</color>
|
||||
<color name="opaque_shadow">#70000000</color>
|
||||
<color name="purple">#FF401465</color>
|
||||
<color name="purple_transparent">#40401465</color>
|
||||
<color name="red">#FFFF0000</color>
|
||||
<color name="red_transparent">#40FF0000</color>
|
||||
<color name="stat_label_background">#FFF0F0F0</color>
|
||||
<color name="stat_label_text">#FF0F0F0F</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="yellow">#FFF8FF00</color>
|
||||
<color name="yellow_transparent">#40F8FF00</color>
|
||||
</resources>
|
||||
|
||||
@@ -33,7 +33,6 @@ limitations under the License.
|
||||
<string name="send_stats_and_points_key">sendStatsAndPoints</string>
|
||||
<string name="send_to_docs_key">sendToDocs</string>
|
||||
<string name="send_to_my_maps_key">sendToMyMaps</string>
|
||||
<string name="sensor_id_key">sensorId</string>
|
||||
<string name="sensor_type_key">sensorType</string>
|
||||
<string name="share_url_only_key">shareUrlOnly</string>
|
||||
<string name="split_frequency_key">splitFrequency</string>
|
||||
|
||||
@@ -32,6 +32,7 @@ limitations under the License.
|
||||
<string name="choose_track_message">Please select the track to view</string>
|
||||
<string name="current_track">Current track</string>
|
||||
<string name="current_segment">Last segment</string>
|
||||
<string name="sensor_state">Sensor State</string>
|
||||
<string name="aggregated_stats">Aggregated Statistics</string>
|
||||
|
||||
<!-- Error messages -->
|
||||
@@ -454,9 +455,7 @@ limitations under the License.
|
||||
|
||||
<!-- Sensor Types => DO NOT TRANSLATE -->
|
||||
<string name="none">NONE</string>
|
||||
<string name="ant_sensor_type">ANT</string>
|
||||
<string name="srm_sensor_type">SRM</string>
|
||||
<string name="athlosoft_sensor_type">ATHLOSOFT</string>
|
||||
<string name="zephyr_sensor_type">ZEPHYR</string>
|
||||
|
||||
<!-- Bluetooth Sensor -->
|
||||
<string name="bluetooth_connected">Connected to %s</string>
|
||||
@@ -467,6 +466,14 @@ limitations under the License.
|
||||
<string name="sensor_data_time">Sensor Data Time</string>
|
||||
<string name="power_meter_enabled">Power meter enabled</string>
|
||||
<string name="heart_rate_monitor_enabled">Heart rate monitor enabled</string>
|
||||
<string name="no_sensor_manager">No Sensor Manager</string>
|
||||
<string name="enabled">Enabled</string>
|
||||
<string name="connecting">Connecting</string>
|
||||
<string name="connected">Connected</string>
|
||||
<string name="disconnected">Disconnected</string>
|
||||
<string name="no_sensor_service">No Sensor Service</string>
|
||||
<string name="searching">Searching</string>
|
||||
<string name="sending">Sending</string>
|
||||
|
||||
<!-- Manifest description (not seen inside the app) -->
|
||||
<string name="notification_permission_label">receive track recording notifications</string>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2010 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<!-- The style for the unit on the statistics activity. -->
|
||||
<style name="StatsUnit" parent="@android:Widget.TextView">
|
||||
<item name="android:gravity">bottom</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginBottom">-2dip</item>
|
||||
<item name="android:layout_marginTop">-2dip</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:textColor">@color/stat_label_text</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for the unit on the statistics activity where there is only one string. -->
|
||||
<style name="StatsUnitSolo" parent="@android:Widget.TextView">
|
||||
<item name="android:gravity">right</item>
|
||||
<item name="android:layout_marginBottom">-9dip</item>
|
||||
<item name="android:layout_marginTop">-9dip</item>
|
||||
<item name="android:textColor">@color/stat_label_text</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for a text view for displaying a value on the statistics activity. -->
|
||||
<style name="StatsRegister" parent="@android:Widget.TextView">
|
||||
<item name="android:gravity">right</item>
|
||||
<item name="android:layout_marginBottom">-9dip</item>
|
||||
<item name="android:layout_marginTop">-9dip</item>
|
||||
<item name="android:text">@string/unknown</item>
|
||||
<item name="android:textColor">@color/stat_label_text</item>
|
||||
<item name="android:textSize">40sp</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for a text view for displaying a large value on the statistics activity. -->
|
||||
<style name="StatsRegister.Big">
|
||||
<item name="android:layout_marginBottom">-13dip</item>
|
||||
<item name="android:layout_marginTop">-13dip</item>
|
||||
<item name="android:textSize">50sp</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for a text view for displaying a small value on the statistics activity. -->
|
||||
<style name="StatsRegister.Small">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginBottom">-5dip</item>
|
||||
<item name="android:layout_marginTop">-5dip</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:textSize">25sp</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for a view which divides two columns in the statistics activity. -->
|
||||
<style name="StatsSpacer" parent="@android:Widget.TextView">
|
||||
<item name="android:background">@color/black</item>
|
||||
<item name="android:layout_marginBottom">-9dip</item>
|
||||
<item name="android:layout_marginTop">-9dip</item>
|
||||
<item name="android:layout_width">5dip</item>
|
||||
<item name="android:textSize">40sp</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for a view which divides the numerator and denominator of the units on the statistics activity. -->
|
||||
<style name="StatsDivider" parent="@android:Widget.TextView">
|
||||
<item name="android:background">@color/black</item>
|
||||
<item name="android:layout_height">3dip</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -101,6 +101,29 @@ limitations under the License.
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
<!-- Sensors -->
|
||||
<PreferenceScreen android:title="@string/sensor_options">
|
||||
<ListPreference android:key="@string/sensor_type_key"
|
||||
android:title="@string/settings_sensor_type"
|
||||
android:summary="@string/settings_sensor_type_summary"
|
||||
android:entries="@array/sensor_type_options"
|
||||
android:entryValues="@array/sensor_type_values"
|
||||
android:defaultValue="@string/none"/>
|
||||
|
||||
<!-- Bluetooth -->
|
||||
<PreferenceCategory android:title="@string/bluetooth_options">
|
||||
<ListPreference android:key="@string/bluetooth_sensor_key"
|
||||
android:title="@string/bluetooth_sensor_title"
|
||||
android:summary="@string/bluetooth_sensor_summary"
|
||||
android:defaultValue="any" />
|
||||
|
||||
<Preference android:key="@string/bluetooth_pairing_key"
|
||||
android:title="@string/bluetooth_pairing"
|
||||
android:summary="@string/bluetooth_pairing_summary"
|
||||
android:persistent="false" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
<!-- Sharing settings -->
|
||||
<PreferenceScreen android:title="@string/settings_sharing">
|
||||
<CheckBoxPreference
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.ChartView.Mode;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||
@@ -178,12 +181,12 @@ public class ChartActivity extends Activity implements
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
ArrayList<double[]> data = new ArrayList<double[]>();
|
||||
// Need two locations so we can keep track of the last location.
|
||||
Location location = new Location("");
|
||||
Location location = new MyTracksLocation("");
|
||||
do {
|
||||
lastSeenLocationId = cursor.getLong(idColumnIdx);
|
||||
providerUtils.fillLocation(cursor, location);
|
||||
if (MyTracksUtils.isValidLocation(location)) {
|
||||
double[] point = new double[3];
|
||||
double[] point = new double[6];
|
||||
location = getDataPoint(location, track, point);
|
||||
data.add(point);
|
||||
}
|
||||
@@ -464,10 +467,14 @@ public class ChartActivity extends Activity implements
|
||||
|
||||
/**
|
||||
* Given a location, creates a new data point for the chart. A data point is
|
||||
* an array double[2], where:
|
||||
* an array double[3 or 6], where:
|
||||
* data[0] = the time or distance
|
||||
* data[1] = the elevation
|
||||
* data[2] = the speed
|
||||
* and possibly:
|
||||
* data[3] = power
|
||||
* data[4] = cadence
|
||||
* data[5] = heart rate
|
||||
*
|
||||
* This must be called in order for each point.
|
||||
*
|
||||
@@ -477,6 +484,35 @@ public class ChartActivity extends Activity implements
|
||||
* @return the previous location, now available for reuse
|
||||
*/
|
||||
private Location getDataPoint(Location location, Track track, double[] result) {
|
||||
if (location instanceof MyTracksLocation &&
|
||||
((MyTracksLocation) location).getSensorDataSet() != null) {
|
||||
SensorDataSet sensorData = ((MyTracksLocation) location).getSensorDataSet();
|
||||
if (sensorData.hasPower()
|
||||
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getPower().hasValue()) {
|
||||
result[3] = sensorData.getPower().getValue();
|
||||
} else {
|
||||
result[3] = Double.NaN;
|
||||
}
|
||||
if (sensorData.hasCadence()
|
||||
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getCadence().hasValue()) {
|
||||
result[4] = sensorData.getCadence().getValue();
|
||||
} else {
|
||||
result[4] = Double.NaN;
|
||||
}
|
||||
if (sensorData.hasHeartRate()
|
||||
&& sensorData.getHeartRate().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getHeartRate().hasValue()) {
|
||||
result[5] = sensorData.getHeartRate().getValue();
|
||||
} else {
|
||||
result[5] = Double.NaN;
|
||||
}
|
||||
} else {
|
||||
result[3] = Double.NaN;
|
||||
result[4] = Double.NaN;
|
||||
result[5] = Double.NaN;
|
||||
}
|
||||
switch (mode) {
|
||||
case BY_DISTANCE:
|
||||
result[0] = profileLength;
|
||||
@@ -529,7 +565,7 @@ public class ChartActivity extends Activity implements
|
||||
|
||||
if (oldLastLocation == null) {
|
||||
// No previous location, but return a blank one for reuse
|
||||
return new Location("");
|
||||
return new MyTracksLocation("");
|
||||
}
|
||||
|
||||
return oldLastLocation;
|
||||
@@ -576,7 +612,7 @@ public class ChartActivity extends Activity implements
|
||||
final ArrayList<double[]> theData = new ArrayList<double[]>();
|
||||
int points = 0;
|
||||
// Need two locations so we can keep track of the last location.
|
||||
Location location = new Location("");
|
||||
Location location = new MyTracksLocation("");
|
||||
while (lastLocationRead < track.getStopId()) {
|
||||
cursor = providerUtils.getLocationsCursor(
|
||||
selectedTrackId, lastLocationRead, bufferSize, false);
|
||||
@@ -592,7 +628,8 @@ public class ChartActivity extends Activity implements
|
||||
if (MyTracksUtils.isValidLocation(location)) {
|
||||
lastLocationRead = lastSeenLocationId =
|
||||
cursor.getLong(idColumnIdx);
|
||||
double[] point = new double[3];
|
||||
// TODO Can we be smarter about choosing 3 or 6 entries?
|
||||
double[] point = new double[6];
|
||||
location = getDataPoint(location, track, point);
|
||||
if (points % chartSamplingFrequency == 0) {
|
||||
theData.add(point);
|
||||
|
||||
@@ -35,8 +35,7 @@ import android.widget.RadioGroup;
|
||||
*/
|
||||
public class ChartSettingsDialog extends Dialog {
|
||||
private RadioButton distance;
|
||||
private CheckBox elevation;
|
||||
private CheckBox speed;
|
||||
private CheckBox[] series;
|
||||
|
||||
public ChartSettingsDialog(Context context) {
|
||||
super(context);
|
||||
@@ -61,8 +60,18 @@ public class ChartSettingsDialog extends Dialog {
|
||||
});
|
||||
|
||||
distance = (RadioButton) findViewById(R.id.chart_settings_by_distance);
|
||||
elevation = (CheckBox) findViewById(R.id.chart_settings_elevation);
|
||||
speed = (CheckBox) findViewById(R.id.chart_settings_speed);
|
||||
|
||||
series = new CheckBox[ChartView.NUM_SERIES];
|
||||
series[ChartView.ELEVATION_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_elevation);
|
||||
series[ChartView.SPEED_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_speed);
|
||||
series[ChartView.POWER_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_power);
|
||||
series[ChartView.CADENCE_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_cadence);
|
||||
series[ChartView.HEART_RATE_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_heart_rate);
|
||||
}
|
||||
|
||||
public void setup(ChartActivity chart) {
|
||||
@@ -74,8 +83,9 @@ public class ChartSettingsDialog extends Dialog {
|
||||
rd.check(chart.getMode() == Mode.BY_DISTANCE
|
||||
? R.id.chart_settings_by_distance
|
||||
: R.id.chart_settings_by_time);
|
||||
elevation.setChecked(chart.isSeriesEnabled(ChartView.ELEVATION_SERIES));
|
||||
speed.setChecked(chart.isSeriesEnabled(ChartView.SPEED_SERIES));
|
||||
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||
series[i].setChecked(chart.isSeriesEnabled(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOk() {
|
||||
@@ -83,8 +93,9 @@ public class ChartSettingsDialog extends Dialog {
|
||||
chart.setMode(distance.isChecked() ? Mode.BY_DISTANCE : Mode.BY_TIME);
|
||||
|
||||
// TODO: check that something is visible.
|
||||
chart.setSeriesEnabled(ChartView.ELEVATION_SERIES, elevation.isChecked());
|
||||
chart.setSeriesEnabled(ChartView.SPEED_SERIES, speed.isChecked());
|
||||
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||
chart.setSeriesEnabled(i, series[i].isChecked());
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class ChartValueSeries {
|
||||
private double min = 0;
|
||||
private double max = 1;
|
||||
private int effectiveMax = 0;
|
||||
private int absoluteMax = Integer.MIN_VALUE;
|
||||
private int effectiveMin = 0;
|
||||
private double spread = 0;
|
||||
private boolean enabled = true;
|
||||
@@ -131,7 +132,7 @@ public class ChartValueSeries {
|
||||
if (min < 0) {
|
||||
effectiveMin -= rounding;
|
||||
}
|
||||
spread = effectiveMax - effectiveMin;
|
||||
spread = getMax() - effectiveMin;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +140,7 @@ public class ChartValueSeries {
|
||||
*/
|
||||
public int getMaxLabelLength() {
|
||||
String minS = format.format(effectiveMin);
|
||||
String maxS = format.format(effectiveMax);
|
||||
String maxS = format.format(getMax());
|
||||
return Math.max(minS.length(), maxS.length());
|
||||
}
|
||||
|
||||
@@ -151,10 +152,16 @@ public class ChartValueSeries {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The rounded up maximum value
|
||||
* @return The minimum of the rounded up max value and the effective max.
|
||||
*/
|
||||
public int getMax() {
|
||||
return effectiveMax;
|
||||
// Return the effective max if no absolute is set
|
||||
// or the effective max is less than the absolute.
|
||||
if ((absoluteMax == Integer.MIN_VALUE) || (effectiveMax < absoluteMax)) {
|
||||
return effectiveMax;
|
||||
} else {
|
||||
return absoluteMax;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,4 +218,16 @@ public class ChartValueSeries {
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getAbsoluteMax() {
|
||||
return absoluteMax;
|
||||
}
|
||||
|
||||
public void setAbsoluteMax(int absoluteMax) {
|
||||
this.absoluteMax = absoluteMax;
|
||||
}
|
||||
|
||||
public boolean hasData() {
|
||||
return monitor.hasData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,10 @@ public class ChartView extends View {
|
||||
*/
|
||||
public static final int ELEVATION_SERIES = 0;
|
||||
public static final int SPEED_SERIES = 1;
|
||||
public static final int NUM_SERIES = 2;
|
||||
public static final int POWER_SERIES = 2;
|
||||
public static final int CADENCE_SERIES = 3;
|
||||
public static final int HEART_RATE_SERIES = 4;
|
||||
public static final int NUM_SERIES = 5;
|
||||
private ChartValueSeries[] series;
|
||||
|
||||
private final ExtremityMonitor xMonitor = new ExtremityMonitor();
|
||||
@@ -172,38 +175,44 @@ public class ChartView extends View {
|
||||
series = new ChartValueSeries[NUM_SERIES];
|
||||
|
||||
// Create the paints
|
||||
Paint fillPaint1 = new Paint();
|
||||
fillPaint1.setStyle(Style.FILL);
|
||||
fillPaint1.setColor(context.getResources().getColor(R.color.green));
|
||||
fillPaint1.setAntiAlias(true);
|
||||
|
||||
Paint fillPaint2 = new Paint();
|
||||
fillPaint2.setStyle(Style.FILL);
|
||||
fillPaint2.setColor(
|
||||
context.getResources().getColor(R.color.blue_transparent));
|
||||
fillPaint2.setAntiAlias(true);
|
||||
|
||||
Paint strokePaint = new Paint();
|
||||
strokePaint.setStyle(Style.STROKE);
|
||||
strokePaint.setColor(context.getResources().getColor(R.color.blue));
|
||||
strokePaint.setAntiAlias(true);
|
||||
Paint fillPaint = new Paint();
|
||||
fillPaint.setStyle(Style.FILL);
|
||||
fillPaint.setColor(context.getResources().getColor(R.color.green));
|
||||
fillPaint.setAntiAlias(true);
|
||||
|
||||
// Create the value series
|
||||
series[ELEVATION_SERIES] =
|
||||
new ChartValueSeries(context,
|
||||
"###,###",
|
||||
fillPaint1,
|
||||
fillPaint,
|
||||
null,
|
||||
100,
|
||||
R.string.elevation);
|
||||
|
||||
series[SPEED_SERIES] =
|
||||
new ChartValueSeries(context,
|
||||
"###,###.0",
|
||||
fillPaint2,
|
||||
strokePaint,
|
||||
5,
|
||||
R.string.speed);
|
||||
series[SPEED_SERIES] = getSeries(context, R.color.blue_transparent,
|
||||
R.color.blue, "###,###.0", R.string.speed);
|
||||
series[POWER_SERIES] = getSeries(context, R.color.red_transparent,
|
||||
R.color.red, "###,###", R.string.power);
|
||||
series[POWER_SERIES].setAbsoluteMax(1500);
|
||||
series[CADENCE_SERIES] = getSeries(context, R.color.purple_transparent,
|
||||
R.color.purple, "###,###", R.string.cadence);
|
||||
series[HEART_RATE_SERIES] = getSeries(context, R.color.yellow_transparent,
|
||||
R.color.yellow, "###,###", R.string.heart_rate);
|
||||
}
|
||||
|
||||
private ChartValueSeries getSeries(Context context,
|
||||
int fillColor, int strokeColor, String format, int seriesName) {
|
||||
Paint fillPaint = new Paint();
|
||||
fillPaint.setStyle(Style.FILL);
|
||||
fillPaint.setColor(context.getResources().getColor(fillColor));
|
||||
fillPaint.setAntiAlias(true);
|
||||
|
||||
Paint strokePaint = new Paint();
|
||||
strokePaint.setStyle(Style.STROKE);
|
||||
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
||||
strokePaint.setAntiAlias(true);
|
||||
return new ChartValueSeries(context, format, fillPaint, strokePaint, 5,
|
||||
seriesName);
|
||||
}
|
||||
|
||||
public void clearWaypoints() {
|
||||
@@ -266,13 +275,25 @@ public class ChartView extends View {
|
||||
*/
|
||||
public synchronized void addDataPoint(double[] theData) {
|
||||
data.add(theData);
|
||||
addDataPointInternal(theData);
|
||||
updateDimensions();
|
||||
setupPath();
|
||||
}
|
||||
|
||||
private void addDataPointInternal(double[] theData) {
|
||||
xMonitor.update(theData[0]);
|
||||
int min = Math.min(series.length, theData.length - 1);
|
||||
for (int i = 0; i < min; i++) {
|
||||
series[i].update(theData[i + 1]);
|
||||
if (!Double.isNaN(theData[i + 1])) {
|
||||
series[i].update(theData[i + 1]);
|
||||
}
|
||||
}
|
||||
// Fill in the extra's if needed.
|
||||
for (int i = theData.length; i < series.length; i++) {
|
||||
if (series[i].hasData()) {
|
||||
series[i].update(0);
|
||||
}
|
||||
}
|
||||
updateDimensions();
|
||||
setupPath();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,11 +305,7 @@ public class ChartView extends View {
|
||||
data.addAll(theData);
|
||||
for (int i = 0; i < theData.size(); i++) {
|
||||
double d[] = theData.get(i);
|
||||
xMonitor.update(d[0]);
|
||||
int min = Math.min(series.length, d.length - 1);
|
||||
for (int j = 0; j < min; j++) {
|
||||
series[j].update(d[j + 1]);
|
||||
}
|
||||
addDataPointInternal(d);
|
||||
}
|
||||
updateDimensions();
|
||||
setupPath();
|
||||
@@ -526,7 +543,7 @@ public class ChartView extends View {
|
||||
|
||||
// Draw the data series
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
cvs.drawPath(c);
|
||||
}
|
||||
}
|
||||
@@ -560,7 +577,7 @@ public class ChartView extends View {
|
||||
c.translate(getScrollX(), 0);
|
||||
drawYAxis(c);
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
drawYLabels(cvs, c);
|
||||
}
|
||||
}
|
||||
@@ -576,13 +593,13 @@ public class ChartView extends View {
|
||||
private void drawSeriesTitles(Canvas c) {
|
||||
int sections = 1;
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
sections++;
|
||||
}
|
||||
}
|
||||
int j = 0;
|
||||
for (ChartValueSeries cvs : series) {
|
||||
if (cvs.isEnabled()) {
|
||||
if (cvs.isEnabled() && cvs.hasData()) {
|
||||
int y = (int) (w * zoomLevel * ((double) (++j) / sections));
|
||||
c.drawText(cvs.getTitle(), y, TOP_BORDER, cvs.getPaint());
|
||||
}
|
||||
@@ -601,11 +618,11 @@ public class ChartView extends View {
|
||||
return;
|
||||
}
|
||||
|
||||
int min = Math.min(series.length, data.get(0).length - 1);
|
||||
|
||||
// All of the data points to the respective series.
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
double[] d = data.get(i);
|
||||
int min = Math.min(series.length, d.length - 1);
|
||||
|
||||
for (int j = 0; j < min; j++) {
|
||||
ChartValueSeries cvs = series[j];
|
||||
Path path = cvs.getPath();
|
||||
@@ -616,17 +633,36 @@ public class ChartView extends View {
|
||||
// Close the path.
|
||||
int yCorner = TOP_BORDER + effectiveHeight;
|
||||
int xCorner = getX(data.get(0)[0]);
|
||||
int min = series.length;
|
||||
for (int j = 0; j < min; j++) {
|
||||
ChartValueSeries cvs = series[j];
|
||||
Path path = cvs.getPath();
|
||||
// Bottom right corner
|
||||
path.lineTo(getX(data.get(data.size() - 1)[0]), yCorner);
|
||||
// Bottom left corner
|
||||
path.lineTo(xCorner, yCorner);
|
||||
// Top right corner
|
||||
path.lineTo(xCorner, getY(cvs, data.get(0)[j + 1]));
|
||||
int first = getFirstPointPopulatedIndex(j + 1);
|
||||
if (first != -1) {
|
||||
// Bottom right corner
|
||||
path.lineTo(getX(data.get(data.size() - 1)[0]), yCorner);
|
||||
// Bottom left corner
|
||||
path.lineTo(xCorner, yCorner);
|
||||
// Top right corner
|
||||
path.lineTo(xCorner, getY(cvs, data.get(first)[j + 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first point which has a series populated.
|
||||
* @param seriesIndex The index of the value series to search for.
|
||||
* @return The index in the first data for the point in the series that has series
|
||||
* index value populated or -1 if none is found.
|
||||
*/
|
||||
private int getFirstPointPopulatedIndex(int seriesIndex) {
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
if (data.get(i).length > seriesIndex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the histogram dimensions.
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -63,8 +64,8 @@ class MenuManager {
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_list_tracks: {
|
||||
Intent startIntent = new Intent(activity, MyTracksList.class);
|
||||
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_TRACK);
|
||||
activity.startActivityForResult(new Intent(activity, MyTracksList.class),
|
||||
MyTracksConstants.SHOW_TRACK);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_list_markers: {
|
||||
@@ -73,20 +74,17 @@ class MenuManager {
|
||||
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_WAYPOINT);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_sensor_state: {
|
||||
return startActivity(SensorStateActivity.class);
|
||||
}
|
||||
case R.id.menu_settings: {
|
||||
Intent startIntent = new Intent(activity, MyTracksSettings.class);
|
||||
activity.startActivity(startIntent);
|
||||
return true;
|
||||
return startActivity(MyTracksSettings.class);
|
||||
}
|
||||
case R.id.menu_aggregated_stats: {
|
||||
Intent startIntent = new Intent(activity, AggregatedStatsActivity.class);
|
||||
activity.startActivity(startIntent);
|
||||
return true;
|
||||
return startActivity(AggregatedStatsActivity.class);
|
||||
}
|
||||
case R.id.menu_help: {
|
||||
Intent startIntent = new Intent(activity, WelcomeActivity.class);
|
||||
activity.startActivity(startIntent);
|
||||
return true;
|
||||
return startActivity(WelcomeActivity.class);
|
||||
}
|
||||
case MyTracksConstants.MENU_CLEAR_MAP: {
|
||||
activity.setSelectedTrackId(-1);
|
||||
@@ -95,4 +93,9 @@ class MenuManager {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean startActivity(Class<? extends Activity> activityClass) {
|
||||
activity.startActivity(new Intent(activity, activityClass));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1128,7 +1128,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
long getSelectedTrackId() {
|
||||
return selectedTrackId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds to track recording service if it is running.
|
||||
*/
|
||||
|
||||
@@ -15,12 +15,17 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.android.apps.mytracks.io.backup.BackupActivityHelper;
|
||||
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
|
||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
@@ -30,6 +35,7 @@ import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.provider.Settings;
|
||||
|
||||
/**
|
||||
* An activity that let's the user see and edit the settings.
|
||||
@@ -53,6 +59,7 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
public static final int DEFAULT_SPLIT_FREQUENCY = 0;
|
||||
|
||||
private BackupPreferencesListener backupListener;
|
||||
|
||||
private SharedPreferences preferences;
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@@ -96,6 +103,19 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
});
|
||||
updatePreferenceUnits(metricUnitsPreference.isChecked());
|
||||
|
||||
ListPreference sensorTypePreference =
|
||||
(ListPreference) findPreference(getString(R.string.sensor_type_key));
|
||||
sensorTypePreference.setOnPreferenceChangeListener(
|
||||
new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference,
|
||||
Object newValue) {
|
||||
updateSensorSettings((String) newValue);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
updateSensorSettings(sensorTypePreference.getValue());
|
||||
|
||||
// Disable TTS announcement preference if not available
|
||||
if (!apiFeatures.hasTextToSpeech()) {
|
||||
IntegerListPreference announcementFrequency =
|
||||
@@ -112,6 +132,7 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
configureBluetoothPreferences();
|
||||
Preference backupNowPreference =
|
||||
findPreference(getString(R.string.backup_to_sd_key));
|
||||
Preference restoreNowPreference =
|
||||
@@ -158,7 +179,15 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
getPreferenceManager().getSharedPreferences()
|
||||
.unregisterOnSharedPreferenceChangeListener(backupListener);
|
||||
|
||||
super.onDestroy();
|
||||
super.onPause();
|
||||
}
|
||||
private void updateSensorSettings(String sensorType) {
|
||||
boolean usesBluetooth =
|
||||
getString(R.string.zephyr_sensor_type).equals(sensorType);
|
||||
findPreference(
|
||||
getString(R.string.bluetooth_sensor_key)).setEnabled(usesBluetooth);
|
||||
findPreference(
|
||||
getString(R.string.bluetooth_pairing_key)).setEnabled(usesBluetooth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,4 +223,44 @@ public class MyTracksSettings extends PreferenceActivity {
|
||||
? R.array.split_frequency_options
|
||||
: R.array.split_frequency_options_ft);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures preference actions related to bluetooth.
|
||||
*/
|
||||
private void configureBluetoothPreferences() {
|
||||
if (BluetoothDeviceUtils.isBluetoothMethodSupported()) {
|
||||
// Populate the list of bluetooth devices
|
||||
populateBluetoothDeviceList();
|
||||
|
||||
// Make the pair devices preference go to the system preferences
|
||||
findPreference(getString(R.string.bluetooth_pairing_key))
|
||||
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
|
||||
startActivity(settingsIntent);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the list preference with all available bluetooth devices.
|
||||
*/
|
||||
private void populateBluetoothDeviceList() {
|
||||
// Build the list of entries and their values
|
||||
List<String> entries = new ArrayList<String>();
|
||||
List<String> entryValues = new ArrayList<String>();
|
||||
|
||||
// The actual devices
|
||||
BluetoothDeviceUtils.getInstance().populateDeviceLists(entries, entryValues);
|
||||
|
||||
CharSequence[] entriesArray = entries.toArray(new CharSequence[entries.size()]);
|
||||
CharSequence[] entryValuesArray = entryValues.toArray(new CharSequence[entryValues.size()]);
|
||||
ListPreference devicesPreference =
|
||||
(ListPreference) findPreference(getString(R.string.bluetooth_sensor_key));
|
||||
devicesPreference.setEntryValues(entryValuesArray);
|
||||
devicesPreference.setEntries(entriesArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import static com.google.android.apps.mytracks.MyTracksConstants.TAG;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* An activity that displays information about sensors.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SensorStateActivity extends Activity {
|
||||
|
||||
private static final SimpleDateFormat TIMESTAMP_FORMAT =
|
||||
new SimpleDateFormat("HH:mm:ss");
|
||||
private static final long REFRESH_PERIOD_MS = 250;
|
||||
|
||||
private final StatsUtilities utils;
|
||||
|
||||
/**
|
||||
* This timer periodically invokes the refresh timer task.
|
||||
*/
|
||||
private Timer timer;
|
||||
|
||||
private final Runnable stateUpdater = new Runnable() {
|
||||
public void run() {
|
||||
updateState();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A task which will update the U/I.
|
||||
*/
|
||||
private class RefreshTask extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
runOnUiThread(stateUpdater);
|
||||
}
|
||||
};
|
||||
|
||||
public SensorStateActivity() {
|
||||
utils = new StatsUtilities(this);
|
||||
Log.w(TAG, "SensorStateActivity()");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Log.w(TAG, "SensorStateActivity.onCreate");
|
||||
|
||||
setContentView(R.layout.sensor_state);
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
timer = new Timer();
|
||||
timer.schedule(new RefreshTask(), REFRESH_PERIOD_MS, REFRESH_PERIOD_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
protected void updateState() {
|
||||
MyTracks mt = MyTracks.getInstance();
|
||||
|
||||
ITrackRecordingService service =
|
||||
mt == null ? null : mt.getTrackRecordingService();
|
||||
if (service == null) {
|
||||
Log.d(MyTracksConstants.TAG, "Could not get track recording service.");
|
||||
updateSensorState(Sensor.SensorState.NONE);
|
||||
updateSensorData(null);
|
||||
return;
|
||||
}
|
||||
Sensor.SensorDataSet sds = null;
|
||||
try {
|
||||
byte[] buff = service.getSensorData();
|
||||
if (buff != null) {
|
||||
sds = Sensor.SensorDataSet.parseFrom(buff);
|
||||
updateSensorData(sds);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Could not read sensor data.", e);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Could not read sensor data.", e);
|
||||
}
|
||||
updateSensorData(sds);
|
||||
|
||||
try {
|
||||
int i = service.getSensorState();
|
||||
updateSensorState(Sensor.SensorState.valueOf(i));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Could not read sensor state.", e);
|
||||
updateSensorState(Sensor.SensorState.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSensorState(Sensor.SensorState state) {
|
||||
TextView sensorTime =
|
||||
((TextView) findViewById(R.id.sensor_state_register));
|
||||
sensorTime.setText(SensorUtils.getStateAsString(state, this));
|
||||
}
|
||||
|
||||
protected void updateSensorData(Sensor.SensorDataSet sds) {
|
||||
if (sds == null) {
|
||||
utils.setUnknown(R.id.sensor_data_time_register);
|
||||
utils.setUnknown(R.id.cadence_state_register);
|
||||
utils.setUnknown(R.id.power_state_register);
|
||||
utils.setUnknown(R.id.heart_rate_register);
|
||||
return;
|
||||
}
|
||||
|
||||
TextView sensorTime =
|
||||
((TextView) findViewById(R.id.sensor_data_time_register));
|
||||
sensorTime.setText(
|
||||
TIMESTAMP_FORMAT.format(new Date(sds.getCreationTime())));
|
||||
|
||||
if (sds.hasPower() && sds.getPower().hasValue()
|
||||
&& sds.getPower().getState() == Sensor.SensorState.SENDING) {
|
||||
utils.setText(R.id.power_state_register,
|
||||
Integer.toString(sds.getPower().getValue()));
|
||||
} else {
|
||||
utils.setText(R.id.power_state_register,
|
||||
SensorUtils.getStateAsString(
|
||||
sds.hasPower()
|
||||
? sds.getPower().getState()
|
||||
: Sensor.SensorState.NONE,
|
||||
this));
|
||||
}
|
||||
|
||||
if (sds.hasCadence() && sds.getCadence().hasValue()
|
||||
&& sds.getCadence().getState() == Sensor.SensorState.SENDING) {
|
||||
utils.setText(R.id.cadence_state_register,
|
||||
Integer.toString(sds.getCadence().getValue()));
|
||||
} else {
|
||||
utils.setText(R.id.cadence_state_register,
|
||||
SensorUtils.getStateAsString(
|
||||
sds.hasCadence()
|
||||
? sds.getCadence().getState()
|
||||
: Sensor.SensorState.NONE,
|
||||
this));
|
||||
}
|
||||
|
||||
if (sds.hasHeartRate() && sds.getHeartRate().hasValue()
|
||||
&& sds.getHeartRate().getState() == Sensor.SensorState.SENDING) {
|
||||
utils.setText(R.id.heart_rate_register,
|
||||
Integer.toString(sds.getHeartRate().getValue()));
|
||||
} else {
|
||||
utils.setText(R.id.heart_rate_register,
|
||||
SensorUtils.getStateAsString(
|
||||
sds.hasHeartRate()
|
||||
? sds.getHeartRate().getState()
|
||||
: Sensor.SensorState.NONE,
|
||||
this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
@@ -38,7 +40,7 @@ import android.util.Log;
|
||||
public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
private static final String DATABASE_NAME = "mytracks.db";
|
||||
private static final int DATABASE_VERSION = 17;
|
||||
private static final int DATABASE_VERSION = 18;
|
||||
private static final int TRACKPOINTS = 1;
|
||||
private static final int TRACKPOINTS_ID = 2;
|
||||
private static final int TRACKS = 3;
|
||||
@@ -70,7 +72,8 @@ public class MyTracksProvider extends ContentProvider {
|
||||
+ TrackPointsColumns.ALTITUDE + " FLOAT, "
|
||||
+ TrackPointsColumns.ACCURACY + " FLOAT, "
|
||||
+ TrackPointsColumns.SPEED + " FLOAT, "
|
||||
+ TrackPointsColumns.BEARING + " FLOAT);");
|
||||
+ TrackPointsColumns.BEARING + " FLOAT, "
|
||||
+ TrackPointsColumns.SENSOR + " BLOB);");
|
||||
db.execSQL("CREATE TABLE " + TRACKS_TABLE + " ("
|
||||
+ TracksColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ TracksColumns.NAME + " STRING, "
|
||||
@@ -132,12 +135,19 @@ public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", which will destroy all old data");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKPOINTS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + WAYPOINTS_TABLE);
|
||||
onCreate(db);
|
||||
if (oldVersion < 17) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", which will destroy all old data");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKPOINTS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKS_TABLE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + WAYPOINTS_TABLE);
|
||||
onCreate(db);
|
||||
} else if (oldVersion >= 17) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", adding sensor column.");
|
||||
db.execSQL("ALTER TABLE " + TRACKPOINTS_TABLE
|
||||
+ " ADD " + TrackPointsColumns.SENSOR + " BLOB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,6 +245,7 @@ public class MyTracksProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
|
||||
@@ -346,6 +357,8 @@ public class MyTracksProvider extends ContentProvider {
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
}
|
||||
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Build query: " + qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null));
|
||||
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null,
|
||||
sortOrder);
|
||||
c.setNotificationUri(getContext().getContentResolver(), url);
|
||||
|
||||
@@ -342,8 +342,14 @@ public class GpxImporter extends DefaultHandler {
|
||||
|
||||
// create new location and set attributes
|
||||
Location loc = new Location(LocationManager.GPS_PROVIDER);
|
||||
loc.setLatitude(Double.parseDouble(latitude));
|
||||
loc.setLongitude(Double.parseDouble(longitude));
|
||||
try {
|
||||
loc.setLatitude(Double.parseDouble(latitude));
|
||||
loc.setLongitude(Double.parseDouble(longitude));
|
||||
} catch (NumberFormatException e) {
|
||||
String msg = createErrorMessage(
|
||||
"Unable to parse lat/long: " + latitude + "/" + longitude);
|
||||
throw new SAXException(msg, e);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
@@ -472,9 +478,14 @@ public class GpxImporter extends DefaultHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void onAltitudeElementEnd() {
|
||||
private void onAltitudeElementEnd() throws SAXException {
|
||||
if (location != null) {
|
||||
location.setAltitude(Double.parseDouble(content));
|
||||
try {
|
||||
location.setAltitude(Double.parseDouble(content));
|
||||
} catch (NumberFormatException e) {
|
||||
String msg = createErrorMessage("Unable to parse altitude: " + content);
|
||||
throw new SAXException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
||||
|
||||
import android.location.Location;
|
||||
@@ -141,6 +144,37 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
||||
pw.print(location.getAltitude());
|
||||
pw.println("</AltitudeMeters>");
|
||||
|
||||
if (location instanceof MyTracksLocation) {
|
||||
SensorDataSet sensorData = ((MyTracksLocation) location).getSensorDataSet();
|
||||
if (sensorData != null) {
|
||||
if (sensorData.hasHeartRate()
|
||||
&& sensorData.getHeartRate().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getHeartRate().hasValue()) {
|
||||
pw.print(" <HeartRateBpm>");
|
||||
pw.print("<Value>");
|
||||
pw.print(sensorData.getHeartRate().getValue());
|
||||
pw.print("</Value>");
|
||||
pw.println("</HeartRateBpm>");
|
||||
}
|
||||
if (sensorData.hasPower()
|
||||
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getPower().hasValue()) {
|
||||
pw.print(" <Extensions>");
|
||||
pw.print("<TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">");
|
||||
pw.print("<Watts>");
|
||||
pw.print(sensorData.getPower().getValue());
|
||||
pw.print("</Watts>");
|
||||
pw.println("</TPX></Extensions>");
|
||||
}
|
||||
if (sensorData.hasCadence()
|
||||
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getCadence().hasValue()) {
|
||||
pw.print(" <Cadence>");
|
||||
pw.print(sensorData.getCadence().getValue());
|
||||
pw.println("</Cadence>");
|
||||
}
|
||||
}
|
||||
}
|
||||
pw.println(" </Trackpoint>");
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,16 @@ import static com.google.android.apps.mytracks.MyTracksConstants.RESUME_TRACK_EX
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorManager;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorManagerFactory;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
@@ -111,6 +116,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
private PeriodicTaskExecuter announcementExecuter;
|
||||
private SplitManager splitManager;
|
||||
|
||||
private SensorManager sensorManager;
|
||||
|
||||
private PreferenceManager prefManager;
|
||||
|
||||
/**
|
||||
@@ -214,7 +221,14 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
|
||||
// Insert the new location:
|
||||
try {
|
||||
Uri pointUri = providerUtils.insertTrackPoint(location, trackId);
|
||||
Location locationToInsert = location;
|
||||
if (sensorManager != null && sensorManager.isEnabled()) {
|
||||
SensorDataSet sd = sensorManager.getSensorDataSet();
|
||||
if (sd != null && sensorManager.isDataValid()) {
|
||||
locationToInsert = new MyTracksLocation(location, sd);
|
||||
}
|
||||
}
|
||||
Uri pointUri = providerUtils.insertTrackPoint(locationToInsert, trackId);
|
||||
int pointId = Integer.parseInt(pointUri.getLastPathSegment());
|
||||
|
||||
// Update the current track:
|
||||
@@ -523,11 +537,15 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
if (lastLocation != null) {
|
||||
distanceToLast = location.distanceTo(lastLocation);
|
||||
}
|
||||
boolean hasSensorData = sensorManager != null
|
||||
&& sensorManager.isEnabled()
|
||||
&& sensorManager.getSensorDataSet() != null
|
||||
&& sensorManager.isDataValid();
|
||||
|
||||
// If the user has been stationary for two recording just record the first
|
||||
// two and ignore the rest. This code will only have an effect if the
|
||||
// maxRecordingDistance = 0
|
||||
if (distanceToLast == 0) {
|
||||
if (distanceToLast == 0 && !hasSensorData) {
|
||||
if (isMoving) {
|
||||
Log.d(MyTracksConstants.TAG, "Found two identical locations.");
|
||||
isMoving = false;
|
||||
@@ -547,7 +565,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"Not recording. More than two identical locations.");
|
||||
}
|
||||
} else if (distanceToLastRecorded > minRecordingDistance) {
|
||||
} else if (distanceToLastRecorded > minRecordingDistance
|
||||
|| hasSensorData) {
|
||||
if (lastLocation != null && !isMoving) {
|
||||
// Last location was the last stationary location. Need to go back and
|
||||
// add it.
|
||||
@@ -627,9 +646,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
|
||||
splitManager = new SplitManager(this);
|
||||
|
||||
sensorManager = SensorManagerFactory.getSensorManager(this);
|
||||
prefManager = new PreferenceManager(this);
|
||||
registerLocationListener();
|
||||
/**
|
||||
|
||||
/*
|
||||
* After 5 min, check every minute that location listener still is
|
||||
* registered and spit out additional debugging info to the logs:
|
||||
*/
|
||||
@@ -711,6 +732,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
shutdownAnnouncer();
|
||||
splitManager.shutdown();
|
||||
splitManager = null;
|
||||
if (sensorManager != null) {
|
||||
sensorManager.onDestroy();
|
||||
sensorManager = null;
|
||||
}
|
||||
|
||||
// Make sure we have no indirect references to this service.
|
||||
locationManager = null;
|
||||
@@ -1022,6 +1047,28 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
}
|
||||
service.onLocationChanged(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSensorData() {
|
||||
if (service.sensorManager == null) {
|
||||
Log.d(MyTracksConstants.TAG, "No sensor manager for data.");
|
||||
return null;
|
||||
}
|
||||
if (service.sensorManager.getSensorDataSet() == null) {
|
||||
Log.d(MyTracksConstants.TAG, "Sensor data set is null.");
|
||||
return null;
|
||||
}
|
||||
return service.sensorManager.getSensorDataSet().toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSensorState() {
|
||||
if (service.sensorManager == null) {
|
||||
Log.d(MyTracksConstants.TAG, "No sensor manager for data.");
|
||||
return Sensor.SensorState.NONE.getNumber();
|
||||
}
|
||||
return service.sensorManager.getSensorState().getNumber();
|
||||
}
|
||||
}
|
||||
|
||||
public long startNewTrack() {
|
||||
@@ -1054,6 +1101,9 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
showNotification();
|
||||
registerLocationListener();
|
||||
splitManager.restore();
|
||||
if (sensorManager != null) {
|
||||
sensorManager.onStartTrack();
|
||||
}
|
||||
|
||||
// Reset the number of auto-resume retries.
|
||||
setAutoResumeTrackRetries(
|
||||
@@ -1096,6 +1146,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
showNotification();
|
||||
long recordedTrackId = recordingTrackId;
|
||||
prefManager.setRecordingTrack(recordingTrackId = -1);
|
||||
|
||||
if (sensorManager != null) {
|
||||
sensorManager.shutdown();
|
||||
}
|
||||
|
||||
releaseWakeLock();
|
||||
|
||||
// Notify the world that we're no longer recording.
|
||||
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class does all the work for setting up and managing Bluetooth
|
||||
* connections with other devices. It has a thread that listens for incoming
|
||||
* connections, a thread for connecting with a device, and a thread for
|
||||
* performing data transmissions when connected.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class BluetoothConnectionManager {
|
||||
// Unique UUID for this application
|
||||
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||
|
||||
private MessageParser parser;
|
||||
|
||||
// Member fields
|
||||
private final BluetoothAdapter adapter;
|
||||
private final Handler handler;
|
||||
private ConnectThread connectThread;
|
||||
private ConnectedThread connectedThread;
|
||||
|
||||
private Sensor.SensorState state;
|
||||
|
||||
// Message types sent from the BluetoothSenorService Handler
|
||||
public static final int MESSAGE_STATE_CHANGE = 1;
|
||||
public static final int MESSAGE_READ = 2;
|
||||
public static final int MESSAGE_WRITE = 3;
|
||||
public static final int MESSAGE_DEVICE_NAME = 4;
|
||||
|
||||
// Key names received from the BluetoothSenorService Handler
|
||||
public static final String DEVICE_NAME = "device_name";
|
||||
|
||||
/**
|
||||
* Constructor. Prepares a new BluetoothSensor session.
|
||||
*
|
||||
* @param context The UI Activity Context
|
||||
* @param handler A Handler to send messages back to the UI Activity
|
||||
*/
|
||||
public BluetoothConnectionManager(Handler handler, MessageParser parser) {
|
||||
this.adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
this.state = Sensor.SensorState.NONE;
|
||||
this.handler = handler;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current state of the sensor connection
|
||||
*
|
||||
* @param state An integer defining the current connection state
|
||||
*/
|
||||
private synchronized void setState(Sensor.SensorState state) {
|
||||
// TODO pretty print this.
|
||||
Log.d(MyTracksConstants.TAG, "setState(" + state + ")");
|
||||
this.state = state;
|
||||
|
||||
// Give the new state to the Handler so the UI Activity can update
|
||||
handler.obtainMessage(MESSAGE_STATE_CHANGE, state.getNumber(), -1).sendToTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current connection state.
|
||||
*/
|
||||
public synchronized Sensor.SensorState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the sensor service. Specifically start AcceptThread to begin a session
|
||||
* in listening (server) mode. Called by the Activity onResume()
|
||||
*/
|
||||
public synchronized void start() {
|
||||
Log.d(MyTracksConstants.TAG, "BluetoothConnectionManager.start()");
|
||||
|
||||
// Cancel any thread attempting to make a connection
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
|
||||
// Cancel any thread currently running a connection
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
|
||||
setState(Sensor.SensorState.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the ConnectThread to initiate a connection to a remote device.
|
||||
*
|
||||
* @param device The BluetoothDevice to connect
|
||||
*/
|
||||
public synchronized void connect(BluetoothDevice device) {
|
||||
Log.d(MyTracksConstants.TAG, "connect to: " + device);
|
||||
|
||||
// Cancel any thread attempting to make a connection
|
||||
if (state == Sensor.SensorState.CONNECTING) {
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel any thread currently running a connection
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
|
||||
// Start the thread to connect with the given device
|
||||
connectThread = new ConnectThread(device);
|
||||
connectThread.start();
|
||||
setState(Sensor.SensorState.CONNECTING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the ConnectedThread to begin managing a Bluetooth connection
|
||||
*
|
||||
* @param socket The BluetoothSocket on which the connection was made
|
||||
* @param device The BluetoothDevice that has been connected
|
||||
*/
|
||||
public synchronized void connected(BluetoothSocket socket,
|
||||
BluetoothDevice device) {
|
||||
Log.d(MyTracksConstants.TAG, "connected");
|
||||
|
||||
// Cancel the thread that completed the connection
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
|
||||
// Cancel any thread currently running a connection
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
|
||||
// Start the thread to manage the connection and perform transmissions
|
||||
connectedThread = new ConnectedThread(socket);
|
||||
connectedThread.start();
|
||||
|
||||
// Send the name of the connected device back to the UI Activity
|
||||
Message msg = handler.obtainMessage(MESSAGE_DEVICE_NAME);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(DEVICE_NAME, device.getName());
|
||||
msg.setData(bundle);
|
||||
handler.sendMessage(msg);
|
||||
|
||||
setState(Sensor.SensorState.CONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all threads
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
Log.d(MyTracksConstants.TAG, "stop()");
|
||||
if (connectThread != null) {
|
||||
connectThread.cancel();
|
||||
connectThread = null;
|
||||
}
|
||||
if (connectedThread != null) {
|
||||
connectedThread.cancel();
|
||||
connectedThread = null;
|
||||
}
|
||||
setState(Sensor.SensorState.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the ConnectedThread in an unsynchronized manner
|
||||
*
|
||||
* @param out The bytes to write
|
||||
* @see ConnectedThread#write(byte[])
|
||||
*/
|
||||
public void write(byte[] out) {
|
||||
// Create temporary object
|
||||
ConnectedThread r;
|
||||
// Synchronize a copy of the ConnectedThread
|
||||
synchronized (this) {
|
||||
if (state != Sensor.SensorState.CONNECTED) {
|
||||
return;
|
||||
}
|
||||
r = connectedThread;
|
||||
}
|
||||
// Perform the write unsynchronized
|
||||
r.write(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the connection attempt failed and notify the UI Activity.
|
||||
*/
|
||||
private void connectionFailed() {
|
||||
setState(Sensor.SensorState.DISCONNECTED);
|
||||
Log.i(MyTracksConstants.TAG, "Bluetooth connection failed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the connection was lost and notify the UI Activity.
|
||||
*/
|
||||
private void connectionLost() {
|
||||
setState(Sensor.SensorState.DISCONNECTED);
|
||||
Log.i(MyTracksConstants.TAG, "Bluetooth connection lost.");
|
||||
}
|
||||
|
||||
/**
|
||||
* This thread runs while attempting to make an outgoing connection with a
|
||||
* device. It runs straight through; the connection either succeeds or fails.
|
||||
*/
|
||||
private class ConnectThread extends Thread {
|
||||
private final BluetoothSocket socket;
|
||||
private final BluetoothDevice device;
|
||||
|
||||
public ConnectThread(BluetoothDevice device) {
|
||||
setName("ConnectThread-" + device.getName());
|
||||
this.device = device;
|
||||
BluetoothSocket tmp = null;
|
||||
|
||||
// Get a BluetoothSocket for a connection with the
|
||||
// given BluetoothDevice
|
||||
try {
|
||||
tmp = getSocket();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "create() failed", e);
|
||||
}
|
||||
socket = tmp;
|
||||
}
|
||||
|
||||
private BluetoothSocket getSocket() throws IOException {
|
||||
try {
|
||||
Class<? extends BluetoothDevice> c = device.getClass();
|
||||
Method insecure = c.getMethod("createInsecureRfcommSocket",
|
||||
Integer.class);
|
||||
insecure.setAccessible(true);
|
||||
return (BluetoothSocket) insecure.invoke(device, 1);
|
||||
} catch (SecurityException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get insecure connect.", e);
|
||||
}
|
||||
return device.createRfcommSocketToServiceRecord(SPP_UUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(MyTracksConstants.TAG, "BEGIN mConnectThread");
|
||||
|
||||
// Always cancel discovery because it will slow down a connection
|
||||
adapter.cancelDiscovery();
|
||||
|
||||
// Make a connection to the BluetoothSocket
|
||||
try {
|
||||
// This is a blocking call and will only return on a
|
||||
// successful connection or an exception
|
||||
socket.connect();
|
||||
} catch (IOException e) {
|
||||
connectionFailed();
|
||||
// Close the socket
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e2) {
|
||||
Log.e(MyTracksConstants.TAG,
|
||||
"unable to close() socket during connection failure", e2);
|
||||
}
|
||||
// Start the service over to restart listening mode
|
||||
BluetoothConnectionManager.this.start();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the ConnectThread because we're done
|
||||
synchronized (BluetoothConnectionManager.this) {
|
||||
connectThread = null;
|
||||
}
|
||||
|
||||
// Start the connected thread
|
||||
connected(socket, device);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "close() of connect socket failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This thread runs during a connection with a remote device. It handles all
|
||||
* incoming and outgoing transmissions.
|
||||
*/
|
||||
private class ConnectedThread extends Thread {
|
||||
private final BluetoothSocket btSocket;
|
||||
private final InputStream mmInStream;
|
||||
private final OutputStream mmOutStream;
|
||||
|
||||
public ConnectedThread(BluetoothSocket socket) {
|
||||
Log.d(MyTracksConstants.TAG, "create ConnectedThread");
|
||||
btSocket = socket;
|
||||
InputStream tmpIn = null;
|
||||
OutputStream tmpOut = null;
|
||||
|
||||
// Get the BluetoothSocket input and output streams
|
||||
try {
|
||||
tmpIn = socket.getInputStream();
|
||||
tmpOut = socket.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "temp sockets not created", e);
|
||||
}
|
||||
|
||||
mmInStream = tmpIn;
|
||||
mmOutStream = tmpOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(MyTracksConstants.TAG, "BEGIN mConnectedThread");
|
||||
byte[] buffer = new byte[parser.getFrameSize()];
|
||||
int bytes;
|
||||
int offset = 0;
|
||||
|
||||
// Keep listening to the InputStream while connected
|
||||
while (true) {
|
||||
try {
|
||||
// Read from the InputStream
|
||||
bytes = mmInStream.read(buffer, offset, parser.getFrameSize() - offset);
|
||||
|
||||
if (bytes < 0) {
|
||||
throw new IOException("EOF reached");
|
||||
}
|
||||
|
||||
offset += bytes;
|
||||
|
||||
if (offset != parser.getFrameSize()) {
|
||||
// partial frame received, call read() again to receive the rest
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if its a valid frame
|
||||
if (!parser.isValid(buffer)) {
|
||||
int index = parser.findNextAlignment(buffer);
|
||||
if (index > 0) {
|
||||
// re-align
|
||||
offset = parser.getFrameSize() - index;
|
||||
System.arraycopy(buffer, index, buffer, 0, offset);
|
||||
Log.w(MyTracksConstants.TAG, "Misaligned data, found new message at " +
|
||||
index + " recovering...");
|
||||
continue;
|
||||
}
|
||||
Log.w(MyTracksConstants.TAG, "Could not find valid data, dropping data");
|
||||
offset = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
|
||||
// Send the obtained bytes to the UI Activity
|
||||
handler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
|
||||
.sendToTarget();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "disconnected", e);
|
||||
connectionLost();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the connected OutStream.
|
||||
*
|
||||
* @param buffer The bytes to write
|
||||
*/
|
||||
public void write(byte[] buffer) {
|
||||
try {
|
||||
mmOutStream.write(buffer);
|
||||
|
||||
// Share the sent message back to the UI Activity
|
||||
handler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer).sendToTarget();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception during write", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
try {
|
||||
btSocket.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "close() of connect socket failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* Manage the connection to a bluetooth sensor.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class BluetoothSensorManager extends SensorManager {
|
||||
|
||||
// Local Bluetooth adapter
|
||||
private BluetoothAdapter bluetoothAdapter = null;
|
||||
|
||||
// Member object for the sensor threads and connections.
|
||||
private BluetoothConnectionManager connectionManager = null;
|
||||
|
||||
// Name of the connected device
|
||||
private String connectedDeviceName = null;
|
||||
|
||||
private Context context = null;
|
||||
|
||||
private Sensor.SensorDataSet sensorDataSet = null;
|
||||
|
||||
private MessageParser parser;
|
||||
|
||||
public BluetoothSensorManager(
|
||||
Context context, MessageParser parser) {
|
||||
this.context = context;
|
||||
this.parser = parser;
|
||||
// Get local Bluetooth adapter
|
||||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
// If BT is not available or not enabled quit.
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupSensor();
|
||||
}
|
||||
|
||||
private void setupSensor() {
|
||||
Log.d(MyTracksConstants.TAG, "setupSensor()");
|
||||
|
||||
// Initialize the BluetoothSensorAdapter to perform bluetooth connections.
|
||||
connectionManager = new BluetoothConnectionManager(messageHandler, parser);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
||||
}
|
||||
|
||||
public void setupChannel() {
|
||||
if (!isEnabled() || connectionManager == null) {
|
||||
Log.w(MyTracksConstants.TAG, "Disabled manager onStartTrack");
|
||||
return;
|
||||
}
|
||||
SharedPreferences prefs =
|
||||
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||
String address =
|
||||
prefs.getString(context.getString(R.string.bluetooth_sensor_key), null);
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
Log.w(MyTracksConstants.TAG, "Connecting to bluetooth sensor: " + address);
|
||||
// Get the BluetoothDevice object
|
||||
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
|
||||
// Attempt to connect to the device
|
||||
connectionManager.connect(device);
|
||||
|
||||
// Performing this check in onResume() covers the case in which BT was
|
||||
// not enabled during onStart(), so we were paused to enable it...
|
||||
// onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
|
||||
if (connectionManager != null) {
|
||||
// Only if the state is STATE_NONE, do we know that we haven't started
|
||||
// already
|
||||
if (connectionManager.getState() == Sensor.SensorState.NONE) {
|
||||
// Start the Bluetooth sensor services
|
||||
Log.w(MyTracksConstants.TAG, "Disabled manager onStartTrack");
|
||||
connectionManager.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
// Stop the Bluetooth sensor services
|
||||
if (connectionManager != null) {
|
||||
connectionManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public Sensor.SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
public Sensor.SensorState getSensorState() {
|
||||
return (connectionManager == null)
|
||||
? Sensor.SensorState.NONE
|
||||
: connectionManager.getState();
|
||||
}
|
||||
|
||||
// The Handler that gets information back from the BluetoothSensorService
|
||||
private final Handler messageHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case BluetoothConnectionManager.MESSAGE_STATE_CHANGE:
|
||||
// TODO should we update the SensorManager state var?
|
||||
Log.i(MyTracksConstants.TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_WRITE:
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_READ:
|
||||
byte[] readBuf = null;
|
||||
try {
|
||||
readBuf = (byte[]) msg.obj;
|
||||
sensorDataSet = parser.parseBuffer(readBuf);
|
||||
Log.d(MyTracksConstants.TAG, "MESSAGE_READ: " + sensorDataSet.toString());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
sensorDataSet = null;
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Got bad sensor data: " + new String(readBuf, 0, readBuf.length),
|
||||
iae);
|
||||
} catch (RuntimeException re) {
|
||||
sensorDataSet = null;
|
||||
Log.i(MyTracksConstants.TAG, "Unexpected exception on read.", re);
|
||||
}
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
|
||||
// save the connected device's name
|
||||
connectedDeviceName =
|
||||
msg.getData().getString(BluetoothConnectionManager.DEVICE_NAME);
|
||||
Toast.makeText(context.getApplicationContext(),
|
||||
"Connected to " + connectedDeviceName, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
/**
|
||||
* An interface for parsing a byte array to a SensorData object.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public interface MessageParser {
|
||||
|
||||
public int getFrameSize();
|
||||
|
||||
public Sensor.SensorDataSet parseBuffer(byte[] readBuff);
|
||||
|
||||
public boolean isValid(byte[] buffer);
|
||||
|
||||
public int findNextAlignment(byte[] buffer);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
/**
|
||||
* Manage the connection to a sensor.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public abstract class SensorManager {
|
||||
|
||||
/**
|
||||
* The maximum age where the data is considered valid.
|
||||
*/
|
||||
public static final long MAX_AGE = 5000;
|
||||
|
||||
/**
|
||||
* Time to wait after a time out to retry.
|
||||
*/
|
||||
public static final int RETRY_PERIOD = 30000;
|
||||
|
||||
private Sensor.SensorState sensorState = Sensor.SensorState.NONE;
|
||||
|
||||
private long sensorStateTimestamp = 0;
|
||||
|
||||
/**
|
||||
* A task to run periodically to check to see if connection was lost.
|
||||
*/
|
||||
private TimerTask checkSensorManager = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"SensorManager state: " + getSensorState());
|
||||
switch (getSensorState()) {
|
||||
case CONNECTING:
|
||||
long age = System.currentTimeMillis() - getSensorStateTimestamp();
|
||||
if (age > 2 * RETRY_PERIOD) {
|
||||
Log.i(MyTracksConstants.TAG, "Retrying connecting SensorManager.");
|
||||
setupChannel();
|
||||
}
|
||||
break;
|
||||
case DISCONNECTED:
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Re-registering disconnected SensoManager.");
|
||||
setupChannel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This timer invokes periodically the checkLocationListener timer task.
|
||||
*/
|
||||
private final Timer timer = new Timer();
|
||||
|
||||
/**
|
||||
* Is the sensor that this manages enabled.
|
||||
* @return true if the sensor is enabled
|
||||
*/
|
||||
public abstract boolean isEnabled();
|
||||
|
||||
/**
|
||||
* This is called when my tracks starts recording a new track.
|
||||
* This is the place to open connections to the sensor.
|
||||
*/
|
||||
public void onStartTrack() {
|
||||
setupChannel();
|
||||
timer.schedule(checkSensorManager, RETRY_PERIOD, RETRY_PERIOD);
|
||||
}
|
||||
|
||||
public abstract void setupChannel();
|
||||
|
||||
public void shutdown() {
|
||||
timer.cancel();
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when my tracks stops recording.
|
||||
* This is the place to shutdown any open connections.
|
||||
*/
|
||||
public abstract void onDestroy();
|
||||
|
||||
/**
|
||||
* Return the last sensor reading.
|
||||
* @return The last reading from the sensor.
|
||||
*/
|
||||
public abstract Sensor.SensorDataSet getSensorDataSet();
|
||||
|
||||
public void setSensorState(Sensor.SensorState sensorState) {
|
||||
this.sensorState = sensorState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current sensor state.
|
||||
* @return The current sensor state.
|
||||
*/
|
||||
public Sensor.SensorState getSensorState() {
|
||||
return sensorState;
|
||||
}
|
||||
|
||||
public long getSensorStateTimestamp() {
|
||||
return sensorStateTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the data is recent enough to be considered valid.
|
||||
*/
|
||||
public boolean isDataValid() {
|
||||
return (System.currentTimeMillis() - getSensorDataSet().getCreationTime()) < MAX_AGE;
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A factory of SensorManagers.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SensorManagerFactory {
|
||||
|
||||
private SensorManagerFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new sensor manager.
|
||||
* @param context Context to fetch system preferences.
|
||||
* @return The sensor manager that corresponds to the sensor type setting.
|
||||
*/
|
||||
public static SensorManager getSensorManager(Context context) {
|
||||
SharedPreferences prefs =
|
||||
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||
if (prefs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String sensor = prefs.getString(context.getString(R.string.sensor_type_key),
|
||||
null);
|
||||
Log.i(MyTracksConstants.TAG, "Creating sensor of type: " + sensor);
|
||||
|
||||
if (sensor == null) {
|
||||
return null;
|
||||
} else if (sensor.equals(context.getString(R.string.zephyr_sensor_type))) {
|
||||
return new ZephyrSensorManager(context);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* A collection of methods for message parsers.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SensorUtils {
|
||||
|
||||
private SensorUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract one unsigned short from a big endian byte array.
|
||||
* @param buffer the buffer to extract the short from
|
||||
* @param index the first byte to be interpreted as part of the short
|
||||
* @return The unsigned short at the given index in the buffer
|
||||
*/
|
||||
public static int unsignedShortToInt(byte[] buffer, int index) {
|
||||
int r = (buffer[index] & 0xFF) << 8;
|
||||
r |= buffer[index + 1] & 0xFF;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static String getStateAsString(Sensor.SensorState state, Context c) {
|
||||
switch (state) {
|
||||
case NONE:
|
||||
return c.getString(R.string.none);
|
||||
case CONNECTING:
|
||||
return c.getString(R.string.connecting);
|
||||
case CONNECTED:
|
||||
return c.getString(R.string.connected);
|
||||
case DISCONNECTED:
|
||||
return c.getString(R.string.disconnected);
|
||||
case SENDING:
|
||||
return c.getString(R.string.sending);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
/**
|
||||
* An implementation of a SensorData parser for Zephyr HRM.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ZephyrMessageParser implements MessageParser {
|
||||
|
||||
@Override
|
||||
public Sensor.SensorDataSet parseBuffer(byte[] buffer) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
sb.append(String.format("%02X", buffer[i]));
|
||||
}
|
||||
Log.w(MyTracksConstants.TAG, "Got zephyr data: " + sb);
|
||||
// The provided units are 1/16 strides per minute.
|
||||
int cadence = SensorUtils.unsignedShortToInt(buffer, 56);
|
||||
// Heart Rate
|
||||
Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder()
|
||||
.setValue(buffer[12] & 0xFF)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
// Cadence
|
||||
//.setCadence(cadence / 16)
|
||||
//.build();
|
||||
Sensor.SensorDataSet sds =
|
||||
Sensor.SensorDataSet.newBuilder()
|
||||
.setCreationTime(System.currentTimeMillis())
|
||||
.setHeartRate(b)
|
||||
.build();
|
||||
|
||||
return sds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(byte[] buffer) {
|
||||
// TODO crc etc.
|
||||
return buffer[0] == 0x02 && buffer[59] == 0x03;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrameSize() {
|
||||
return 60;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findNextAlignment(byte[] buffer) {
|
||||
// TODO test or understand this code.
|
||||
for (int i = 0; i < buffer.length - 1; i++) {
|
||||
if (buffer[i] == 0x03 && buffer[i+1] == 0x02) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class ZephyrSensorManager extends BluetoothSensorManager {
|
||||
|
||||
public ZephyrSensorManager(Context context) {
|
||||
super(context, new ZephyrMessageParser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with bluetooth devices.
|
||||
* This can be used safely even in systems that don't support bluetooth,
|
||||
* in which case a dummy implementation will be used.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public abstract class BluetoothDeviceUtils {
|
||||
public static final String ANY_DEVICE = "any";
|
||||
private static BluetoothDeviceUtils instance;
|
||||
|
||||
/**
|
||||
* Dummy implementation, for systems that don't support bluetooth.
|
||||
*/
|
||||
private static class DummyImpl extends BluetoothDeviceUtils {
|
||||
@Override
|
||||
public void populateDeviceLists(List<String> deviceNames, List<String> deviceAddresses) {
|
||||
// Do nothing - no devices to add
|
||||
}
|
||||
|
||||
@Override
|
||||
public BluetoothDevice findDeviceMatching(String targetDeviceAddress) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Real implementation, for systems that DO support bluetooth.
|
||||
*/
|
||||
private static class RealImpl extends BluetoothDeviceUtils {
|
||||
private final BluetoothAdapter bluetoothAdapter;
|
||||
|
||||
public RealImpl() {
|
||||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
if (bluetoothAdapter == null) {
|
||||
throw new IllegalStateException("Unable to get bluetooth adapter");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateDeviceLists(List<String> deviceNames, List<String> deviceAddresses) {
|
||||
ensureNotDiscovering();
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
BluetoothClass bluetoothClass = device.getBluetoothClass();
|
||||
if (bluetoothClass != null) {
|
||||
// Not really sure what we want, but I know what we don't want.
|
||||
switch(bluetoothClass.getMajorDeviceClass()) {
|
||||
case BluetoothClass.Device.Major.COMPUTER:
|
||||
case BluetoothClass.Device.Major.PHONE:
|
||||
break;
|
||||
default:
|
||||
deviceAddresses.add(device.getAddress());
|
||||
deviceNames.add(device.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BluetoothDevice findDeviceMatching(String targetDeviceAddress) {
|
||||
if (targetDeviceAddress.equals(ANY_DEVICE)) {
|
||||
return findAnyDevice();
|
||||
} else {
|
||||
return findDeviceByAddress(targetDeviceAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the first suitable bluetooth sensor.
|
||||
*/
|
||||
private BluetoothDevice findAnyDevice() {
|
||||
ensureNotDiscovering();
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
// Look for the first paired computer device
|
||||
if (isSuitableDevice(device)) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns a device with the given address, or null if it's not
|
||||
* a suitable sensor.
|
||||
*/
|
||||
private BluetoothDevice findDeviceByAddress(String targetDeviceAddress) {
|
||||
ensureNotDiscovering();
|
||||
|
||||
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(targetDeviceAddress);
|
||||
if (isSuitableDevice(device)) {
|
||||
return device;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the bluetooth adapter is not in discovery mode.
|
||||
*/
|
||||
private void ensureNotDiscovering() {
|
||||
// If it's in discovery mode, cancel that for now.
|
||||
bluetoothAdapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given device is a suitable sensor.
|
||||
*
|
||||
* @param device the device to check
|
||||
* @return true if it's suitable, false otherwise
|
||||
*/
|
||||
private boolean isSuitableDevice(BluetoothDevice device) {
|
||||
// Check that the device is bonded
|
||||
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the proper (singleton) instance of this class.
|
||||
*/
|
||||
public static BluetoothDeviceUtils getInstance() {
|
||||
if (instance == null) {
|
||||
if (!isBluetoothMethodSupported()) {
|
||||
Log.d(MyTracksConstants.TAG, "Using dummy bluetooth utils");
|
||||
instance = new DummyImpl();
|
||||
} else {
|
||||
Log.d(MyTracksConstants.TAG, "Using real bluetooth utils");
|
||||
try {
|
||||
instance = new RealImpl();
|
||||
} catch (IllegalStateException ise) {
|
||||
Log.w(MyTracksConstants.TAG, "Oops, I mean, using dummy bluetooth utils", ise);
|
||||
instance = new DummyImpl();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the given lists with the names and addresses of all suitable
|
||||
* bluetooth devices.
|
||||
*
|
||||
* @param deviceNames the list to populate with user-visible names
|
||||
* @param deviceAddresses the list to populate with device addresses
|
||||
*/
|
||||
public abstract void populateDeviceLists(List<String> deviceNames, List<String> deviceAddresses);
|
||||
|
||||
/**
|
||||
* Finds the bluetooth device with the given address.
|
||||
*
|
||||
* @param targetDeviceAddress the address of the device, or
|
||||
* {@link #ANY_DEVICE} for using the first suitable device
|
||||
* @return the device's descriptor, or null if not found
|
||||
*/
|
||||
public abstract BluetoothDevice findDeviceMatching(String targetDeviceAddress);
|
||||
|
||||
/**
|
||||
* @return whether the bluetooth method is supported on this device
|
||||
*/
|
||||
public static boolean isBluetoothMethodSupported() {
|
||||
return Integer.parseInt(Build.VERSION.SDK) >= 5;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry exported="true" kind="lib" path="libs/protobuf-java-2.3.0-lite.jar"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,53 @@
|
||||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy of
|
||||
// the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations under
|
||||
// the License.
|
||||
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
// An enumeration of the state of a single sensor.
|
||||
enum SensorState {
|
||||
// There is no sensor.
|
||||
NONE = 1;
|
||||
|
||||
// Now initiating an outgoing connection.
|
||||
CONNECTING = 2;
|
||||
|
||||
// Now connected to a remote device.
|
||||
CONNECTED = 3;
|
||||
|
||||
// A state where the sensor is not connected.
|
||||
DISCONNECTED = 4;
|
||||
|
||||
// This is the only state where the sensor data can be used.
|
||||
SENDING = 5;
|
||||
};
|
||||
|
||||
// The state and possible reading for one integer sensor.
|
||||
message SensorData {
|
||||
required SensorState state = 1 [default = NONE];
|
||||
|
||||
// Value is only defined if state = SENDING.
|
||||
optional int32 value = 2;
|
||||
};
|
||||
|
||||
// A message to encapsulate sensor readings associated with a track point.
|
||||
message SensorDataSet {
|
||||
// This is a required field.
|
||||
optional uint64 creation_time = 1 [default = 0];
|
||||
|
||||
optional SensorData heart_rate = 2;
|
||||
optional SensorData cadence = 3;
|
||||
optional SensorData power = 4;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
/**
|
||||
* This class extends the standard Android location with extra information.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class MyTracksLocation extends Location {
|
||||
|
||||
private Sensor.SensorDataSet sensorDataSet = null;
|
||||
|
||||
/**
|
||||
* The id of this location from the provider.
|
||||
*/
|
||||
private int id = -1;
|
||||
|
||||
public MyTracksLocation(Location location, Sensor.SensorDataSet sd) {
|
||||
super(location);
|
||||
this.sensorDataSet = sd;
|
||||
}
|
||||
|
||||
public MyTracksLocation(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public Sensor.SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
public void setSensorData(Sensor.SensorDataSet sensorData) {
|
||||
this.sensorDataSet = sensorData;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
super.reset();
|
||||
sensorDataSet = null;
|
||||
id = -1;
|
||||
}
|
||||
}
|
||||
+26
-3
@@ -16,7 +16,9 @@
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import static com.google.android.apps.mytracks.lib.MyTracksLibConstants.*;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
@@ -75,6 +77,12 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
if (location.hasSpeed()) {
|
||||
values.put(TrackPointsColumns.SPEED, location.getSpeed());
|
||||
}
|
||||
if (location instanceof MyTracksLocation) {
|
||||
MyTracksLocation mtLocation = (MyTracksLocation) location;
|
||||
if (mtLocation.getSensorDataSet() != null) {
|
||||
values.put(TrackPointsColumns.SENSOR, mtLocation.getSensorDataSet().toByteArray());
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -172,7 +180,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
|
||||
@Override
|
||||
public Location createLocation(Cursor cursor) {
|
||||
Location location = new Location("");
|
||||
Location location = new MyTracksLocation("");
|
||||
fillLocation(cursor, location);
|
||||
return location;
|
||||
}
|
||||
@@ -189,6 +197,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
int idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
||||
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
||||
int idxSensor = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR);
|
||||
|
||||
if (!cursor.isNull(idxLatitude)) {
|
||||
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
|
||||
@@ -211,6 +220,18 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
if (!cursor.isNull(idxAccuracy)) {
|
||||
location.setAccuracy(cursor.getFloat(idxAccuracy));
|
||||
}
|
||||
if (location instanceof MyTracksLocation &&
|
||||
!cursor.isNull(idxSensor)) {
|
||||
MyTracksLocation mtLocation = (MyTracksLocation) location;
|
||||
// TODO get the right buffer.
|
||||
Sensor.SensorDataSet sensorData;
|
||||
try {
|
||||
sensorData = Sensor.SensorDataSet.parseFrom(cursor.getBlob(idxSensor));
|
||||
mtLocation.setSensorData(sensorData);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.w(TAG, "Failed to parse sensor data.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -450,7 +471,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
@Override
|
||||
public void deleteAllTracks() {
|
||||
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI, null, null);
|
||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI,
|
||||
null, null);
|
||||
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
||||
}
|
||||
|
||||
@@ -464,7 +486,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
}
|
||||
contentResolver.delete(WaypointsColumns.CONTENT_URI,
|
||||
WaypointsColumns.TRACKID + "=" + trackId, null);
|
||||
contentResolver.delete(TracksColumns.CONTENT_URI, "_id=" + trackId, null);
|
||||
contentResolver.delete(
|
||||
TracksColumns.CONTENT_URI, "_id=" + trackId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,816 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: proto/sensor.proto
|
||||
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
public final class Sensor {
|
||||
private Sensor() {}
|
||||
public static void registerAllExtensions(
|
||||
com.google.protobuf.ExtensionRegistryLite registry) {
|
||||
}
|
||||
public enum SensorState
|
||||
implements com.google.protobuf.Internal.EnumLite {
|
||||
NONE(0, 1),
|
||||
CONNECTING(1, 2),
|
||||
CONNECTED(2, 3),
|
||||
DISCONNECTED(3, 4),
|
||||
SENDING(4, 5),
|
||||
;
|
||||
|
||||
|
||||
public final int getNumber() { return value; }
|
||||
|
||||
public static SensorState valueOf(int value) {
|
||||
switch (value) {
|
||||
case 1: return NONE;
|
||||
case 2: return CONNECTING;
|
||||
case 3: return CONNECTED;
|
||||
case 4: return DISCONNECTED;
|
||||
case 5: return SENDING;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static com.google.protobuf.Internal.EnumLiteMap<SensorState>
|
||||
internalGetValueMap() {
|
||||
return internalValueMap;
|
||||
}
|
||||
private static com.google.protobuf.Internal.EnumLiteMap<SensorState>
|
||||
internalValueMap =
|
||||
new com.google.protobuf.Internal.EnumLiteMap<SensorState>() {
|
||||
public SensorState findValueByNumber(int number) {
|
||||
return SensorState.valueOf(number)
|
||||
; }
|
||||
};
|
||||
|
||||
private final int index;
|
||||
private final int value;
|
||||
private SensorState(int index, int value) {
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(enum_scope:com.google.android.apps.mytracks.content.SensorState)
|
||||
}
|
||||
|
||||
public static final class SensorData extends
|
||||
com.google.protobuf.GeneratedMessageLite {
|
||||
// Use SensorData.newBuilder() to construct.
|
||||
private SensorData() {
|
||||
initFields();
|
||||
}
|
||||
private SensorData(boolean noInit) {}
|
||||
|
||||
private static final SensorData defaultInstance;
|
||||
public static SensorData getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public SensorData getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
// required .com.google.android.apps.mytracks.content.SensorState state = 1 [default = NONE];
|
||||
public static final int STATE_FIELD_NUMBER = 1;
|
||||
private boolean hasState;
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorState state_;
|
||||
public boolean hasState() { return hasState; }
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorState getState() { return state_; }
|
||||
|
||||
// optional int32 value = 2;
|
||||
public static final int VALUE_FIELD_NUMBER = 2;
|
||||
private boolean hasValue;
|
||||
private int value_ = 0;
|
||||
public boolean hasValue() { return hasValue; }
|
||||
public int getValue() { return value_; }
|
||||
|
||||
private void initFields() {
|
||||
state_ = com.google.android.apps.mytracks.content.Sensor.SensorState.NONE;
|
||||
}
|
||||
public final boolean isInitialized() {
|
||||
if (!hasState) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
||||
throws java.io.IOException {
|
||||
getSerializedSize();
|
||||
if (hasState()) {
|
||||
output.writeEnum(1, getState().getNumber());
|
||||
}
|
||||
if (hasValue()) {
|
||||
output.writeInt32(2, getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private int memoizedSerializedSize = -1;
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSerializedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
size = 0;
|
||||
if (hasState()) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeEnumSize(1, getState().getNumber());
|
||||
}
|
||||
if (hasValue()) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeInt32Size(2, getValue());
|
||||
}
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(
|
||||
com.google.protobuf.ByteString data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(
|
||||
com.google.protobuf.ByteString data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(byte[] data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(
|
||||
byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseDelimitedFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
Builder builder = newBuilder();
|
||||
if (builder.mergeDelimitedFrom(input)) {
|
||||
return builder.buildParsed();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseDelimitedFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
Builder builder = newBuilder();
|
||||
if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
|
||||
return builder.buildParsed();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(
|
||||
com.google.protobuf.CodedInputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorData parseFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
|
||||
public static Builder newBuilder() { return Builder.create(); }
|
||||
public Builder newBuilderForType() { return newBuilder(); }
|
||||
public static Builder newBuilder(com.google.android.apps.mytracks.content.Sensor.SensorData prototype) {
|
||||
return newBuilder().mergeFrom(prototype);
|
||||
}
|
||||
public Builder toBuilder() { return newBuilder(this); }
|
||||
|
||||
public static final class Builder extends
|
||||
com.google.protobuf.GeneratedMessageLite.Builder<
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData, Builder> {
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorData result;
|
||||
|
||||
// Construct using com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder()
|
||||
private Builder() {}
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.result = new com.google.android.apps.mytracks.content.Sensor.SensorData();
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected com.google.android.apps.mytracks.content.Sensor.SensorData internalGetResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder clear() {
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot call clear() after build().");
|
||||
}
|
||||
result = new com.google.android.apps.mytracks.content.Sensor.SensorData();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clone() {
|
||||
return create().mergeFrom(result);
|
||||
}
|
||||
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData getDefaultInstanceForType() {
|
||||
return com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return result.isInitialized();
|
||||
}
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData build() {
|
||||
if (result != null && !isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorData buildParsed()
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
result).asInvalidProtocolBufferException();
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData buildPartial() {
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
"build() has already been called on this Builder.");
|
||||
}
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData returnMe = result;
|
||||
result = null;
|
||||
return returnMe;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(com.google.android.apps.mytracks.content.Sensor.SensorData other) {
|
||||
if (other == com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance()) return this;
|
||||
if (other.hasState()) {
|
||||
setState(other.getState());
|
||||
}
|
||||
if (other.hasValue()) {
|
||||
setValue(other.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
while (true) {
|
||||
int tag = input.readTag();
|
||||
switch (tag) {
|
||||
case 0:
|
||||
return this;
|
||||
default: {
|
||||
if (!parseUnknownField(input, extensionRegistry, tag)) {
|
||||
return this;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
int rawValue = input.readEnum();
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorState value = com.google.android.apps.mytracks.content.Sensor.SensorState.valueOf(rawValue);
|
||||
if (value != null) {
|
||||
setState(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
setValue(input.readInt32());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// required .com.google.android.apps.mytracks.content.SensorState state = 1 [default = NONE];
|
||||
public boolean hasState() {
|
||||
return result.hasState();
|
||||
}
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorState getState() {
|
||||
return result.getState();
|
||||
}
|
||||
public Builder setState(com.google.android.apps.mytracks.content.Sensor.SensorState value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
result.hasState = true;
|
||||
result.state_ = value;
|
||||
return this;
|
||||
}
|
||||
public Builder clearState() {
|
||||
result.hasState = false;
|
||||
result.state_ = com.google.android.apps.mytracks.content.Sensor.SensorState.NONE;
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional int32 value = 2;
|
||||
public boolean hasValue() {
|
||||
return result.hasValue();
|
||||
}
|
||||
public int getValue() {
|
||||
return result.getValue();
|
||||
}
|
||||
public Builder setValue(int value) {
|
||||
result.hasValue = true;
|
||||
result.value_ = value;
|
||||
return this;
|
||||
}
|
||||
public Builder clearValue() {
|
||||
result.hasValue = false;
|
||||
result.value_ = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:com.google.android.apps.mytracks.content.SensorData)
|
||||
}
|
||||
|
||||
static {
|
||||
defaultInstance = new SensorData(true);
|
||||
com.google.android.apps.mytracks.content.Sensor.internalForceInit();
|
||||
defaultInstance.initFields();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:com.google.android.apps.mytracks.content.SensorData)
|
||||
}
|
||||
|
||||
public static final class SensorDataSet extends
|
||||
com.google.protobuf.GeneratedMessageLite {
|
||||
// Use SensorDataSet.newBuilder() to construct.
|
||||
private SensorDataSet() {
|
||||
initFields();
|
||||
}
|
||||
private SensorDataSet(boolean noInit) {}
|
||||
|
||||
private static final SensorDataSet defaultInstance;
|
||||
public static SensorDataSet getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public SensorDataSet getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
// optional uint64 creation_time = 1 [default = 0];
|
||||
public static final int CREATION_TIME_FIELD_NUMBER = 1;
|
||||
private boolean hasCreationTime;
|
||||
private long creationTime_ = 0L;
|
||||
public boolean hasCreationTime() { return hasCreationTime; }
|
||||
public long getCreationTime() { return creationTime_; }
|
||||
|
||||
// optional .com.google.android.apps.mytracks.content.SensorData heart_rate = 2;
|
||||
public static final int HEART_RATE_FIELD_NUMBER = 2;
|
||||
private boolean hasHeartRate;
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorData heartRate_;
|
||||
public boolean hasHeartRate() { return hasHeartRate; }
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData getHeartRate() { return heartRate_; }
|
||||
|
||||
// optional .com.google.android.apps.mytracks.content.SensorData cadence = 3;
|
||||
public static final int CADENCE_FIELD_NUMBER = 3;
|
||||
private boolean hasCadence;
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorData cadence_;
|
||||
public boolean hasCadence() { return hasCadence; }
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData getCadence() { return cadence_; }
|
||||
|
||||
// optional .com.google.android.apps.mytracks.content.SensorData power = 4;
|
||||
public static final int POWER_FIELD_NUMBER = 4;
|
||||
private boolean hasPower;
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorData power_;
|
||||
public boolean hasPower() { return hasPower; }
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData getPower() { return power_; }
|
||||
|
||||
private void initFields() {
|
||||
heartRate_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
|
||||
cadence_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
|
||||
power_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
|
||||
}
|
||||
public final boolean isInitialized() {
|
||||
if (hasHeartRate()) {
|
||||
if (!getHeartRate().isInitialized()) return false;
|
||||
}
|
||||
if (hasCadence()) {
|
||||
if (!getCadence().isInitialized()) return false;
|
||||
}
|
||||
if (hasPower()) {
|
||||
if (!getPower().isInitialized()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
||||
throws java.io.IOException {
|
||||
getSerializedSize();
|
||||
if (hasCreationTime()) {
|
||||
output.writeUInt64(1, getCreationTime());
|
||||
}
|
||||
if (hasHeartRate()) {
|
||||
output.writeMessage(2, getHeartRate());
|
||||
}
|
||||
if (hasCadence()) {
|
||||
output.writeMessage(3, getCadence());
|
||||
}
|
||||
if (hasPower()) {
|
||||
output.writeMessage(4, getPower());
|
||||
}
|
||||
}
|
||||
|
||||
private int memoizedSerializedSize = -1;
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSerializedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
size = 0;
|
||||
if (hasCreationTime()) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt64Size(1, getCreationTime());
|
||||
}
|
||||
if (hasHeartRate()) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(2, getHeartRate());
|
||||
}
|
||||
if (hasCadence()) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(3, getCadence());
|
||||
}
|
||||
if (hasPower()) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(4, getPower());
|
||||
}
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(
|
||||
com.google.protobuf.ByteString data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(
|
||||
com.google.protobuf.ByteString data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(byte[] data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(
|
||||
byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseDelimitedFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
Builder builder = newBuilder();
|
||||
if (builder.mergeDelimitedFrom(input)) {
|
||||
return builder.buildParsed();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseDelimitedFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
Builder builder = newBuilder();
|
||||
if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
|
||||
return builder.buildParsed();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(
|
||||
com.google.protobuf.CodedInputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static com.google.android.apps.mytracks.content.Sensor.SensorDataSet parseFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
|
||||
public static Builder newBuilder() { return Builder.create(); }
|
||||
public Builder newBuilderForType() { return newBuilder(); }
|
||||
public static Builder newBuilder(com.google.android.apps.mytracks.content.Sensor.SensorDataSet prototype) {
|
||||
return newBuilder().mergeFrom(prototype);
|
||||
}
|
||||
public Builder toBuilder() { return newBuilder(this); }
|
||||
|
||||
public static final class Builder extends
|
||||
com.google.protobuf.GeneratedMessageLite.Builder<
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorDataSet, Builder> {
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorDataSet result;
|
||||
|
||||
// Construct using com.google.android.apps.mytracks.content.Sensor.SensorDataSet.newBuilder()
|
||||
private Builder() {}
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.result = new com.google.android.apps.mytracks.content.Sensor.SensorDataSet();
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected com.google.android.apps.mytracks.content.Sensor.SensorDataSet internalGetResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder clear() {
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot call clear() after build().");
|
||||
}
|
||||
result = new com.google.android.apps.mytracks.content.Sensor.SensorDataSet();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clone() {
|
||||
return create().mergeFrom(result);
|
||||
}
|
||||
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorDataSet getDefaultInstanceForType() {
|
||||
return com.google.android.apps.mytracks.content.Sensor.SensorDataSet.getDefaultInstance();
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return result.isInitialized();
|
||||
}
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorDataSet build() {
|
||||
if (result != null && !isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
private com.google.android.apps.mytracks.content.Sensor.SensorDataSet buildParsed()
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
result).asInvalidProtocolBufferException();
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorDataSet buildPartial() {
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
"build() has already been called on this Builder.");
|
||||
}
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorDataSet returnMe = result;
|
||||
result = null;
|
||||
return returnMe;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(com.google.android.apps.mytracks.content.Sensor.SensorDataSet other) {
|
||||
if (other == com.google.android.apps.mytracks.content.Sensor.SensorDataSet.getDefaultInstance()) return this;
|
||||
if (other.hasCreationTime()) {
|
||||
setCreationTime(other.getCreationTime());
|
||||
}
|
||||
if (other.hasHeartRate()) {
|
||||
mergeHeartRate(other.getHeartRate());
|
||||
}
|
||||
if (other.hasCadence()) {
|
||||
mergeCadence(other.getCadence());
|
||||
}
|
||||
if (other.hasPower()) {
|
||||
mergePower(other.getPower());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
while (true) {
|
||||
int tag = input.readTag();
|
||||
switch (tag) {
|
||||
case 0:
|
||||
return this;
|
||||
default: {
|
||||
if (!parseUnknownField(input, extensionRegistry, tag)) {
|
||||
return this;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
setCreationTime(input.readUInt64());
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData.Builder subBuilder = com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder();
|
||||
if (hasHeartRate()) {
|
||||
subBuilder.mergeFrom(getHeartRate());
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
setHeartRate(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
case 26: {
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData.Builder subBuilder = com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder();
|
||||
if (hasCadence()) {
|
||||
subBuilder.mergeFrom(getCadence());
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
setCadence(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
case 34: {
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData.Builder subBuilder = com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder();
|
||||
if (hasPower()) {
|
||||
subBuilder.mergeFrom(getPower());
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
setPower(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// optional uint64 creation_time = 1 [default = 0];
|
||||
public boolean hasCreationTime() {
|
||||
return result.hasCreationTime();
|
||||
}
|
||||
public long getCreationTime() {
|
||||
return result.getCreationTime();
|
||||
}
|
||||
public Builder setCreationTime(long value) {
|
||||
result.hasCreationTime = true;
|
||||
result.creationTime_ = value;
|
||||
return this;
|
||||
}
|
||||
public Builder clearCreationTime() {
|
||||
result.hasCreationTime = false;
|
||||
result.creationTime_ = 0L;
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional .com.google.android.apps.mytracks.content.SensorData heart_rate = 2;
|
||||
public boolean hasHeartRate() {
|
||||
return result.hasHeartRate();
|
||||
}
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData getHeartRate() {
|
||||
return result.getHeartRate();
|
||||
}
|
||||
public Builder setHeartRate(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
result.hasHeartRate = true;
|
||||
result.heartRate_ = value;
|
||||
return this;
|
||||
}
|
||||
public Builder setHeartRate(com.google.android.apps.mytracks.content.Sensor.SensorData.Builder builderForValue) {
|
||||
result.hasHeartRate = true;
|
||||
result.heartRate_ = builderForValue.build();
|
||||
return this;
|
||||
}
|
||||
public Builder mergeHeartRate(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
|
||||
if (result.hasHeartRate() &&
|
||||
result.heartRate_ != com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance()) {
|
||||
result.heartRate_ =
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder(result.heartRate_).mergeFrom(value).buildPartial();
|
||||
} else {
|
||||
result.heartRate_ = value;
|
||||
}
|
||||
result.hasHeartRate = true;
|
||||
return this;
|
||||
}
|
||||
public Builder clearHeartRate() {
|
||||
result.hasHeartRate = false;
|
||||
result.heartRate_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional .com.google.android.apps.mytracks.content.SensorData cadence = 3;
|
||||
public boolean hasCadence() {
|
||||
return result.hasCadence();
|
||||
}
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData getCadence() {
|
||||
return result.getCadence();
|
||||
}
|
||||
public Builder setCadence(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
result.hasCadence = true;
|
||||
result.cadence_ = value;
|
||||
return this;
|
||||
}
|
||||
public Builder setCadence(com.google.android.apps.mytracks.content.Sensor.SensorData.Builder builderForValue) {
|
||||
result.hasCadence = true;
|
||||
result.cadence_ = builderForValue.build();
|
||||
return this;
|
||||
}
|
||||
public Builder mergeCadence(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
|
||||
if (result.hasCadence() &&
|
||||
result.cadence_ != com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance()) {
|
||||
result.cadence_ =
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder(result.cadence_).mergeFrom(value).buildPartial();
|
||||
} else {
|
||||
result.cadence_ = value;
|
||||
}
|
||||
result.hasCadence = true;
|
||||
return this;
|
||||
}
|
||||
public Builder clearCadence() {
|
||||
result.hasCadence = false;
|
||||
result.cadence_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional .com.google.android.apps.mytracks.content.SensorData power = 4;
|
||||
public boolean hasPower() {
|
||||
return result.hasPower();
|
||||
}
|
||||
public com.google.android.apps.mytracks.content.Sensor.SensorData getPower() {
|
||||
return result.getPower();
|
||||
}
|
||||
public Builder setPower(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
result.hasPower = true;
|
||||
result.power_ = value;
|
||||
return this;
|
||||
}
|
||||
public Builder setPower(com.google.android.apps.mytracks.content.Sensor.SensorData.Builder builderForValue) {
|
||||
result.hasPower = true;
|
||||
result.power_ = builderForValue.build();
|
||||
return this;
|
||||
}
|
||||
public Builder mergePower(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
|
||||
if (result.hasPower() &&
|
||||
result.power_ != com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance()) {
|
||||
result.power_ =
|
||||
com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder(result.power_).mergeFrom(value).buildPartial();
|
||||
} else {
|
||||
result.power_ = value;
|
||||
}
|
||||
result.hasPower = true;
|
||||
return this;
|
||||
}
|
||||
public Builder clearPower() {
|
||||
result.hasPower = false;
|
||||
result.power_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:com.google.android.apps.mytracks.content.SensorDataSet)
|
||||
}
|
||||
|
||||
static {
|
||||
defaultInstance = new SensorDataSet(true);
|
||||
com.google.android.apps.mytracks.content.Sensor.internalForceInit();
|
||||
defaultInstance.initFields();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:com.google.android.apps.mytracks.content.SensorDataSet)
|
||||
}
|
||||
|
||||
|
||||
static {
|
||||
}
|
||||
|
||||
public static void internalForceInit() {}
|
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
}
|
||||
@@ -42,4 +42,5 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
public static final String TIME = "time";
|
||||
public static final String ACCURACY = "accuracy";
|
||||
public static final String SPEED = "speed";
|
||||
public static final String SENSOR = "sensor";
|
||||
}
|
||||
|
||||
@@ -84,4 +84,18 @@ interface ITrackRecordingService {
|
||||
* Deletes all the stored tracks.
|
||||
*/
|
||||
void deleteAllTracks();
|
||||
|
||||
/**
|
||||
* The current sensor data.
|
||||
* The data is returned as a byte array which is a binary version of a
|
||||
* Sensor.SensorDataSet object.
|
||||
* @return the current sensor data or null if there is none.
|
||||
*/
|
||||
byte[] getSensorData();
|
||||
|
||||
/**
|
||||
* The current state of the sensor manager.
|
||||
* The value is the value of a Sensor.SensorState enum.
|
||||
*/
|
||||
int getSensorState();
|
||||
}
|
||||
|
||||
@@ -103,6 +103,11 @@ public class ExtremityMonitor {
|
||||
public void setMax(double max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public boolean hasData() {
|
||||
return min != Double.POSITIVE_INFINITY
|
||||
&& max != Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -19,9 +19,9 @@ import static com.google.android.testing.mocking.AndroidMock.eq;
|
||||
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
|
||||
import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
@@ -77,7 +77,7 @@ public class GpxImporterTest extends AndroidTestCase {
|
||||
DATE_FORMAT2.setTimeZone(utc);
|
||||
}
|
||||
|
||||
// TODO use real files from different sources with more track points
|
||||
// TODO: use real files from different sources with more track points.
|
||||
private static final String VALID_TEST_GPX = "<gpx><trk><name><![CDATA["
|
||||
+ TRACK_NAME + "]]></name><desc><![CDATA[" + TRACK_DESC
|
||||
+ "]]></desc><trkseg>" + "<trkpt lat=\"" + TRACK_LAT_1 + "\" lon=\""
|
||||
@@ -93,6 +93,12 @@ public class GpxImporterTest extends AndroidTestCase {
|
||||
.replaceAll(TRACK_LAT_1, "1000.0");
|
||||
private static final String INVALID_TIME_TEST_GPX = VALID_TEST_GPX
|
||||
.replaceAll(TRACK_TIME_1, "invalid");
|
||||
private static final String INVALID_ALTITUDE_TEST_GPX = VALID_TEST_GPX
|
||||
.replaceAll(TRACK_ELE_1, "invalid");
|
||||
private static final String INVALID_LATITUDE_TEST_GPX = VALID_TEST_GPX
|
||||
.replaceAll(TRACK_LAT_1, "invalid");
|
||||
private static final String INVALID_LONGITUDE_TEST_GPX = VALID_TEST_GPX
|
||||
.replaceAll(TRACK_LON_1, "invalid");
|
||||
|
||||
private static final long TRACK_ID = 1;
|
||||
private static final long TRACK_POINT_ID_1 = 1;
|
||||
@@ -122,7 +128,7 @@ public class GpxImporterTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test import success
|
||||
* Test import success.
|
||||
*/
|
||||
public void testImportSuccess() throws Exception {
|
||||
Capture<Track> trackParam = new Capture<Track>();
|
||||
@@ -171,7 +177,7 @@ public class GpxImporterTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid location - track should be deleted
|
||||
* Test with invalid location - track should be deleted.
|
||||
*/
|
||||
public void testImportLocationFailure() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
@@ -179,7 +185,7 @@ public class GpxImporterTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid time - track should be deleted
|
||||
* Test with invalid time - track should be deleted.
|
||||
*/
|
||||
public void testImportTimeFailure() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
@@ -187,13 +193,37 @@ public class GpxImporterTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid xml - track should be deleted
|
||||
* Test with invalid xml - track should be deleted.
|
||||
*/
|
||||
public void testImportXMLFailure() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_XML_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid altitude - track should be deleted.
|
||||
*/
|
||||
public void testImportInvalidAltitude() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_ALTITUDE_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid latitude - track should be deleted.
|
||||
*/
|
||||
public void testImportInvalidLatitude() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_LATITUDE_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid longitude - track should be deleted.
|
||||
*/
|
||||
public void testImportInvalidLongitude() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_LONGITUDE_TEST_GPX);
|
||||
}
|
||||
|
||||
private void testInvalidXML(String xml) throws ParserConfigurationException,
|
||||
IOException {
|
||||
expect(providerUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import android.location.Location;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -38,11 +39,11 @@ public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
||||
/**
|
||||
* Asserts that the given tags describe the given points, in the same order.
|
||||
*/
|
||||
private void assertTagsMatchPoints(List<Element> tags, Location... locs) {
|
||||
private void assertTagsMatchPoints(List<Element> tags, MyTracksLocation... locs) {
|
||||
assertEquals(locs.length, tags.size());
|
||||
for (int i = 0; i < locs.length; i++) {
|
||||
Element tag = tags.get(i);
|
||||
Location loc = locs[i];
|
||||
MyTracksLocation loc = locs[i];
|
||||
|
||||
assertTagMatchesLocation(tag, loc);
|
||||
}
|
||||
@@ -51,7 +52,7 @@ public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
||||
/**
|
||||
* Asserts that the given tag describes the given location.
|
||||
*/
|
||||
private void assertTagMatchesLocation(Element tag, Location loc) {
|
||||
private void assertTagMatchesLocation(Element tag, MyTracksLocation loc) {
|
||||
Element posTag = getChildElement(tag, "Position");
|
||||
assertEquals(Double.toString(loc.getLatitude()),
|
||||
getChildTextValue(posTag, "LatitudeDegrees"));
|
||||
@@ -62,5 +63,16 @@ public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
||||
getChildTextValue(tag, "Time"));
|
||||
assertEquals(Double.toString(loc.getAltitude()),
|
||||
getChildTextValue(tag, "AltitudeMeters"));
|
||||
assertTrue(loc.getSensorDataSet() != null);
|
||||
Sensor.SensorDataSet sds = loc.getSensorDataSet();
|
||||
|
||||
List<Element> bpm = getChildElements(tag, "HeartRateBpm", 1);
|
||||
assertEquals(Integer.toString(sds.getHeartRate().getValue()),
|
||||
getChildTextValue(bpm.get(0), "Value"));
|
||||
|
||||
List<Element> ext = getChildElements(tag, "Extensions", 1);
|
||||
List<Element> tpx = getChildElements(ext.get(0), "TPX", 1);
|
||||
assertEquals(Integer.toString(sds.getPower().getValue()),
|
||||
getChildTextValue(tpx.get(0), "Watts"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
|
||||
import android.location.Location;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
@@ -46,7 +47,7 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
private static final int BUFFER_SIZE = 10240;
|
||||
protected static final long TRACK_ID = 12345L;
|
||||
protected Track track;
|
||||
protected Location location1, location2, location3, location4;
|
||||
protected MyTracksLocation location1, location2, location3, location4;
|
||||
protected Waypoint wp1, wp2;
|
||||
|
||||
@Override
|
||||
@@ -58,10 +59,10 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
track.setName(TRACK_NAME);
|
||||
track.setDescription(TRACK_DESCRIPTION);
|
||||
|
||||
location1 = new Location("mock");
|
||||
location2 = new Location("mock");
|
||||
location3 = new Location("mock");
|
||||
location4 = new Location("mock");
|
||||
location1 = new MyTracksLocation("mock");
|
||||
location2 = new MyTracksLocation("mock");
|
||||
location3 = new MyTracksLocation("mock");
|
||||
location4 = new MyTracksLocation("mock");
|
||||
populateLocations(location1, location2, location3, location4);
|
||||
|
||||
wp1 = new Waypoint();
|
||||
@@ -77,13 +78,24 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
/**
|
||||
* Populates the given locations with coordinates and time.
|
||||
*/
|
||||
protected void populateLocations(Location... locs) {
|
||||
protected void populateLocations(MyTracksLocation... locs) {
|
||||
for (int i = 0; i < locs.length; i++) {
|
||||
Location loc = locs[i];
|
||||
MyTracksLocation loc = locs[i];
|
||||
loc.setAltitude(i * 1000);
|
||||
loc.setLatitude(i);
|
||||
loc.setLongitude(-i);
|
||||
loc.setTime(10000000 + i * 1000);
|
||||
Sensor.SensorData.Builder hr = Sensor.SensorData.newBuilder()
|
||||
.setValue(100 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder power = Sensor.SensorData.newBuilder()
|
||||
.setValue(400 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorDataSet sds =
|
||||
Sensor.SensorDataSet.newBuilder().setHeartRate(hr.build())
|
||||
.setPower(power)
|
||||
.build();
|
||||
loc.setSensorData(sds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +151,7 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all child elements of a given parent which gave the given name.
|
||||
* Returns all child elements of a given parent which have the given name.
|
||||
*
|
||||
* @param parent the parent to get children from
|
||||
* @param elementName the element name to look for
|
||||
|
||||
+11
@@ -692,6 +692,17 @@ public class TrackRecordingServiceTest
|
||||
functionalTest(R.string.min_required_accuracy_key, 500);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noSensorType() throws Exception {
|
||||
functionalTest(R.string.sensor_type_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_zephyrSensorType() throws Exception {
|
||||
functionalTest(R.string.sensor_type_key,
|
||||
context.getString(R.string.zephyr_sensor_type));
|
||||
}
|
||||
|
||||
private ITrackRecordingService bindAndGetService(Intent intent) {
|
||||
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(
|
||||
bindService(intent));
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
public class SensorManagerFactoryTest extends AndroidTestCase {
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
sharedPreferences = getContext().getSharedPreferences(
|
||||
MyTracksSettings.SETTINGS_NAME, 0);
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testDefaultSettings() throws Exception {
|
||||
assertNull(SensorManagerFactory.getSensorManager(getContext()));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCreateZephyr() throws Exception {
|
||||
sharedPreferences.edit()
|
||||
.putString(getContext().getString(R.string.sensor_type_key),
|
||||
getContext().getString(R.string.zephyr_sensor_type))
|
||||
.commit();
|
||||
SensorManager sm = SensorManagerFactory.getSensorManager(getContext());
|
||||
assertNotNull(sm);
|
||||
assertTrue(sm instanceof ZephyrSensorManager);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class ZephyrMessageParserTest extends TestCase {
|
||||
|
||||
ZephyrMessageParser parser = new ZephyrMessageParser();
|
||||
|
||||
public void testIsValid() {
|
||||
byte[] buf = new byte[60];
|
||||
assertFalse(parser.isValid(buf));
|
||||
buf[0] = 0x02;
|
||||
assertFalse(parser.isValid(buf));
|
||||
buf[59] = 0x03;
|
||||
assertTrue(parser.isValid(buf));
|
||||
}
|
||||
|
||||
public void testParseBuffer() {
|
||||
byte[] buf = new byte[60];
|
||||
buf[12] = 50;
|
||||
Sensor.SensorDataSet sds = parser.parseBuffer(buf);
|
||||
assertTrue(sds.hasHeartRate());
|
||||
assertTrue(sds.getHeartRate().getState() == Sensor.SensorState.SENDING);
|
||||
assertEquals(50, sds.getHeartRate().getValue());
|
||||
}
|
||||
|
||||
public void testFindNextAlignment() {
|
||||
byte[] buf = new byte[60];
|
||||
assertEquals(-1, parser.findNextAlignment(buf));
|
||||
buf[10] = 0x03;
|
||||
buf[11] = 0x02;
|
||||
assertEquals(10, parser.findNextAlignment(buf));
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,6 @@
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="src" path="MyTracksLib_src"/>
|
||||
<classpathentry exported="true" kind="lib" path="/MyTracksLib/libs/protobuf-java-2.3.0-lite.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
Reference in New Issue
Block a user