forked from upstream-mirrors/OpenTracks
Merging sensor and library changes together.
This commit is contained in:
@@ -7,5 +7,6 @@
|
|||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry kind="src" path="MyTracksLib_src"/>
|
<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"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ limitations under the License.
|
|||||||
android:label="@string/statistics"
|
android:label="@string/statistics"
|
||||||
android:exported="false" />
|
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"
|
<activity android:name="com.google.android.apps.mytracks.ChartActivity"
|
||||||
android:label="@string/elevation_label"
|
android:label="@string/elevation_label"
|
||||||
android:exported="false" />
|
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.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<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 -->
|
<!-- Permissions for uploading tracks -->
|
||||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
<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_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/speed"/>
|
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>
|
</LinearLayout>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableLayout>
|
</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
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
style="@style/StatsRegister.Big"
|
||||||
android:id="@+id/speed_register"
|
android:id="@+id/speed_register"
|
||||||
android:layout_column="1"
|
android:layout_column="1"
|
||||||
android:layout_marginBottom="-13dip"
|
android:layout_span="4"/>
|
||||||
android:layout_marginTop="-13dip"
|
|
||||||
android:layout_span="4"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="50sp" />
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:gravity="bottom"
|
android:gravity="bottom"
|
||||||
android:layout_column="5"
|
android:layout_column="5"
|
||||||
@@ -62,24 +57,12 @@ limitations under the License.
|
|||||||
android:layout_width="fill_parent" />
|
android:layout_width="fill_parent" />
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/speed_unit_label_top"
|
android:id="@+id/speed_unit_label_top"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="2dip"
|
|
||||||
android:layout_marginTop="2dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsDivider" />
|
||||||
android:layout_height="3dip"
|
|
||||||
android:layout_width="fill_parent" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/speed_unit_label_bottom"
|
android:id="@+id/speed_unit_label_bottom"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="2dip"
|
|
||||||
android:layout_marginTop="2dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
@@ -100,43 +83,25 @@ limitations under the License.
|
|||||||
<TableRow
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
style="@style/StatsRegister"
|
||||||
android:id="@+id/total_distance_register"
|
android:id="@+id/total_distance_register"
|
||||||
android:layout_column="1"
|
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" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="left"
|
|
||||||
android:id="@+id/total_distance_unit_label"
|
android:id="@+id/total_distance_unit_label"
|
||||||
android:layout_column="2"
|
android:layout_column="2"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsUnitSolo" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/mile"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
<!-- TODO 4/08/2009
|
<!-- TODO 4/08/2009
|
||||||
The following is a bad hack to get a spacer the same size as the
|
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
|
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
|
set the view height properly. fill_parent on this element causes as
|
||||||
NullPointerException. This same filler is repeated on each line.-->
|
NullPointerException. This same filler is repeated on each line.-->
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsSpacer"
|
||||||
android:layout_column="3"
|
android:layout_column="3"/>
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_width="5dip"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/max_speed_register"
|
android:id="@+id/max_speed_register"
|
||||||
android:layout_column="4"
|
style="@style/StatsRegister"
|
||||||
android:layout_marginBottom="-9dip"
|
android:layout_column="4" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:gravity="bottom"
|
android:gravity="bottom"
|
||||||
android:layout_column="5"
|
android:layout_column="5"
|
||||||
@@ -145,24 +110,12 @@ limitations under the License.
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/max_speed_unit_label_top"
|
android:id="@+id/max_speed_unit_label_top"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="-1dip"
|
|
||||||
android:layout_marginTop="-1dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsDivider" />
|
||||||
android:layout_height="3dip"
|
|
||||||
android:layout_width="fill_parent" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/max_speed_unit_label_bottom"
|
android:id="@+id/max_speed_unit_label_bottom"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="-1dip"
|
|
||||||
android:layout_marginTop="-1dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
@@ -182,33 +135,19 @@ limitations under the License.
|
|||||||
<TableRow
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/total_time_register"
|
android:id="@+id/total_time_register"
|
||||||
android:layout_column="1"
|
android:layout_column="1"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_span="2"
|
android:layout_span="2"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:text="@string/unknown"
|
style="@style/StatsRegister" />
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsSpacer"
|
||||||
android:layout_column="3"
|
android:layout_column="3"/>
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_width="5dip"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/average_speed_register"
|
android:id="@+id/average_speed_register"
|
||||||
android:layout_column="4"
|
android:layout_column="4"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsRegister" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:gravity="bottom"
|
android:gravity="bottom"
|
||||||
android:layout_column="5"
|
android:layout_column="5"
|
||||||
@@ -217,24 +156,12 @@ limitations under the License.
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/average_speed_unit_label_top"
|
android:id="@+id/average_speed_unit_label_top"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="-1dip"
|
|
||||||
android:layout_marginTop="-1dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsDivider" />
|
||||||
android:layout_height="3dip"
|
|
||||||
android:layout_width="fill_parent" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/average_speed_unit_label_bottom"
|
android:id="@+id/average_speed_unit_label_bottom"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="-1dip"
|
|
||||||
android:layout_marginTop="-1dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
@@ -254,33 +181,18 @@ limitations under the License.
|
|||||||
<TableRow
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/moving_time_register"
|
android:id="@+id/moving_time_register"
|
||||||
android:layout_column="1"
|
android:layout_column="1"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_span="2"
|
android:layout_span="2"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:text="@string/unknown"
|
style="@style/StatsRegister" />
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsSpacer"
|
||||||
android:layout_column="3"
|
android:layout_column="3"/>
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_width="5dip"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/average_moving_speed_register"
|
android:id="@+id/average_moving_speed_register"
|
||||||
android:layout_column="4"
|
style="@style/StatsRegister" />
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:gravity="bottom"
|
android:gravity="bottom"
|
||||||
android:layout_column="5"
|
android:layout_column="5"
|
||||||
@@ -289,24 +201,14 @@ limitations under the License.
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/average_moving_speed_unit_label_top"
|
android:id="@+id/average_moving_speed_unit_label_top"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="-1dip"
|
|
||||||
android:layout_marginTop="-1dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsDivider" />
|
||||||
android:layout_height="3dip"
|
android:layout_height="3dip"
|
||||||
android:layout_width="fill_parent" />
|
android:layout_width="fill_parent" />
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/average_moving_speed_unit_label_bottom"
|
android:id="@+id/average_moving_speed_unit_label_bottom"
|
||||||
android:gravity="bottom"
|
style="@style/StatsUnit" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="-1dip"
|
|
||||||
android:layout_marginTop="-1dip"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
@@ -326,46 +228,24 @@ limitations under the License.
|
|||||||
<TableRow
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/elevation_register"
|
android:id="@+id/elevation_register"
|
||||||
android:layout_column="1"
|
android:layout_column="1"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsRegister" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="left"
|
|
||||||
android:id="@+id/elevation_unit_label"
|
android:id="@+id/elevation_unit_label"
|
||||||
android:layout_column="2"
|
android:layout_column="2"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsUnitSolo" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/feet"
|
|
||||||
android:textColor="@color/stat_label_text"/>
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsSpacer"
|
||||||
android:layout_column="3"
|
android:layout_column="3"/>
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_width="5dip"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/elevation_gain_register"
|
android:id="@+id/elevation_gain_register"
|
||||||
android:layout_column="4"
|
android:layout_column="4"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsRegister" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/elevation_gain_unit_label"
|
android:id="@+id/elevation_gain_unit_label"
|
||||||
android:layout_column="5"
|
android:layout_column="5"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsUnitSolo" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/feet"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
<!-- Min and max elevation -->
|
<!-- Min and max elevation -->
|
||||||
@@ -384,46 +264,24 @@ limitations under the License.
|
|||||||
<TableRow
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/min_elevation_register"
|
android:id="@+id/min_elevation_register"
|
||||||
android:layout_column="1"
|
android:layout_column="1"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsRegister" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="left"
|
|
||||||
android:id="@+id/min_elevation_unit_label"
|
android:id="@+id/min_elevation_unit_label"
|
||||||
android:layout_column="2"
|
android:layout_column="2"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsUnitSolo" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/feet"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsSpacer"
|
||||||
android:layout_column="3"
|
android:layout_column="3"/>
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_width="5dip"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/max_elevation_register"
|
android:id="@+id/max_elevation_register"
|
||||||
android:layout_column="4"
|
android:layout_column="4"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsRegister" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/unknown"
|
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/max_elevation_unit_label"
|
android:id="@+id/max_elevation_unit_label"
|
||||||
android:layout_column="5"
|
android:layout_column="5"
|
||||||
android:layout_marginBottom="-9dip"
|
style="@style/StatsUnitSolo" />
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:text="@string/feet"
|
|
||||||
android:textColor="@color/stat_label_text" />
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
<!-- Min and max grade -->
|
<!-- Min and max grade -->
|
||||||
@@ -442,36 +300,18 @@ limitations under the License.
|
|||||||
<TableRow
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/min_grade_register"
|
android:id="@+id/min_grade_register"
|
||||||
android:layout_column="1"
|
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:layout_span="2"
|
||||||
android:text="@string/unknown"
|
style="@style/StatsRegister" />
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsSpacer"
|
||||||
android:layout_column="3"
|
android:layout_column="3"/>
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_width="5dip"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/max_grade_register"
|
android:id="@+id/max_grade_register"
|
||||||
android:layout_column="4"
|
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:layout_span="2"
|
||||||
android:text="@string/unknown"
|
style="@style/StatsRegister" />
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="40sp" />
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
<!-- Latitude and Longitude -->
|
<!-- Latitude and Longitude -->
|
||||||
@@ -488,36 +328,19 @@ limitations under the License.
|
|||||||
<TableRow
|
<TableRow
|
||||||
android:background="@color/stat_label_background">
|
android:background="@color/stat_label_background">
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/latitude_register"
|
android:id="@+id/latitude_register"
|
||||||
android:layout_column="1"
|
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:layout_span="2"
|
||||||
android:text="@string/unknown"
|
style="@style/StatsRegister.Small" />
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="25sp" />
|
|
||||||
<TextView
|
<TextView
|
||||||
android:background="@color/black"
|
style="@style/StatsSpacer"
|
||||||
android:layout_column="3"
|
android:layout_column="3"
|
||||||
android:layout_marginBottom="-9dip"
|
|
||||||
android:layout_marginTop="-9dip"
|
|
||||||
android:layout_width="5dip"
|
|
||||||
android:textSize="25sp" />
|
android:textSize="25sp" />
|
||||||
<TextView
|
<TextView
|
||||||
android:gravity="right"
|
|
||||||
android:id="@+id/longitude_register"
|
android:id="@+id/longitude_register"
|
||||||
android:layout_column="4"
|
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:layout_span="2"
|
||||||
android:text="@string/unknown"
|
style="@style/StatsRegister.Small" />
|
||||||
android:textColor="@color/stat_label_text"
|
|
||||||
android:textSize="25sp" />
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
</TableLayout>
|
</TableLayout>
|
||||||
|
|||||||
@@ -22,10 +22,14 @@
|
|||||||
android:title="@string/settings"/>
|
android:title="@string/settings"/>
|
||||||
|
|
||||||
<item android:id="@+id/menu_aggregated_stats"
|
<item android:id="@+id/menu_aggregated_stats"
|
||||||
android:icon="@android:drawable/ic_menu_info_details"
|
|
||||||
android:orderInCategory="1002"
|
android:orderInCategory="1002"
|
||||||
android:title="@string/aggregated_stats"/>
|
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"
|
<item android:id="@+id/menu_help"
|
||||||
android:icon="@android:drawable/ic_menu_info_details"
|
android:icon="@android:drawable/ic_menu_info_details"
|
||||||
android:orderInCategory="1003"
|
android:orderInCategory="1003"
|
||||||
|
|||||||
@@ -390,4 +390,16 @@ limitations under the License.
|
|||||||
<item>CSV</item>
|
<item>CSV</item>
|
||||||
<item>TCX</item>
|
<item>TCX</item>
|
||||||
</array>
|
</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>
|
</resources>
|
||||||
|
|||||||
@@ -15,17 +15,22 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<color name="gold">#FFffd700</color>
|
|
||||||
<color name="white">#FFFFFFFF</color>
|
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="lightblue">#FF00FFFF</color>
|
|
||||||
<color name="blue">#FF0000FF</color>
|
<color name="blue">#FF0000FF</color>
|
||||||
<color name="blue_transparent">#400000FF</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="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="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_background">#FFF0F0F0</color>
|
||||||
<color name="stat_label_text">#FF0F0F0F</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>
|
</resources>
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ limitations under the License.
|
|||||||
<string name="send_stats_and_points_key">sendStatsAndPoints</string>
|
<string name="send_stats_and_points_key">sendStatsAndPoints</string>
|
||||||
<string name="send_to_docs_key">sendToDocs</string>
|
<string name="send_to_docs_key">sendToDocs</string>
|
||||||
<string name="send_to_my_maps_key">sendToMyMaps</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="sensor_type_key">sensorType</string>
|
||||||
<string name="share_url_only_key">shareUrlOnly</string>
|
<string name="share_url_only_key">shareUrlOnly</string>
|
||||||
<string name="split_frequency_key">splitFrequency</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="choose_track_message">Please select the track to view</string>
|
||||||
<string name="current_track">Current track</string>
|
<string name="current_track">Current track</string>
|
||||||
<string name="current_segment">Last segment</string>
|
<string name="current_segment">Last segment</string>
|
||||||
|
<string name="sensor_state">Sensor State</string>
|
||||||
<string name="aggregated_stats">Aggregated Statistics</string>
|
<string name="aggregated_stats">Aggregated Statistics</string>
|
||||||
|
|
||||||
<!-- Error messages -->
|
<!-- Error messages -->
|
||||||
@@ -454,9 +455,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<!-- Sensor Types => DO NOT TRANSLATE -->
|
<!-- Sensor Types => DO NOT TRANSLATE -->
|
||||||
<string name="none">NONE</string>
|
<string name="none">NONE</string>
|
||||||
<string name="ant_sensor_type">ANT</string>
|
<string name="zephyr_sensor_type">ZEPHYR</string>
|
||||||
<string name="srm_sensor_type">SRM</string>
|
|
||||||
<string name="athlosoft_sensor_type">ATHLOSOFT</string>
|
|
||||||
|
|
||||||
<!-- Bluetooth Sensor -->
|
<!-- Bluetooth Sensor -->
|
||||||
<string name="bluetooth_connected">Connected to %s</string>
|
<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="sensor_data_time">Sensor Data Time</string>
|
||||||
<string name="power_meter_enabled">Power meter enabled</string>
|
<string name="power_meter_enabled">Power meter enabled</string>
|
||||||
<string name="heart_rate_monitor_enabled">Heart rate monitor 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) -->
|
<!-- Manifest description (not seen inside the app) -->
|
||||||
<string name="notification_permission_label">receive track recording notifications</string>
|
<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>
|
</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 -->
|
<!-- Sharing settings -->
|
||||||
<PreferenceScreen android:title="@string/settings_sharing">
|
<PreferenceScreen android:title="@string/settings_sharing">
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
|
|||||||
@@ -16,11 +16,14 @@
|
|||||||
package com.google.android.apps.mytracks;
|
package com.google.android.apps.mytracks;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.ChartView.Mode;
|
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.MyTracksProviderUtils;
|
||||||
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
import com.google.android.apps.mytracks.content.Track;
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
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.services.StatusAnnouncerFactory;
|
||||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
||||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||||
@@ -178,12 +181,12 @@ public class ChartActivity extends Activity implements
|
|||||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||||
ArrayList<double[]> data = new ArrayList<double[]>();
|
ArrayList<double[]> data = new ArrayList<double[]>();
|
||||||
// Need two locations so we can keep track of the last location.
|
// Need two locations so we can keep track of the last location.
|
||||||
Location location = new Location("");
|
Location location = new MyTracksLocation("");
|
||||||
do {
|
do {
|
||||||
lastSeenLocationId = cursor.getLong(idColumnIdx);
|
lastSeenLocationId = cursor.getLong(idColumnIdx);
|
||||||
providerUtils.fillLocation(cursor, location);
|
providerUtils.fillLocation(cursor, location);
|
||||||
if (MyTracksUtils.isValidLocation(location)) {
|
if (MyTracksUtils.isValidLocation(location)) {
|
||||||
double[] point = new double[3];
|
double[] point = new double[6];
|
||||||
location = getDataPoint(location, track, point);
|
location = getDataPoint(location, track, point);
|
||||||
data.add(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
|
* 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[0] = the time or distance
|
||||||
* data[1] = the elevation
|
* data[1] = the elevation
|
||||||
* data[2] = the speed
|
* 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.
|
* 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
|
* @return the previous location, now available for reuse
|
||||||
*/
|
*/
|
||||||
private Location getDataPoint(Location location, Track track, double[] result) {
|
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) {
|
switch (mode) {
|
||||||
case BY_DISTANCE:
|
case BY_DISTANCE:
|
||||||
result[0] = profileLength;
|
result[0] = profileLength;
|
||||||
@@ -529,7 +565,7 @@ public class ChartActivity extends Activity implements
|
|||||||
|
|
||||||
if (oldLastLocation == null) {
|
if (oldLastLocation == null) {
|
||||||
// No previous location, but return a blank one for reuse
|
// No previous location, but return a blank one for reuse
|
||||||
return new Location("");
|
return new MyTracksLocation("");
|
||||||
}
|
}
|
||||||
|
|
||||||
return oldLastLocation;
|
return oldLastLocation;
|
||||||
@@ -576,7 +612,7 @@ public class ChartActivity extends Activity implements
|
|||||||
final ArrayList<double[]> theData = new ArrayList<double[]>();
|
final ArrayList<double[]> theData = new ArrayList<double[]>();
|
||||||
int points = 0;
|
int points = 0;
|
||||||
// Need two locations so we can keep track of the last location.
|
// Need two locations so we can keep track of the last location.
|
||||||
Location location = new Location("");
|
Location location = new MyTracksLocation("");
|
||||||
while (lastLocationRead < track.getStopId()) {
|
while (lastLocationRead < track.getStopId()) {
|
||||||
cursor = providerUtils.getLocationsCursor(
|
cursor = providerUtils.getLocationsCursor(
|
||||||
selectedTrackId, lastLocationRead, bufferSize, false);
|
selectedTrackId, lastLocationRead, bufferSize, false);
|
||||||
@@ -592,7 +628,8 @@ public class ChartActivity extends Activity implements
|
|||||||
if (MyTracksUtils.isValidLocation(location)) {
|
if (MyTracksUtils.isValidLocation(location)) {
|
||||||
lastLocationRead = lastSeenLocationId =
|
lastLocationRead = lastSeenLocationId =
|
||||||
cursor.getLong(idColumnIdx);
|
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);
|
location = getDataPoint(location, track, point);
|
||||||
if (points % chartSamplingFrequency == 0) {
|
if (points % chartSamplingFrequency == 0) {
|
||||||
theData.add(point);
|
theData.add(point);
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ import android.widget.RadioGroup;
|
|||||||
*/
|
*/
|
||||||
public class ChartSettingsDialog extends Dialog {
|
public class ChartSettingsDialog extends Dialog {
|
||||||
private RadioButton distance;
|
private RadioButton distance;
|
||||||
private CheckBox elevation;
|
private CheckBox[] series;
|
||||||
private CheckBox speed;
|
|
||||||
|
|
||||||
public ChartSettingsDialog(Context context) {
|
public ChartSettingsDialog(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -61,8 +60,18 @@ public class ChartSettingsDialog extends Dialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
distance = (RadioButton) findViewById(R.id.chart_settings_by_distance);
|
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) {
|
public void setup(ChartActivity chart) {
|
||||||
@@ -74,8 +83,9 @@ public class ChartSettingsDialog extends Dialog {
|
|||||||
rd.check(chart.getMode() == Mode.BY_DISTANCE
|
rd.check(chart.getMode() == Mode.BY_DISTANCE
|
||||||
? R.id.chart_settings_by_distance
|
? R.id.chart_settings_by_distance
|
||||||
: R.id.chart_settings_by_time);
|
: R.id.chart_settings_by_time);
|
||||||
elevation.setChecked(chart.isSeriesEnabled(ChartView.ELEVATION_SERIES));
|
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||||
speed.setChecked(chart.isSeriesEnabled(ChartView.SPEED_SERIES));
|
series[i].setChecked(chart.isSeriesEnabled(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleOk() {
|
private void handleOk() {
|
||||||
@@ -83,8 +93,9 @@ public class ChartSettingsDialog extends Dialog {
|
|||||||
chart.setMode(distance.isChecked() ? Mode.BY_DISTANCE : Mode.BY_TIME);
|
chart.setMode(distance.isChecked() ? Mode.BY_DISTANCE : Mode.BY_TIME);
|
||||||
|
|
||||||
// TODO: check that something is visible.
|
// TODO: check that something is visible.
|
||||||
chart.setSeriesEnabled(ChartView.ELEVATION_SERIES, elevation.isChecked());
|
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||||
chart.setSeriesEnabled(ChartView.SPEED_SERIES, speed.isChecked());
|
chart.setSeriesEnabled(i, series[i].isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public class ChartValueSeries {
|
|||||||
private double min = 0;
|
private double min = 0;
|
||||||
private double max = 1;
|
private double max = 1;
|
||||||
private int effectiveMax = 0;
|
private int effectiveMax = 0;
|
||||||
|
private int absoluteMax = Integer.MIN_VALUE;
|
||||||
private int effectiveMin = 0;
|
private int effectiveMin = 0;
|
||||||
private double spread = 0;
|
private double spread = 0;
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
@@ -131,7 +132,7 @@ public class ChartValueSeries {
|
|||||||
if (min < 0) {
|
if (min < 0) {
|
||||||
effectiveMin -= rounding;
|
effectiveMin -= rounding;
|
||||||
}
|
}
|
||||||
spread = effectiveMax - effectiveMin;
|
spread = getMax() - effectiveMin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,7 +140,7 @@ public class ChartValueSeries {
|
|||||||
*/
|
*/
|
||||||
public int getMaxLabelLength() {
|
public int getMaxLabelLength() {
|
||||||
String minS = format.format(effectiveMin);
|
String minS = format.format(effectiveMin);
|
||||||
String maxS = format.format(effectiveMax);
|
String maxS = format.format(getMax());
|
||||||
return Math.max(minS.length(), maxS.length());
|
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() {
|
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) {
|
public void setEnabled(boolean enabled) {
|
||||||
this.enabled = 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 ELEVATION_SERIES = 0;
|
||||||
public static final int SPEED_SERIES = 1;
|
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 ChartValueSeries[] series;
|
||||||
|
|
||||||
private final ExtremityMonitor xMonitor = new ExtremityMonitor();
|
private final ExtremityMonitor xMonitor = new ExtremityMonitor();
|
||||||
@@ -172,38 +175,44 @@ public class ChartView extends View {
|
|||||||
series = new ChartValueSeries[NUM_SERIES];
|
series = new ChartValueSeries[NUM_SERIES];
|
||||||
|
|
||||||
// Create the paints
|
// Create the paints
|
||||||
Paint fillPaint1 = new Paint();
|
Paint fillPaint = new Paint();
|
||||||
fillPaint1.setStyle(Style.FILL);
|
fillPaint.setStyle(Style.FILL);
|
||||||
fillPaint1.setColor(context.getResources().getColor(R.color.green));
|
fillPaint.setColor(context.getResources().getColor(R.color.green));
|
||||||
fillPaint1.setAntiAlias(true);
|
fillPaint.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);
|
|
||||||
|
|
||||||
// Create the value series
|
// Create the value series
|
||||||
series[ELEVATION_SERIES] =
|
series[ELEVATION_SERIES] =
|
||||||
new ChartValueSeries(context,
|
new ChartValueSeries(context,
|
||||||
"###,###",
|
"###,###",
|
||||||
fillPaint1,
|
fillPaint,
|
||||||
null,
|
null,
|
||||||
100,
|
100,
|
||||||
R.string.elevation);
|
R.string.elevation);
|
||||||
|
|
||||||
series[SPEED_SERIES] =
|
series[SPEED_SERIES] = getSeries(context, R.color.blue_transparent,
|
||||||
new ChartValueSeries(context,
|
R.color.blue, "###,###.0", R.string.speed);
|
||||||
"###,###.0",
|
series[POWER_SERIES] = getSeries(context, R.color.red_transparent,
|
||||||
fillPaint2,
|
R.color.red, "###,###", R.string.power);
|
||||||
strokePaint,
|
series[POWER_SERIES].setAbsoluteMax(1500);
|
||||||
5,
|
series[CADENCE_SERIES] = getSeries(context, R.color.purple_transparent,
|
||||||
R.string.speed);
|
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() {
|
public void clearWaypoints() {
|
||||||
@@ -266,13 +275,25 @@ public class ChartView extends View {
|
|||||||
*/
|
*/
|
||||||
public synchronized void addDataPoint(double[] theData) {
|
public synchronized void addDataPoint(double[] theData) {
|
||||||
data.add(theData);
|
data.add(theData);
|
||||||
|
addDataPointInternal(theData);
|
||||||
|
updateDimensions();
|
||||||
|
setupPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDataPointInternal(double[] theData) {
|
||||||
xMonitor.update(theData[0]);
|
xMonitor.update(theData[0]);
|
||||||
int min = Math.min(series.length, theData.length - 1);
|
int min = Math.min(series.length, theData.length - 1);
|
||||||
for (int i = 0; i < min; i++) {
|
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);
|
data.addAll(theData);
|
||||||
for (int i = 0; i < theData.size(); i++) {
|
for (int i = 0; i < theData.size(); i++) {
|
||||||
double d[] = theData.get(i);
|
double d[] = theData.get(i);
|
||||||
xMonitor.update(d[0]);
|
addDataPointInternal(d);
|
||||||
int min = Math.min(series.length, d.length - 1);
|
|
||||||
for (int j = 0; j < min; j++) {
|
|
||||||
series[j].update(d[j + 1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
updateDimensions();
|
updateDimensions();
|
||||||
setupPath();
|
setupPath();
|
||||||
@@ -526,7 +543,7 @@ public class ChartView extends View {
|
|||||||
|
|
||||||
// Draw the data series
|
// Draw the data series
|
||||||
for (ChartValueSeries cvs : series) {
|
for (ChartValueSeries cvs : series) {
|
||||||
if (cvs.isEnabled()) {
|
if (cvs.isEnabled() && cvs.hasData()) {
|
||||||
cvs.drawPath(c);
|
cvs.drawPath(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -560,7 +577,7 @@ public class ChartView extends View {
|
|||||||
c.translate(getScrollX(), 0);
|
c.translate(getScrollX(), 0);
|
||||||
drawYAxis(c);
|
drawYAxis(c);
|
||||||
for (ChartValueSeries cvs : series) {
|
for (ChartValueSeries cvs : series) {
|
||||||
if (cvs.isEnabled()) {
|
if (cvs.isEnabled() && cvs.hasData()) {
|
||||||
drawYLabels(cvs, c);
|
drawYLabels(cvs, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -576,13 +593,13 @@ public class ChartView extends View {
|
|||||||
private void drawSeriesTitles(Canvas c) {
|
private void drawSeriesTitles(Canvas c) {
|
||||||
int sections = 1;
|
int sections = 1;
|
||||||
for (ChartValueSeries cvs : series) {
|
for (ChartValueSeries cvs : series) {
|
||||||
if (cvs.isEnabled()) {
|
if (cvs.isEnabled() && cvs.hasData()) {
|
||||||
sections++;
|
sections++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (ChartValueSeries cvs : series) {
|
for (ChartValueSeries cvs : series) {
|
||||||
if (cvs.isEnabled()) {
|
if (cvs.isEnabled() && cvs.hasData()) {
|
||||||
int y = (int) (w * zoomLevel * ((double) (++j) / sections));
|
int y = (int) (w * zoomLevel * ((double) (++j) / sections));
|
||||||
c.drawText(cvs.getTitle(), y, TOP_BORDER, cvs.getPaint());
|
c.drawText(cvs.getTitle(), y, TOP_BORDER, cvs.getPaint());
|
||||||
}
|
}
|
||||||
@@ -601,11 +618,11 @@ public class ChartView extends View {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int min = Math.min(series.length, data.get(0).length - 1);
|
|
||||||
|
|
||||||
// All of the data points to the respective series.
|
// All of the data points to the respective series.
|
||||||
for (int i = 0; i < data.size(); i++) {
|
for (int i = 0; i < data.size(); i++) {
|
||||||
double[] d = data.get(i);
|
double[] d = data.get(i);
|
||||||
|
int min = Math.min(series.length, d.length - 1);
|
||||||
|
|
||||||
for (int j = 0; j < min; j++) {
|
for (int j = 0; j < min; j++) {
|
||||||
ChartValueSeries cvs = series[j];
|
ChartValueSeries cvs = series[j];
|
||||||
Path path = cvs.getPath();
|
Path path = cvs.getPath();
|
||||||
@@ -616,17 +633,36 @@ public class ChartView extends View {
|
|||||||
// Close the path.
|
// Close the path.
|
||||||
int yCorner = TOP_BORDER + effectiveHeight;
|
int yCorner = TOP_BORDER + effectiveHeight;
|
||||||
int xCorner = getX(data.get(0)[0]);
|
int xCorner = getX(data.get(0)[0]);
|
||||||
|
int min = series.length;
|
||||||
for (int j = 0; j < min; j++) {
|
for (int j = 0; j < min; j++) {
|
||||||
ChartValueSeries cvs = series[j];
|
ChartValueSeries cvs = series[j];
|
||||||
Path path = cvs.getPath();
|
Path path = cvs.getPath();
|
||||||
// Bottom right corner
|
int first = getFirstPointPopulatedIndex(j + 1);
|
||||||
path.lineTo(getX(data.get(data.size() - 1)[0]), yCorner);
|
if (first != -1) {
|
||||||
// Bottom left corner
|
// Bottom right corner
|
||||||
path.lineTo(xCorner, yCorner);
|
path.lineTo(getX(data.get(data.size() - 1)[0]), yCorner);
|
||||||
// Top right corner
|
// Bottom left corner
|
||||||
path.lineTo(xCorner, getY(cvs, data.get(0)[j + 1]));
|
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.
|
* Update the histogram dimensions.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package com.google.android.apps.mytracks;
|
|||||||
|
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -63,8 +64,8 @@ class MenuManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.menu_list_tracks: {
|
case R.id.menu_list_tracks: {
|
||||||
Intent startIntent = new Intent(activity, MyTracksList.class);
|
activity.startActivityForResult(new Intent(activity, MyTracksList.class),
|
||||||
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_TRACK);
|
MyTracksConstants.SHOW_TRACK);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.menu_list_markers: {
|
case R.id.menu_list_markers: {
|
||||||
@@ -73,20 +74,17 @@ class MenuManager {
|
|||||||
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_WAYPOINT);
|
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_WAYPOINT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case R.id.menu_sensor_state: {
|
||||||
|
return startActivity(SensorStateActivity.class);
|
||||||
|
}
|
||||||
case R.id.menu_settings: {
|
case R.id.menu_settings: {
|
||||||
Intent startIntent = new Intent(activity, MyTracksSettings.class);
|
return startActivity(MyTracksSettings.class);
|
||||||
activity.startActivity(startIntent);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case R.id.menu_aggregated_stats: {
|
case R.id.menu_aggregated_stats: {
|
||||||
Intent startIntent = new Intent(activity, AggregatedStatsActivity.class);
|
return startActivity(AggregatedStatsActivity.class);
|
||||||
activity.startActivity(startIntent);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case R.id.menu_help: {
|
case R.id.menu_help: {
|
||||||
Intent startIntent = new Intent(activity, WelcomeActivity.class);
|
return startActivity(WelcomeActivity.class);
|
||||||
activity.startActivity(startIntent);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
case MyTracksConstants.MENU_CLEAR_MAP: {
|
case MyTracksConstants.MENU_CLEAR_MAP: {
|
||||||
activity.setSelectedTrackId(-1);
|
activity.setSelectedTrackId(-1);
|
||||||
@@ -95,4 +93,9 @@ class MenuManager {
|
|||||||
}
|
}
|
||||||
return false;
|
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() {
|
long getSelectedTrackId() {
|
||||||
return selectedTrackId;
|
return selectedTrackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds to track recording service if it is running.
|
* Binds to track recording service if it is running.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,12 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.apps.mytracks;
|
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.BackupActivityHelper;
|
||||||
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
|
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
|
||||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||||
|
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.CheckBoxPreference;
|
import android.preference.CheckBoxPreference;
|
||||||
@@ -30,6 +35,7 @@ import android.preference.PreferenceActivity;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.preference.Preference.OnPreferenceChangeListener;
|
import android.preference.Preference.OnPreferenceChangeListener;
|
||||||
import android.preference.Preference.OnPreferenceClickListener;
|
import android.preference.Preference.OnPreferenceClickListener;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity that let's the user see and edit the 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;
|
public static final int DEFAULT_SPLIT_FREQUENCY = 0;
|
||||||
|
|
||||||
private BackupPreferencesListener backupListener;
|
private BackupPreferencesListener backupListener;
|
||||||
|
|
||||||
private SharedPreferences preferences;
|
private SharedPreferences preferences;
|
||||||
|
|
||||||
/** Called when the activity is first created. */
|
/** Called when the activity is first created. */
|
||||||
@@ -96,6 +103,19 @@ public class MyTracksSettings extends PreferenceActivity {
|
|||||||
});
|
});
|
||||||
updatePreferenceUnits(metricUnitsPreference.isChecked());
|
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
|
// Disable TTS announcement preference if not available
|
||||||
if (!apiFeatures.hasTextToSpeech()) {
|
if (!apiFeatures.hasTextToSpeech()) {
|
||||||
IntegerListPreference announcementFrequency =
|
IntegerListPreference announcementFrequency =
|
||||||
@@ -112,6 +132,7 @@ public class MyTracksSettings extends PreferenceActivity {
|
|||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
|
configureBluetoothPreferences();
|
||||||
Preference backupNowPreference =
|
Preference backupNowPreference =
|
||||||
findPreference(getString(R.string.backup_to_sd_key));
|
findPreference(getString(R.string.backup_to_sd_key));
|
||||||
Preference restoreNowPreference =
|
Preference restoreNowPreference =
|
||||||
@@ -158,7 +179,15 @@ public class MyTracksSettings extends PreferenceActivity {
|
|||||||
getPreferenceManager().getSharedPreferences()
|
getPreferenceManager().getSharedPreferences()
|
||||||
.unregisterOnSharedPreferenceChangeListener(backupListener);
|
.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
|
||||||
: R.array.split_frequency_options_ft);
|
: 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;
|
package com.google.android.apps.mytracks.content;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@@ -38,7 +40,7 @@ import android.util.Log;
|
|||||||
public class MyTracksProvider extends ContentProvider {
|
public class MyTracksProvider extends ContentProvider {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "mytracks.db";
|
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 = 1;
|
||||||
private static final int TRACKPOINTS_ID = 2;
|
private static final int TRACKPOINTS_ID = 2;
|
||||||
private static final int TRACKS = 3;
|
private static final int TRACKS = 3;
|
||||||
@@ -70,7 +72,8 @@ public class MyTracksProvider extends ContentProvider {
|
|||||||
+ TrackPointsColumns.ALTITUDE + " FLOAT, "
|
+ TrackPointsColumns.ALTITUDE + " FLOAT, "
|
||||||
+ TrackPointsColumns.ACCURACY + " FLOAT, "
|
+ TrackPointsColumns.ACCURACY + " FLOAT, "
|
||||||
+ TrackPointsColumns.SPEED + " FLOAT, "
|
+ TrackPointsColumns.SPEED + " FLOAT, "
|
||||||
+ TrackPointsColumns.BEARING + " FLOAT);");
|
+ TrackPointsColumns.BEARING + " FLOAT, "
|
||||||
|
+ TrackPointsColumns.SENSOR + " BLOB);");
|
||||||
db.execSQL("CREATE TABLE " + TRACKS_TABLE + " ("
|
db.execSQL("CREATE TABLE " + TRACKS_TABLE + " ("
|
||||||
+ TracksColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
+ TracksColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
+ TracksColumns.NAME + " STRING, "
|
+ TracksColumns.NAME + " STRING, "
|
||||||
@@ -132,12 +135,19 @@ public class MyTracksProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
if (oldVersion < 17) {
|
||||||
+ newVersion + ", which will destroy all old data");
|
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKPOINTS_TABLE);
|
+ newVersion + ", which will destroy all old data");
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + TRACKS_TABLE);
|
db.execSQL("DROP TABLE IF EXISTS " + TRACKPOINTS_TABLE);
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + WAYPOINTS_TABLE);
|
db.execSQL("DROP TABLE IF EXISTS " + TRACKS_TABLE);
|
||||||
onCreate(db);
|
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
|
@Override
|
||||||
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
|
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
|
||||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
|
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
|
||||||
@@ -346,6 +357,8 @@ public class MyTracksProvider extends ContentProvider {
|
|||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
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,
|
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null,
|
||||||
sortOrder);
|
sortOrder);
|
||||||
c.setNotificationUri(getContext().getContentResolver(), url);
|
c.setNotificationUri(getContext().getContentResolver(), url);
|
||||||
|
|||||||
@@ -342,8 +342,14 @@ public class GpxImporter extends DefaultHandler {
|
|||||||
|
|
||||||
// create new location and set attributes
|
// create new location and set attributes
|
||||||
Location loc = new Location(LocationManager.GPS_PROVIDER);
|
Location loc = new Location(LocationManager.GPS_PROVIDER);
|
||||||
loc.setLatitude(Double.parseDouble(latitude));
|
try {
|
||||||
loc.setLongitude(Double.parseDouble(longitude));
|
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;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,9 +478,14 @@ public class GpxImporter extends DefaultHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAltitudeElementEnd() {
|
private void onAltitudeElementEnd() throws SAXException {
|
||||||
if (location != null) {
|
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;
|
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.Track;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
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 com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
@@ -141,6 +144,37 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
|||||||
pw.print(location.getAltitude());
|
pw.print(location.getAltitude());
|
||||||
pw.println("</AltitudeMeters>");
|
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>");
|
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.MyTracks;
|
||||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
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.MyTracksProviderUtils;
|
||||||
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
import com.google.android.apps.mytracks.content.Track;
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
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.TripStatistics;
|
||||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||||
@@ -111,6 +116,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
private PeriodicTaskExecuter announcementExecuter;
|
private PeriodicTaskExecuter announcementExecuter;
|
||||||
private SplitManager splitManager;
|
private SplitManager splitManager;
|
||||||
|
|
||||||
|
private SensorManager sensorManager;
|
||||||
|
|
||||||
private PreferenceManager prefManager;
|
private PreferenceManager prefManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,7 +221,14 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
|
|
||||||
// Insert the new location:
|
// Insert the new location:
|
||||||
try {
|
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());
|
int pointId = Integer.parseInt(pointUri.getLastPathSegment());
|
||||||
|
|
||||||
// Update the current track:
|
// Update the current track:
|
||||||
@@ -523,11 +537,15 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
if (lastLocation != null) {
|
if (lastLocation != null) {
|
||||||
distanceToLast = location.distanceTo(lastLocation);
|
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
|
// If the user has been stationary for two recording just record the first
|
||||||
// two and ignore the rest. This code will only have an effect if the
|
// two and ignore the rest. This code will only have an effect if the
|
||||||
// maxRecordingDistance = 0
|
// maxRecordingDistance = 0
|
||||||
if (distanceToLast == 0) {
|
if (distanceToLast == 0 && !hasSensorData) {
|
||||||
if (isMoving) {
|
if (isMoving) {
|
||||||
Log.d(MyTracksConstants.TAG, "Found two identical locations.");
|
Log.d(MyTracksConstants.TAG, "Found two identical locations.");
|
||||||
isMoving = false;
|
isMoving = false;
|
||||||
@@ -547,7 +565,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
Log.d(MyTracksConstants.TAG,
|
Log.d(MyTracksConstants.TAG,
|
||||||
"Not recording. More than two identical locations.");
|
"Not recording. More than two identical locations.");
|
||||||
}
|
}
|
||||||
} else if (distanceToLastRecorded > minRecordingDistance) {
|
} else if (distanceToLastRecorded > minRecordingDistance
|
||||||
|
|| hasSensorData) {
|
||||||
if (lastLocation != null && !isMoving) {
|
if (lastLocation != null && !isMoving) {
|
||||||
// Last location was the last stationary location. Need to go back and
|
// Last location was the last stationary location. Need to go back and
|
||||||
// add it.
|
// add it.
|
||||||
@@ -627,9 +646,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
|
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
|
||||||
splitManager = new SplitManager(this);
|
splitManager = new SplitManager(this);
|
||||||
|
|
||||||
|
sensorManager = SensorManagerFactory.getSensorManager(this);
|
||||||
prefManager = new PreferenceManager(this);
|
prefManager = new PreferenceManager(this);
|
||||||
registerLocationListener();
|
registerLocationListener();
|
||||||
/**
|
|
||||||
|
/*
|
||||||
* After 5 min, check every minute that location listener still is
|
* After 5 min, check every minute that location listener still is
|
||||||
* registered and spit out additional debugging info to the logs:
|
* registered and spit out additional debugging info to the logs:
|
||||||
*/
|
*/
|
||||||
@@ -711,6 +732,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
shutdownAnnouncer();
|
shutdownAnnouncer();
|
||||||
splitManager.shutdown();
|
splitManager.shutdown();
|
||||||
splitManager = null;
|
splitManager = null;
|
||||||
|
if (sensorManager != null) {
|
||||||
|
sensorManager.onDestroy();
|
||||||
|
sensorManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we have no indirect references to this service.
|
// Make sure we have no indirect references to this service.
|
||||||
locationManager = null;
|
locationManager = null;
|
||||||
@@ -1022,6 +1047,28 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
}
|
}
|
||||||
service.onLocationChanged(loc);
|
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() {
|
public long startNewTrack() {
|
||||||
@@ -1054,6 +1101,9 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
showNotification();
|
showNotification();
|
||||||
registerLocationListener();
|
registerLocationListener();
|
||||||
splitManager.restore();
|
splitManager.restore();
|
||||||
|
if (sensorManager != null) {
|
||||||
|
sensorManager.onStartTrack();
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the number of auto-resume retries.
|
// Reset the number of auto-resume retries.
|
||||||
setAutoResumeTrackRetries(
|
setAutoResumeTrackRetries(
|
||||||
@@ -1096,6 +1146,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
showNotification();
|
showNotification();
|
||||||
long recordedTrackId = recordingTrackId;
|
long recordedTrackId = recordingTrackId;
|
||||||
prefManager.setRecordingTrack(recordingTrackId = -1);
|
prefManager.setRecordingTrack(recordingTrackId = -1);
|
||||||
|
|
||||||
|
if (sensorManager != null) {
|
||||||
|
sensorManager.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
releaseWakeLock();
|
releaseWakeLock();
|
||||||
|
|
||||||
// Notify the world that we're no longer recording.
|
// 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>
|
<classpath>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<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="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</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;
|
package com.google.android.apps.mytracks.content;
|
||||||
|
|
||||||
import static com.google.android.apps.mytracks.lib.MyTracksLibConstants.*;
|
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.android.apps.mytracks.stats.TripStatistics;
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@@ -75,6 +77,12 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
if (location.hasSpeed()) {
|
if (location.hasSpeed()) {
|
||||||
values.put(TrackPointsColumns.SPEED, location.getSpeed());
|
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;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +180,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Location createLocation(Cursor cursor) {
|
public Location createLocation(Cursor cursor) {
|
||||||
Location location = new Location("");
|
Location location = new MyTracksLocation("");
|
||||||
fillLocation(cursor, location);
|
fillLocation(cursor, location);
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
@@ -189,6 +197,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
int idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
int idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||||
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
||||||
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
||||||
|
int idxSensor = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR);
|
||||||
|
|
||||||
if (!cursor.isNull(idxLatitude)) {
|
if (!cursor.isNull(idxLatitude)) {
|
||||||
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
|
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
|
||||||
@@ -211,6 +220,18 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
if (!cursor.isNull(idxAccuracy)) {
|
if (!cursor.isNull(idxAccuracy)) {
|
||||||
location.setAccuracy(cursor.getFloat(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
|
@Override
|
||||||
@@ -450,7 +471,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public void deleteAllTracks() {
|
public void deleteAllTracks() {
|
||||||
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
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);
|
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,7 +486,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
}
|
}
|
||||||
contentResolver.delete(WaypointsColumns.CONTENT_URI,
|
contentResolver.delete(WaypointsColumns.CONTENT_URI,
|
||||||
WaypointsColumns.TRACKID + "=" + trackId, null);
|
WaypointsColumns.TRACKID + "=" + trackId, null);
|
||||||
contentResolver.delete(TracksColumns.CONTENT_URI, "_id=" + trackId, null);
|
contentResolver.delete(
|
||||||
|
TracksColumns.CONTENT_URI, "_id=" + trackId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 TIME = "time";
|
||||||
public static final String ACCURACY = "accuracy";
|
public static final String ACCURACY = "accuracy";
|
||||||
public static final String SPEED = "speed";
|
public static final String SPEED = "speed";
|
||||||
|
public static final String SENSOR = "sensor";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,4 +84,18 @@ interface ITrackRecordingService {
|
|||||||
* Deletes all the stored tracks.
|
* Deletes all the stored tracks.
|
||||||
*/
|
*/
|
||||||
void deleteAllTracks();
|
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) {
|
public void setMax(double max) {
|
||||||
this.max = max;
|
this.max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasData() {
|
||||||
|
return min != Double.POSITIVE_INFINITY
|
||||||
|
&& max != Double.NEGATIVE_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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 static com.google.android.testing.mocking.AndroidMock.expect;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
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.Track;
|
||||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
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.apps.mytracks.testing.TestingProviderUtilsFactory;
|
||||||
import com.google.android.testing.mocking.AndroidMock;
|
import com.google.android.testing.mocking.AndroidMock;
|
||||||
import com.google.android.testing.mocking.UsesMocks;
|
import com.google.android.testing.mocking.UsesMocks;
|
||||||
@@ -77,7 +77,7 @@ public class GpxImporterTest extends AndroidTestCase {
|
|||||||
DATE_FORMAT2.setTimeZone(utc);
|
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["
|
private static final String VALID_TEST_GPX = "<gpx><trk><name><![CDATA["
|
||||||
+ TRACK_NAME + "]]></name><desc><![CDATA[" + TRACK_DESC
|
+ TRACK_NAME + "]]></name><desc><![CDATA[" + TRACK_DESC
|
||||||
+ "]]></desc><trkseg>" + "<trkpt lat=\"" + TRACK_LAT_1 + "\" lon=\""
|
+ "]]></desc><trkseg>" + "<trkpt lat=\"" + TRACK_LAT_1 + "\" lon=\""
|
||||||
@@ -93,6 +93,12 @@ public class GpxImporterTest extends AndroidTestCase {
|
|||||||
.replaceAll(TRACK_LAT_1, "1000.0");
|
.replaceAll(TRACK_LAT_1, "1000.0");
|
||||||
private static final String INVALID_TIME_TEST_GPX = VALID_TEST_GPX
|
private static final String INVALID_TIME_TEST_GPX = VALID_TEST_GPX
|
||||||
.replaceAll(TRACK_TIME_1, "invalid");
|
.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_ID = 1;
|
||||||
private static final long TRACK_POINT_ID_1 = 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 {
|
public void testImportSuccess() throws Exception {
|
||||||
Capture<Track> trackParam = new Capture<Track>();
|
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,
|
public void testImportLocationFailure() throws ParserConfigurationException,
|
||||||
SAXException, IOException {
|
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,
|
public void testImportTimeFailure() throws ParserConfigurationException,
|
||||||
SAXException, IOException {
|
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,
|
public void testImportXMLFailure() throws ParserConfigurationException,
|
||||||
SAXException, IOException {
|
SAXException, IOException {
|
||||||
testInvalidXML(INVALID_XML_TEST_GPX);
|
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,
|
private void testInvalidXML(String xml) throws ParserConfigurationException,
|
||||||
IOException {
|
IOException {
|
||||||
expect(providerUtils.insertTrack((Track) AndroidMock.anyObject()))
|
expect(providerUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
package com.google.android.apps.mytracks.io;
|
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.Document;
|
||||||
import org.w3c.dom.Element;
|
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.
|
* 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());
|
assertEquals(locs.length, tags.size());
|
||||||
for (int i = 0; i < locs.length; i++) {
|
for (int i = 0; i < locs.length; i++) {
|
||||||
Element tag = tags.get(i);
|
Element tag = tags.get(i);
|
||||||
Location loc = locs[i];
|
MyTracksLocation loc = locs[i];
|
||||||
|
|
||||||
assertTagMatchesLocation(tag, loc);
|
assertTagMatchesLocation(tag, loc);
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
|||||||
/**
|
/**
|
||||||
* Asserts that the given tag describes the given location.
|
* 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");
|
Element posTag = getChildElement(tag, "Position");
|
||||||
assertEquals(Double.toString(loc.getLatitude()),
|
assertEquals(Double.toString(loc.getLatitude()),
|
||||||
getChildTextValue(posTag, "LatitudeDegrees"));
|
getChildTextValue(posTag, "LatitudeDegrees"));
|
||||||
@@ -62,5 +63,16 @@ public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
|||||||
getChildTextValue(tag, "Time"));
|
getChildTextValue(tag, "Time"));
|
||||||
assertEquals(Double.toString(loc.getAltitude()),
|
assertEquals(Double.toString(loc.getAltitude()),
|
||||||
getChildTextValue(tag, "AltitudeMeters"));
|
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.
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
package com.google.android.apps.mytracks.io;
|
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.Track;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
|
|
||||||
import android.location.Location;
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
@@ -46,7 +47,7 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
|||||||
private static final int BUFFER_SIZE = 10240;
|
private static final int BUFFER_SIZE = 10240;
|
||||||
protected static final long TRACK_ID = 12345L;
|
protected static final long TRACK_ID = 12345L;
|
||||||
protected Track track;
|
protected Track track;
|
||||||
protected Location location1, location2, location3, location4;
|
protected MyTracksLocation location1, location2, location3, location4;
|
||||||
protected Waypoint wp1, wp2;
|
protected Waypoint wp1, wp2;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,10 +59,10 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
|||||||
track.setName(TRACK_NAME);
|
track.setName(TRACK_NAME);
|
||||||
track.setDescription(TRACK_DESCRIPTION);
|
track.setDescription(TRACK_DESCRIPTION);
|
||||||
|
|
||||||
location1 = new Location("mock");
|
location1 = new MyTracksLocation("mock");
|
||||||
location2 = new Location("mock");
|
location2 = new MyTracksLocation("mock");
|
||||||
location3 = new Location("mock");
|
location3 = new MyTracksLocation("mock");
|
||||||
location4 = new Location("mock");
|
location4 = new MyTracksLocation("mock");
|
||||||
populateLocations(location1, location2, location3, location4);
|
populateLocations(location1, location2, location3, location4);
|
||||||
|
|
||||||
wp1 = new Waypoint();
|
wp1 = new Waypoint();
|
||||||
@@ -77,13 +78,24 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
|
|||||||
/**
|
/**
|
||||||
* Populates the given locations with coordinates and time.
|
* 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++) {
|
for (int i = 0; i < locs.length; i++) {
|
||||||
Location loc = locs[i];
|
MyTracksLocation loc = locs[i];
|
||||||
loc.setAltitude(i * 1000);
|
loc.setAltitude(i * 1000);
|
||||||
loc.setLatitude(i);
|
loc.setLatitude(i);
|
||||||
loc.setLongitude(-i);
|
loc.setLongitude(-i);
|
||||||
loc.setTime(10000000 + i * 1000);
|
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 parent the parent to get children from
|
||||||
* @param elementName the element name to look for
|
* @param elementName the element name to look for
|
||||||
|
|||||||
+11
@@ -692,6 +692,17 @@ public class TrackRecordingServiceTest
|
|||||||
functionalTest(R.string.min_required_accuracy_key, 500);
|
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) {
|
private ITrackRecordingService bindAndGetService(Intent intent) {
|
||||||
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(
|
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(
|
||||||
bindService(intent));
|
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="src" path="gen"/>
|
||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry kind="src" path="MyTracksLib_src"/>
|
<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"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
Reference in New Issue
Block a user