forked from upstream-mirrors/OpenTracks
merge heads
This commit is contained in:
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="2000"
|
||||
android:duration="1000"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:toAlpha="0.0" />
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<!-- Distance, calorie, total time, and moving time -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_distance"
|
||||
layout="@layout/stats_large_item" />
|
||||
<include
|
||||
android:id="@+id/stats_calorie"
|
||||
layout="@layout/stats_calorie" />
|
||||
<View
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="1dp"
|
||||
android:visibility="invisible" />
|
||||
<include
|
||||
android:id="@+id/stats_total_time"
|
||||
layout="@layout/stats_large_item" />
|
||||
<include
|
||||
android:id="@+id/stats_moving_time"
|
||||
layout="@layout/stats_large_item" />
|
||||
</LinearLayout>
|
||||
<!-- Horizontal line -->
|
||||
<View style="@style/StatsHorizontalLine" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<TableLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp" >
|
||||
<!-- Speed and average speed -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
<include
|
||||
android:id="@+id/stats_average_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
<!-- Max speed and average moving speed -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_max_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
<include
|
||||
android:id="@+id/stats_average_moving_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
<!-- Latitude and longitude -->
|
||||
<LinearLayout
|
||||
android:id="@+id/stats_coordinate_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_latitude"
|
||||
layout="@layout/stats_small_item" />
|
||||
<include
|
||||
android:id="@+id/stats_longitude"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
<!-- Vertical line -->
|
||||
<View
|
||||
android:background="@color/stats_separator"
|
||||
android:id="@+id/stats_grade_elevation_separator"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="1dp" />
|
||||
<LinearLayout
|
||||
android:id="@+id/stats_grade_elevation_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp" >
|
||||
<!-- Grade -->
|
||||
<LinearLayout style="@style/StatsSmallGroupContainer" >
|
||||
<!-- Grade label -->
|
||||
<TextView
|
||||
android:text="@string/stats_grade"
|
||||
style="@style/StatsSmallLabel" />
|
||||
<TableLayout
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content" >
|
||||
<!-- Grade min -->
|
||||
<include
|
||||
android:id="@+id/stats_grade_min"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Grade max -->
|
||||
<include
|
||||
android:id="@+id/stats_grade_max"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
<!-- Elevation -->
|
||||
<LinearLayout style="@style/StatsSmallGroupContainer" >
|
||||
<!-- Elevation label -->
|
||||
<TextView
|
||||
android:text="@string/stats_elevation"
|
||||
style="@style/StatsSmallLabel" />
|
||||
<TableLayout
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content" >
|
||||
<!-- Elevation -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation gain -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_gain"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation min -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_min"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation max -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_max"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
@@ -17,8 +17,7 @@ limitations under the License.
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:baselineAligned="false"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal" >
|
||||
android:layout_width="match_parent" >
|
||||
<include layout="@layout/track_controller" />
|
||||
<TabHost
|
||||
android:id="@android:id/tabhost"
|
||||
|
||||
@@ -16,8 +16,7 @@ limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal" >
|
||||
android:layout_width="match_parent" >
|
||||
<include layout="@layout/track_controller" />
|
||||
<ListView
|
||||
android:id="@+id/track_list"
|
||||
|
||||
@@ -19,7 +19,6 @@ limitations under the License.
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp" >
|
||||
<ImageView
|
||||
|
||||
@@ -14,10 +14,54 @@ 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.
|
||||
-->
|
||||
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:numColumns="4"
|
||||
android:padding="4dp"
|
||||
android:stretchMode="spacingWidth"
|
||||
android:verticalSpacing="4dp" />
|
||||
android:orientation="vertical" >
|
||||
<GridView
|
||||
android:id="@+id/choose_activity_type_grid_view"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:numColumns="4"
|
||||
android:padding="4dp"
|
||||
android:stretchMode="spacingWidth"
|
||||
android:verticalSpacing="4dp" />
|
||||
<LinearLayout
|
||||
android:id="@+id/choose_activity_type_weight_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical" >
|
||||
<TextView
|
||||
android:id="@+id/choose_activity_type_weight_label"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:paddingLeft="12dp"
|
||||
style="@style/TextMedium" />
|
||||
<FrameLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent" >
|
||||
<EditText
|
||||
android:id="@+id/choose_activity_type_weight"
|
||||
android:inputType="numberDecimal"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_width="match_parent"
|
||||
android:selectAllOnFocus="true"
|
||||
android:singleLine="true"
|
||||
style="@style/TextMedium" />
|
||||
<ImageButton
|
||||
android:background="@android:color/transparent"
|
||||
android:id="@+id/choose_activity_type_weight_image_button"
|
||||
android:layout_gravity="right|center_vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingRight="12dp"
|
||||
android:src="@android:drawable/ic_menu_close_clear_cancel" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -24,103 +24,107 @@ limitations under the License.
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
<!-- A gradient on top of the text -->
|
||||
<ImageView
|
||||
android:alpha=".6"
|
||||
android:contentDescription="@string/icon_marker"
|
||||
android:id="@+id/list_item_text_gradient"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:src="@drawable/text_gradient" />
|
||||
<!-- Table 1 -->
|
||||
<TableLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:paddingBottom="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:shrinkColumns="1"
|
||||
android:stretchColumns="1"
|
||||
style="@style/ListItem" >
|
||||
<!-- Table 1: Row 1 -->
|
||||
<TableRow>
|
||||
<!-- Table 1: Row 1: Column 1: (icon) -->
|
||||
<ImageView
|
||||
android:contentDescription="@string/icon_track"
|
||||
android:id="@+id/list_item_icon"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_width="wrap_content" />
|
||||
<!-- Table 1: Row 1: Column 2: (Table 2) -->
|
||||
<TableLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:shrinkColumns="0"
|
||||
android:stretchColumns="0" >
|
||||
<TableRow>
|
||||
<!-- Table 2: Row 1: Column 1: (name) -->
|
||||
<TextView
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/list_item_name"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:singleLine="true"
|
||||
style="@style/TextMedium" />
|
||||
<!-- Table 2: Row 1: Column 2: (date) -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_date"
|
||||
style="@style/ListItemDate" />
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<!-- Need to match the style set in ListItemUtils.setListItem -->
|
||||
<!-- Table 2: Row 2: Column 1: (shared owner, total time, total distance, and marker count) -->
|
||||
<LinearLayout
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<FrameLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<!-- A gradient on top of the text -->
|
||||
<ImageView
|
||||
android:alpha=".6"
|
||||
android:contentDescription="@string/icon_marker"
|
||||
android:id="@+id/list_item_text_gradient"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:src="@drawable/text_gradient" />
|
||||
<!-- Table 1 -->
|
||||
<TableLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:paddingBottom="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:shrinkColumns="1"
|
||||
android:stretchColumns="1"
|
||||
style="@style/ListItem" >
|
||||
<!-- Table 1: Row 1 -->
|
||||
<TableRow>
|
||||
<!-- Table 1: Row 1: Column 1: (icon) -->
|
||||
<ImageView
|
||||
android:contentDescription="@string/icon_track"
|
||||
android:id="@+id/list_item_icon"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_width="wrap_content" />
|
||||
<!-- Table 1: Row 1: Column 2: (Table 2) -->
|
||||
<TableLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:shrinkColumns="0"
|
||||
android:stretchColumns="0" >
|
||||
<TableRow>
|
||||
<!-- Table 2: Row 1: Column 1: (name) -->
|
||||
<TextView
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/list_item_owner_time_distance"
|
||||
android:id="@+id/list_item_name"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:singleLine="true"
|
||||
style="@style/TextSmall" />
|
||||
<ImageView
|
||||
android:contentDescription="@string/icon_marker"
|
||||
android:id="@+id/list_item_marker_count_icon"
|
||||
style="@style/TextMedium" />
|
||||
<!-- Table 2: Row 1: Column 2: (date) -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_date"
|
||||
style="@style/ListItemDate" />
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<!-- Need to match the style set in ListItemUtils.setListItem -->
|
||||
<!-- Table 2: Row 2: Column 1: (shared owner, total time, total distance, and marker count) -->
|
||||
<LinearLayout
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_menu_insert_marker" />
|
||||
android:layout_width="match_parent" >
|
||||
<TextView
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/list_item_owner_time_distance"
|
||||
android:layout_gravity="bottom"
|
||||
android:singleLine="true"
|
||||
style="@style/TextSmall" />
|
||||
<ImageView
|
||||
android:contentDescription="@string/icon_marker"
|
||||
android:id="@+id/list_item_marker_count_icon"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_menu_insert_marker" />
|
||||
<TextView
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/list_item_marker_count"
|
||||
android:layout_gravity="bottom"
|
||||
android:singleLine="true"
|
||||
style="@style/TextSmall" />
|
||||
</LinearLayout>
|
||||
<!-- Table 2: Row 2: Column 2: (time) -->
|
||||
<TextView
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/list_item_marker_count"
|
||||
android:layout_gravity="bottom"
|
||||
android:singleLine="true"
|
||||
style="@style/TextSmall" />
|
||||
</LinearLayout>
|
||||
<!-- Table 2: Row 2: Column 2: (time) -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_time"
|
||||
style="@style/ListItemDate" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<!-- Table 1: Row 2: (category and description) -->
|
||||
<TextView
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/list_item_category_description"
|
||||
android:layout_column="1"
|
||||
android:maxLines="2"
|
||||
android:singleLine="false"
|
||||
style="@style/TextSmall" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
android:id="@+id/list_item_time"
|
||||
style="@style/ListItemDate" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<!-- Table 1: Row 2: (category and description) -->
|
||||
<TextView
|
||||
android:ellipsize="end"
|
||||
android:id="@+id/list_item_category_description"
|
||||
android:layout_column="1"
|
||||
android:maxLines="2"
|
||||
android:singleLine="false"
|
||||
style="@style/TextSmall" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
@@ -19,9 +19,7 @@ limitations under the License.
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp" >
|
||||
android:orientation="vertical">
|
||||
<include layout="@layout/stats_content" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
+22
-11
@@ -15,17 +15,28 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/stats_small_1line_id"
|
||||
style="@style/StatsSmallValueContainer" >
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
style="@style/StatsSmallValue" />
|
||||
<TextView
|
||||
android:id="@+id/stats_unit"
|
||||
style="@style/StatsUnit" />
|
||||
style="@style/StatsLargeItemContainer" >
|
||||
<!-- Label -->
|
||||
<TextView
|
||||
android:id="@+id/stats_label"
|
||||
android:layout_gravity="bottom"
|
||||
android:paddingLeft="8dp"
|
||||
style="@style/StatsSmallLabel" />
|
||||
style="@style/StatsLargeLabel" />
|
||||
<!-- Value container -->
|
||||
<LinearLayout style="@style/StatsLargeValueContainer" >
|
||||
<!-- Value -->
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
style="@style/StatsLargeValue" />
|
||||
<!-- Unit -->
|
||||
<TextView
|
||||
android:id="@+id/stats_unit"
|
||||
style="@style/StatsUnit" />
|
||||
<!-- Activity type -->
|
||||
<Spinner
|
||||
android:id="@+id/stats_activity_type_icon"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginTop="-6dp"
|
||||
android:layout_width="wrap_content" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -15,108 +15,114 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<!-- Row 1: distance and speed -->
|
||||
<!-- Distance and calorie -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_distance"
|
||||
layout="@layout/stats_large_item" />
|
||||
<View style="@style/StatsVerticalSpacer" />
|
||||
<include
|
||||
android:id="@+id/stats_speed"
|
||||
layout="@layout/stats_large_item" />
|
||||
android:id="@+id/stats_calorie"
|
||||
layout="@layout/stats_calorie" />
|
||||
</LinearLayout>
|
||||
<!-- Row 2: total time and average speed -->
|
||||
<!-- Total time and moving time -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_total_time"
|
||||
layout="@layout/stats_large_item" />
|
||||
<View style="@style/StatsVerticalSpacer" />
|
||||
<include
|
||||
android:id="@+id/stats_average_speed"
|
||||
layout="@layout/stats_large_item" />
|
||||
</LinearLayout>
|
||||
<!-- Row 3: horizontal line -->
|
||||
<View style="@style/StatsHorizontalLine" />
|
||||
<!-- Row 3: moving time and average moving speed -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_moving_time"
|
||||
layout="@layout/stats_small_item" />
|
||||
<View style="@style/StatsVerticalSpacer" />
|
||||
<include
|
||||
android:id="@+id/stats_average_moving_time"
|
||||
layout="@layout/stats_small_item" />
|
||||
layout="@layout/stats_large_item" />
|
||||
</LinearLayout>
|
||||
<!-- Row 4: empty and max speed -->
|
||||
<!-- Horizontal line -->
|
||||
<View style="@style/StatsHorizontalLine" />
|
||||
<!-- Speed and average speed -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<include
|
||||
android:id="@+id/stats_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
<include
|
||||
android:id="@+id/stats_average_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
<!-- Max speed and average moving speed -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<LinearLayout style="@style/StatsSmallItemContainer" />
|
||||
<View style="@style/StatsVerticalSpacer" />
|
||||
<include
|
||||
android:id="@+id/stats_max_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
<include
|
||||
android:id="@+id/stats_average_moving_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
<!-- Row 5: horizontal line -->
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_grade_elevation_horizontal_line"
|
||||
android:id="@+id/stats_grade_elevation_separator"
|
||||
style="@style/StatsHorizontalLine" />
|
||||
<!-- Row 5: grade and elevation -->
|
||||
<LinearLayout
|
||||
android:id="@+id/stats_grade_elevation_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<!-- Grade -->
|
||||
<LinearLayout style="@style/StatsSmallItemContainer" >
|
||||
<LinearLayout style="@style/StatsSmallGroupContainer" >
|
||||
<!-- Grade label -->
|
||||
<TextView
|
||||
android:text="@string/stats_grade"
|
||||
style="@style/StatsSmallLabel" />
|
||||
<!-- Grade min -->
|
||||
<include
|
||||
android:id="@+id/stats_grade_min"
|
||||
layout="@layout/stats_small_item_1line" />
|
||||
<!-- Grade max -->
|
||||
<include
|
||||
android:id="@+id/stats_grade_max"
|
||||
layout="@layout/stats_small_item_1line" />
|
||||
<TableLayout
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content" >
|
||||
<!-- Grade min -->
|
||||
<include
|
||||
android:id="@+id/stats_grade_min"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Grade max -->
|
||||
<include
|
||||
android:id="@+id/stats_grade_max"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
<View style="@style/StatsVerticalSpacer" />
|
||||
<!-- Elevation -->
|
||||
<LinearLayout style="@style/StatsSmallItemContainer" >
|
||||
<LinearLayout style="@style/StatsSmallGroupContainer" >
|
||||
<!-- Elevation label -->
|
||||
<TextView
|
||||
android:text="@string/stats_elevation"
|
||||
style="@style/StatsSmallLabel" />
|
||||
<!-- Elevation -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation"
|
||||
layout="@layout/stats_small_item_1line" />
|
||||
<!-- Elevation gain -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_gain"
|
||||
layout="@layout/stats_small_item_1line" />
|
||||
<!-- Elevation min -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_min"
|
||||
layout="@layout/stats_small_item_1line" />
|
||||
<!-- Elevation max -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_max"
|
||||
layout="@layout/stats_small_item_1line" />
|
||||
<TableLayout
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content" >
|
||||
<!-- Elevation -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation gain -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_gain"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation min -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_min"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation max -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_max"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<!-- Row 6: horizontal line -->
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_coordinate_horizontal_line"
|
||||
android:id="@+id/stats_coordinate_separator"
|
||||
style="@style/StatsHorizontalLine" />
|
||||
<!-- Row 6: latitude and longitude -->
|
||||
<!-- Latitude and longitude -->
|
||||
<LinearLayout
|
||||
android:id="@+id/stats_coordinate_container"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -124,30 +130,8 @@ limitations under the License.
|
||||
<include
|
||||
android:id="@+id/stats_latitude"
|
||||
layout="@layout/stats_small_item" />
|
||||
<View style="@style/StatsVerticalSpacer" />
|
||||
<include
|
||||
android:id="@+id/stats_longitude"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
<!-- Row 7: calorie horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_calorie_horizontal_line"
|
||||
style="@style/StatsHorizontalLine" />
|
||||
<!-- Row 7: calorie -->
|
||||
<LinearLayout
|
||||
android:id="@+id/stats_calorie_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
<!-- calorie value -->
|
||||
<include
|
||||
android:id="@+id/stats_calorie"
|
||||
layout="@layout/stats_small_item" />
|
||||
<!-- Setup activity type-->
|
||||
<Button
|
||||
android:id="@+id/stats_calorie_setup"
|
||||
android:layout_gravity="right"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:text="@string/stats_calorie_setup" />
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
@@ -15,17 +15,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/stats_large_item_id"
|
||||
style="@style/StatsLargeItemContainer" >
|
||||
<!-- Distance label -->
|
||||
<!-- Label -->
|
||||
<TextView
|
||||
android:id="@+id/stats_label"
|
||||
style="@style/StatsLargeLabel" />
|
||||
<!-- Distance value -->
|
||||
<!-- Value container -->
|
||||
<LinearLayout style="@style/StatsLargeValueContainer" >
|
||||
<!-- Value -->
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
style="@style/StatsLargeValue" />
|
||||
<!-- Unit -->
|
||||
<TextView
|
||||
android:id="@+id/stats_unit"
|
||||
style="@style/StatsUnit" />
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content" >
|
||||
<!-- Value container -->
|
||||
<LinearLayout
|
||||
android:layout_gravity="right|bottom"
|
||||
style="@style/StatsSmallValueContainer" >
|
||||
<!-- Value -->
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
style="@style/StatsSmallValue" />
|
||||
<!-- Unit -->
|
||||
<TextView
|
||||
android:id="@+id/stats_unit"
|
||||
style="@style/StatsUnit" />
|
||||
</LinearLayout>
|
||||
<!-- Label container -->
|
||||
<LinearLayout
|
||||
android:layout_gravity="left|bottom"
|
||||
style="@style/StatsSmallValueContainer" >
|
||||
<TextView
|
||||
android:visibility="invisible"
|
||||
style="@style/StatsSmallValue" />
|
||||
<!-- Label -->
|
||||
<TextView
|
||||
android:id="@+id/stats_label"
|
||||
android:layout_gravity="bottom"
|
||||
android:paddingLeft="8dp"
|
||||
style="@style/StatsSmallLabel" />
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
@@ -15,16 +15,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/stats_small_item_id"
|
||||
style="@style/StatsSmallItemContainer" >
|
||||
<!-- Label -->
|
||||
<TextView
|
||||
android:id="@+id/stats_label"
|
||||
style="@style/StatsSmallLabel" />
|
||||
<!-- Distance value -->
|
||||
<!-- Value container -->
|
||||
<LinearLayout style="@style/StatsSmallValueContainer" >
|
||||
<!-- Value -->
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
style="@style/StatsSmallValue" />
|
||||
<!-- Unit -->
|
||||
<TextView
|
||||
android:id="@+id/stats_unit"
|
||||
style="@style/StatsUnit" />
|
||||
|
||||
@@ -20,7 +20,6 @@ limitations under the License.
|
||||
android:id="@+id/track_controler_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp" >
|
||||
<View
|
||||
|
||||
@@ -23,8 +23,7 @@ limitations under the License.
|
||||
android:id="@android:id/tabs"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal" />
|
||||
android:layout_width="match_parent" />
|
||||
<FrameLayout
|
||||
android:id="@android:id/tabcontent"
|
||||
android:layout_height="0dp"
|
||||
|
||||
@@ -38,8 +38,7 @@ limitations under the License.
|
||||
style="@style/EditLabel.WithPaddingTop" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal" >
|
||||
android:layout_width="match_parent" >
|
||||
<AutoCompleteTextView
|
||||
android:gravity="bottom"
|
||||
android:hint="@string/track_edit_activity_type_hint"
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/TrackWidgetBoundingBox" >
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingTop="4dp"
|
||||
style="@style/TrackWidgetFrame" >
|
||||
|
||||
@@ -34,7 +34,6 @@ limitations under the License.
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:measureWithLargestChild">true</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
</style>
|
||||
<style name="BottomButtonDivider">
|
||||
<item name="android:background">?android:attr/dividerVertical</item>
|
||||
|
||||
@@ -18,22 +18,22 @@ limitations under the License.
|
||||
<!-- The style for choose activity text -->
|
||||
<style name="StatsSmallLabel" parent="@style/TextSmall">
|
||||
<!-- same as StatsSmallLabel in values/styles.xml except adding textAllCaps -->
|
||||
<item name="android:gravity">right</item>
|
||||
<item name="android:layout_gravity">right</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
</style>
|
||||
<style name="StatsLargeLabel" parent="@style/TextSmall">
|
||||
<!-- same as StatsLargeLabel in values/styles.xml except adding textAllCaps -->
|
||||
<item name="android:gravity">right</item>
|
||||
<item name="android:layout_gravity">right</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:textColor">@color/stats_large_label</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
<style name="TextChooseActivity">
|
||||
</style>
|
||||
<style name="TextChooseActivity">
|
||||
<!-- Same as TextChooseActivity in values/styles.xml except removing textSize and using textAppearance -->
|
||||
<item name="android:textAppearance">?android:attr/textAppearanceListItemSmall</item>
|
||||
</style>
|
||||
|
||||
@@ -42,8 +42,6 @@ limitations under the License.
|
||||
<string name="stats_rate_key">statsRate</string>
|
||||
<string name="stats_show_coordinate_key">statsShowCoordinate</string>
|
||||
<string name="stats_show_grade_elevation_key">statsShowGradeElevation</string>
|
||||
<string name="stats_show_calorie_key">statsShowCalorie</string>
|
||||
<string name="stats_weight_key">statsWeight</string>
|
||||
<string name="stats_units_key">statsUnits</string>
|
||||
<string name="track_color_mode_medium_key">trackColorModeMedium</string>
|
||||
<string name="track_color_mode_percentage_key">trackColorModePercentage</string>
|
||||
@@ -70,6 +68,7 @@ limitations under the License.
|
||||
<string name="google_account_key">googleAccount</string>
|
||||
<string name="recording_track_id_key">recordingTrackId</string>
|
||||
<string name="recording_track_paused_key">recordingTrackPaused</string>
|
||||
<string name="weight_key">weight</string>
|
||||
<!-- Keys for non-persisted preferences. Sorted alphabetically. DO NOT TRANSLATE. -->
|
||||
<string name="settings_advanced_key">settingsAdvanced</string>
|
||||
<string name="settings_backup_key">settingsBackup</string>
|
||||
|
||||
@@ -555,7 +555,15 @@ limitations under the License.
|
||||
translation_description="Label for a track segment">
|
||||
Segment
|
||||
</string>
|
||||
|
||||
<string name="description_weight_imperial"
|
||||
translation_description="Label for weight in pounds.">
|
||||
Weight (lb)
|
||||
</string>
|
||||
<string name="description_weight_metric"
|
||||
translation_description="Label for weight in kilograms.">
|
||||
Weight (kg)
|
||||
</string>
|
||||
|
||||
<!-- Eula -->
|
||||
<string name="eula_accept"
|
||||
translation_description="In the 'Terms of Service' dialog, the button to accept.">
|
||||
@@ -2025,16 +2033,6 @@ limitations under the License.
|
||||
Character limit: ~23 (1 line)">
|
||||
Grade/Elevation
|
||||
</string>
|
||||
<string name="settings_stats_calorie_weight"
|
||||
translation_description="In the 'Stats settings, the option to configure user's weight.
|
||||
|
||||
Character limit: ~23 (1 line)">
|
||||
Weight
|
||||
</string>
|
||||
<string name="settings_stats_calorie_weight_description"
|
||||
translation_description="In the 'Stats settings, the description message about weight setting item.">
|
||||
Weight for calculating calories.
|
||||
</string>
|
||||
<string name="settings_stats_rate_title"
|
||||
translation_description="In the 'Stats' settings, the option to change the preferred rate,
|
||||
either speed (mi/hr) or pace (min/mi).
|
||||
@@ -2157,11 +2155,11 @@ limitations under the License.
|
||||
Character limit: ~23 (1 line)">
|
||||
Calories
|
||||
</string>
|
||||
<string name="stats_calorie_setup"
|
||||
translation_description="In the 'Stats' page, the button to setup calorie.
|
||||
<string name="stats_calorie_no_calculation"
|
||||
translation_description="In the 'Stats' page, the message when calories cannot be calculated.
|
||||
|
||||
Character limit: ~23 (1 line)">
|
||||
Setup Activity Type
|
||||
%1$s activity type. E.g., 'driving'.">
|
||||
Calories can\'t be calculated with %1$s.
|
||||
</string>
|
||||
<string name="stats_fastest_pace"
|
||||
translation_description="In the 'Stats' page, the fastest pace.">
|
||||
@@ -2514,12 +2512,6 @@ limitations under the License.
|
||||
%1$d an integer value. E.g., '2'.">
|
||||
%1$d kB
|
||||
</string>
|
||||
<string name="value_integer_kilogram"
|
||||
translation_description="Display an integer value in kilogram (kg).
|
||||
|
||||
%1$d an integer value. E.g., '2'.">
|
||||
%1$d kg
|
||||
</string>
|
||||
<string name="value_integer_kilometer"
|
||||
translation_description="In a setting picker, the string format to display an integer value in
|
||||
kilometers (km).
|
||||
@@ -2594,12 +2586,6 @@ limitations under the License.
|
||||
%1$d an integer value. E.g., '2'.">
|
||||
%1$d %%
|
||||
</string>
|
||||
<string name="value_integer_pound"
|
||||
translation_description="Display an integer value in pound (lb).
|
||||
|
||||
%1$d an integer value. E.g., '2'.">
|
||||
%1$d lb
|
||||
</string>
|
||||
<string name="value_integer_second"
|
||||
translation_description="In a setting picker, the string format to display an integer value in
|
||||
seconds (s).
|
||||
|
||||
@@ -30,7 +30,6 @@ limitations under the License.
|
||||
<style name="BottomButtonBar">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
</style>
|
||||
<!-- bottom button divider -->
|
||||
<style name="BottomButtonDivider">
|
||||
@@ -84,7 +83,7 @@ limitations under the License.
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
<item name="android:layout_marginLeft">20dp</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
</style>
|
||||
</style>
|
||||
<!-- help answer -->
|
||||
<style name="HelpAnswer" parent="@style/TextSmall">
|
||||
<item name="android:autoLink">web</item>
|
||||
@@ -133,30 +132,31 @@ limitations under the License.
|
||||
<style name="StatsHorizontalLine">
|
||||
<item name="android:background">@color/stats_separator</item>
|
||||
<item name="android:layout_height">1dp</item>
|
||||
<item name="android:layout_marginLeft">8dp</item>
|
||||
<item name="android:layout_marginRight">8dp</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
</style>
|
||||
<!-- stats large item container -->
|
||||
<style name="StatsLargeItemContainer">
|
||||
<item name="android:layout_gravity">top</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">10</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:paddingBottom">6dp</item>
|
||||
<item name="android:paddingLeft">8dp</item>
|
||||
<item name="android:paddingRight">8dp</item>
|
||||
<item name="android:paddingTop">6dp</item>
|
||||
</style>
|
||||
<!-- stats large label -->
|
||||
<style name="StatsLargeLabel" parent="@style/TextSmall">
|
||||
<item name="android:gravity">right</item>
|
||||
<item name="android:layout_gravity">right</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:textColor">@color/stats_large_label</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
<!-- stats large value -->
|
||||
<style name="StatsLargeValue">
|
||||
<item name="android:layout_gravity">bottom</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
@@ -166,7 +166,7 @@ limitations under the License.
|
||||
</style>
|
||||
<!-- stats large value container -->
|
||||
<style name="StatsLargeValueContainer">
|
||||
<item name="android:layout_gravity">right</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">-4dp</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
@@ -174,20 +174,26 @@ limitations under the License.
|
||||
<!-- stats scroll view -->
|
||||
<style name="StatsScrollView" parent="@style/ScrollView">
|
||||
<item name="android:scrollbarStyle">outsideOverlay</item>
|
||||
</style>
|
||||
<!-- stats small item container -->
|
||||
</style>
|
||||
<!-- stats small group container -->
|
||||
<style name="StatsSmallGroupContainer" parent="@style/StatsLargeItemContainer">
|
||||
<item name="android:layout_gravity">top|center</item>
|
||||
<item name="android:paddingTop">12dp</item>
|
||||
</style>
|
||||
<!-- stats small item container, for placing inside StatsSmallGroupContainer -->
|
||||
<style name="StatsSmallItemContainer" parent="@style/StatsLargeItemContainer">
|
||||
<item name="android:paddingTop">12dp</item>
|
||||
</style>
|
||||
<!-- stats small label -->
|
||||
<style name="StatsSmallLabel" parent="@style/TextSmall">
|
||||
<item name="android:gravity">right</item>
|
||||
<item name="android:layout_gravity">right</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
<!-- stats small value -->
|
||||
<style name="StatsSmallValue" parent="@style/TextLarge">
|
||||
<item name="android:layout_gravity">bottom</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
</style>
|
||||
@@ -197,20 +203,15 @@ limitations under the License.
|
||||
</style>
|
||||
<!-- stats unit -->
|
||||
<style name="StatsUnit" parent="@style/TextSmall">
|
||||
<item name="android:layout_gravity">bottom</item>
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
<!-- stats vertical spacer -->
|
||||
<style name="StatsVerticalSpacer">
|
||||
<item name="android:layout_height">match_parent</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">0dp</item>
|
||||
</style>
|
||||
<style name="Text">
|
||||
<style name="Text">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
</style>
|
||||
</style>
|
||||
<!-- text choose activity -->
|
||||
<style name="TextChooseActivity">
|
||||
<item name="android:textSize">18sp</item>
|
||||
@@ -257,7 +258,6 @@ limitations under the License.
|
||||
<item name="android:layout_height">0dp</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
<item name="android:paddingBottom">4dp</item>
|
||||
<item name="android:paddingTop">4dp</item>
|
||||
</style>
|
||||
@@ -290,7 +290,6 @@ limitations under the License.
|
||||
<style name="TrackWidgetHorizontalSeparator">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
</style>
|
||||
<!-- track widget item container -->
|
||||
<style name="TrackWidgetItemContainer">
|
||||
@@ -354,7 +353,7 @@ limitations under the License.
|
||||
<item name="android:layout_weight">2</item>
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
</style>
|
||||
<!-- track widget stats container -->
|
||||
<style name="TrackWidgetStatsContainer">
|
||||
<item name="android:clickable">true</item>
|
||||
@@ -369,7 +368,6 @@ limitations under the License.
|
||||
<item name="android:layout_height">0dp</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
</style>
|
||||
<!-- track widget unit -->
|
||||
<style name="TrackWidgetUnit">
|
||||
@@ -412,7 +410,6 @@ limitations under the License.
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">-4dp</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
</style>
|
||||
<!-- track widget value container 4x3 -->
|
||||
<style name="TrackWidgetValueContainer4x3" parent="@style/TrackWidgetValueContainer">
|
||||
|
||||
@@ -33,16 +33,5 @@ limitations under the License.
|
||||
android:defaultValue="false"
|
||||
android:key="@string/stats_show_coordinate_key"
|
||||
android:title="@string/settings_stats_coordinate" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/stats_show_calorie_key"
|
||||
android:title="@string/stats_calorie" />
|
||||
<EditTextPreference
|
||||
android:dependency="@string/stats_show_calorie_key"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:key="@string/stats_weight_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_stats_calorie_weight" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
@@ -19,7 +19,7 @@ package com.google.android.apps.mytracks;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
|
||||
import com.google.android.apps.mytracks.util.StatsUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class AggregatedStatsActivity extends AbstractMyTracksActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatsUtils.setTripStatisticsValues(
|
||||
this, this, null, getTripStatistics(), PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
this, this, null, getTripStatistics(), ActivityType.WALKING, null);
|
||||
StatsUtils.setLocationValues(this, this, null, null, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,6 @@ public class Constants {
|
||||
*/
|
||||
public static final String ACCOUNT_TYPE = "com.google";
|
||||
|
||||
/**
|
||||
* Maximum number of track points that will be loaded at one time. With
|
||||
* recording frequency of 2 seconds, 20000 corresponds to 11.1 hours.
|
||||
*/
|
||||
public static final int MAX_LOADED_TRACK_POINTS = 20000;
|
||||
|
||||
/**
|
||||
* Maximum number of waypoints that will be loaded at one time.
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -32,6 +33,9 @@ import android.os.AsyncTask;
|
||||
*/
|
||||
public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
// 1 second in milliseconds
|
||||
private static final long ONE_SECOND = (long) UnitConversions.S_TO_MS;
|
||||
|
||||
private DeleteActivity deleteActivity;
|
||||
private final long[] trackIds;
|
||||
private final Context context;
|
||||
@@ -82,7 +86,7 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
Thread.sleep(ONE_SECOND);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.android.apps.mytracks.services.sensors.SensorManager;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorManagerFactory;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
@@ -41,7 +42,9 @@ import android.widget.TextView;
|
||||
public class SensorStateActivity extends AbstractMyTracksActivity {
|
||||
|
||||
private static final String TAG = SensorStateActivity.class.getName();
|
||||
private static final long ONE_SECOND = 1000;
|
||||
|
||||
// 1 second in milliseconds
|
||||
private static final long ONE_SECOND = (long) UnitConversions.S_TO_MS;
|
||||
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private Handler handler;
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.google.android.apps.mytracks;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -38,7 +39,9 @@ import android.widget.TextView;
|
||||
public class TrackController {
|
||||
|
||||
private static final String TAG = TrackController.class.getSimpleName();
|
||||
private static final int ONE_SECOND = 1000;
|
||||
|
||||
// 1 second in milliseconds
|
||||
private static final long ONE_SECOND = (long) UnitConversions.S_TO_MS;
|
||||
|
||||
private final Activity activity;
|
||||
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
|
||||
@@ -23,6 +23,8 @@ import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
import com.google.android.apps.mytracks.fragments.ChartFragment;
|
||||
import com.google.android.apps.mytracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import com.google.android.apps.mytracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
|
||||
import com.google.android.apps.mytracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import com.google.android.apps.mytracks.fragments.ExportDialogFragment;
|
||||
import com.google.android.apps.mytracks.fragments.ExportDialogFragment.ExportCaller;
|
||||
@@ -37,11 +39,15 @@ import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection
|
||||
import com.google.android.apps.mytracks.settings.SettingsActivity;
|
||||
import com.google.android.apps.mytracks.util.AnalyticsUtils;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.GoogleFeedbackUtils;
|
||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -74,7 +80,8 @@ import java.util.Date;
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TrackDetailActivity extends AbstractSendToGoogleActivity implements ExportCaller {
|
||||
public class TrackDetailActivity extends AbstractSendToGoogleActivity
|
||||
implements ChooseActivityTypeCaller, ExportCaller {
|
||||
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
@@ -612,4 +619,23 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements
|
||||
}
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
public void chooseActivityType(String category) {
|
||||
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(),
|
||||
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue, boolean newWeight) {
|
||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||
TrackUtils.updateTrack(
|
||||
this, track, null, category, null, myTracksProviderUtils, trackRecordingServiceConnection, newWeight);
|
||||
|
||||
// Add toast if cannot calculate calorie
|
||||
if (CalorieUtils.getActivityType(this, category) == ActivityType.INVALID) {
|
||||
String message = getString(R.string.stats_calorie_no_calculation, category);
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,17 +21,12 @@ import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import com.google.android.apps.mytracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackNameUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
@@ -42,7 +37,6 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
|
||||
/**
|
||||
@@ -70,6 +64,8 @@ public class TrackEditActivity extends AbstractMyTracksActivity
|
||||
private AutoCompleteTextView activityType;
|
||||
private Spinner activityTypeIcon;
|
||||
private EditText description;
|
||||
|
||||
private boolean newWeight = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
@@ -101,14 +97,14 @@ public class TrackEditActivity extends AbstractMyTracksActivity
|
||||
this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||
activityType.setAdapter(adapter);
|
||||
activityType.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||
TrackEditActivity.this, (String) activityType.getAdapter().getItem(position)));
|
||||
}
|
||||
});
|
||||
activityType.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (!hasFocus) {
|
||||
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||
@@ -125,55 +121,28 @@ public class TrackEditActivity extends AbstractMyTracksActivity
|
||||
iconValue = track.getIcon();
|
||||
}
|
||||
|
||||
// Add a border around the icon
|
||||
Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeResource(getResources(), R.drawable.ic_track_airplane, options);
|
||||
final int padding = 4;
|
||||
final int width = options.outWidth + 2 * padding;
|
||||
final int height = options.outHeight + 2 * padding;
|
||||
|
||||
activityTypeIcon = (Spinner) findViewById(R.id.track_edit_activity_type_icon);
|
||||
ArrayAdapter<StringBuilder> activityTypeIconAdapter = new ArrayAdapter<StringBuilder>(
|
||||
this, android.R.layout.simple_spinner_item,
|
||||
new StringBuilder[] {new StringBuilder(iconValue)}) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, android.view.ViewGroup parent) {
|
||||
ImageView imageView =
|
||||
convertView != null ? (ImageView) convertView : new ImageView(getContext());
|
||||
Bitmap source = BitmapFactory.decodeResource(
|
||||
getResources(), TrackIconUtils.getIconDrawable(getItem(position).toString()));
|
||||
if (ApiAdapterFactory.getApiAdapter().isSpinnerBackgroundLight()) {
|
||||
source = TrackIconUtils.invertBitmap(source);
|
||||
}
|
||||
imageView.setImageBitmap(source);
|
||||
imageView.setMinimumHeight(height);
|
||||
imageView.setMinimumWidth(width);
|
||||
imageView.setPadding(padding, padding, padding, padding);
|
||||
return imageView;
|
||||
}
|
||||
};
|
||||
activityTypeIcon.setAdapter(activityTypeIconAdapter);
|
||||
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(this, iconValue));
|
||||
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
new ChooseActivityTypeDialogFragment().show(getSupportFragmentManager(),
|
||||
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
||||
getSupportFragmentManager(),
|
||||
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
|
||||
@Override
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
new ChooseActivityTypeDialogFragment().show(getSupportFragmentManager(),
|
||||
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
||||
getSupportFragmentManager(),
|
||||
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -184,36 +153,9 @@ public class TrackEditActivity extends AbstractMyTracksActivity
|
||||
save.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
track.setName(name.getText().toString());
|
||||
|
||||
String category = activityType.getText().toString();
|
||||
boolean newCategory = !category.equals(track.getCategory());
|
||||
boolean isRecording = trackId
|
||||
== PreferencesUtils.getLong(TrackEditActivity.this, R.string.recording_track_id_key);
|
||||
|
||||
if (newCategory && !isRecording) {
|
||||
// Update calorie
|
||||
double calorie = CalorieUtils.getTrackCalorie(TrackEditActivity.this, track, -1L);
|
||||
track.getTripStatistics().setCalorie(calorie);
|
||||
}
|
||||
|
||||
track.setCategory(category);
|
||||
track.setIcon(TrackIconUtils.getIconValue(TrackEditActivity.this, category));
|
||||
track.setDescription(description.getText().toString());
|
||||
track.setModifiedTime(System.currentTimeMillis());
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
|
||||
if (newCategory && isRecording) {
|
||||
// Update calorie through track recording service
|
||||
TrackRecordingServiceConnectionUtils.updateCalorie(trackRecordingServiceConnection);
|
||||
}
|
||||
|
||||
boolean driveSync = PreferencesUtils.getBoolean(
|
||||
TrackEditActivity.this, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT);
|
||||
if (driveSync) {
|
||||
PreferencesUtils.addToList(TrackEditActivity.this, R.string.drive_edited_list_key,
|
||||
PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT, String.valueOf(track.getId()));
|
||||
}
|
||||
TrackUtils.updateTrack(TrackEditActivity.this, track, name.getText().toString(),
|
||||
activityType.getText().toString(), description.getText().toString(),
|
||||
myTracksProviderUtils, trackRecordingServiceConnection, newWeight);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
@@ -261,26 +203,18 @@ public class TrackEditActivity extends AbstractMyTracksActivity
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.track_edit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the activity type icon.
|
||||
*
|
||||
* @param value the icon value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
private void setActivityTypeIcon(String value) {
|
||||
iconValue = value;
|
||||
ArrayAdapter<StringBuilder> adapter =
|
||||
(ArrayAdapter<StringBuilder>) activityTypeIcon.getAdapter();
|
||||
StringBuilder stringBuilder = adapter.getItem(0);
|
||||
stringBuilder.delete(0, stringBuilder.length());
|
||||
stringBuilder.append(value);
|
||||
adapter.notifyDataSetChanged();
|
||||
TrackIconUtils.setIconSpinner(activityTypeIcon, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String value) {
|
||||
activityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
||||
public void onChooseActivityTypeDone(String value, boolean hasNewWeight) {
|
||||
if (!newWeight) {
|
||||
newWeight = hasNewWeight;
|
||||
}
|
||||
setActivityTypeIcon(value);
|
||||
activityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,50 +91,58 @@ public class MyTracksProvider extends ContentProvider {
|
||||
} else {
|
||||
// Incremental upgrades. One if statement per DB version.
|
||||
|
||||
// Add track points SENSOR column
|
||||
// Add track point SENSOR column
|
||||
if (oldVersion <= 17) {
|
||||
Log.w(TAG, "Upgrade DB: Adding sensor column.");
|
||||
Log.w(TAG, "Upgrade DB: Adding track point sensor column.");
|
||||
db.execSQL("ALTER TABLE " + TrackPointsColumns.TABLE_NAME + " ADD "
|
||||
+ TrackPointsColumns.SENSOR + " BLOB");
|
||||
}
|
||||
|
||||
// Add tracks TABLEID column
|
||||
// Add track TABLEID column
|
||||
if (oldVersion <= 18) {
|
||||
Log.w(TAG, "Upgrade DB: Adding tableid column.");
|
||||
Log.w(TAG, "Upgrade DB: Adding track tableid column.");
|
||||
db.execSQL("ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD " + TracksColumns.TABLEID
|
||||
+ " STRING");
|
||||
}
|
||||
|
||||
// Add tracks ICON column
|
||||
// Add track ICON column
|
||||
if (oldVersion <= 19) {
|
||||
Log.w(TAG, "Upgrade DB: Adding icon column.");
|
||||
Log.w(TAG, "Upgrade DB: Adding track icon column.");
|
||||
db.execSQL(
|
||||
"ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD " + TracksColumns.ICON + " STRING");
|
||||
}
|
||||
|
||||
// Add track DRIVEID, MODIFIEDTIME, and SHAREDWITHME columns
|
||||
if (oldVersion <= 20) {
|
||||
Log.w(TAG, "Upgrade DB: Adding driveid column.");
|
||||
Log.w(TAG, "Upgrade DB: Adding track driveid column.");
|
||||
db.execSQL("ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD " + TracksColumns.DRIVEID
|
||||
+ " STRING");
|
||||
Log.w(TAG, "Upgrade DB: Adding modifiedtime column.");
|
||||
Log.w(TAG, "Upgrade DB: Adding track modifiedtime column.");
|
||||
db.execSQL("ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD "
|
||||
+ TracksColumns.MODIFIEDTIME + " INTEGER");
|
||||
Log.w(TAG, "Upgrade DB: Adding sharedwithme column.");
|
||||
|
||||
Log.w(TAG, "Upgrade DB: Adding track sharedwithme column.");
|
||||
db.execSQL("ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD "
|
||||
+ TracksColumns.SHAREDWITHME + " INTEGER");
|
||||
Log.w(TAG, "Upgrade DB: Adding sharedowner column.");
|
||||
|
||||
Log.w(TAG, "Upgrade DB: Adding track sharedowner column.");
|
||||
db.execSQL("ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD " + TracksColumns.SHAREDOWNER
|
||||
+ " STRING");
|
||||
}
|
||||
|
||||
|
||||
// Add waypoint CALORIE and PHOTOURL columns. Add track CALORIE column.
|
||||
if (oldVersion <= 21) {
|
||||
Log.w(TAG, "Upgrade DB: Adding photo url column.");
|
||||
db.execSQL("ALTER TABLE " + WaypointsColumns.TABLE_NAME + " ADD " + WaypointsColumns.PHOTOURL
|
||||
+ " STRING");
|
||||
Log.w(TAG, "Upgrade DB: Adding waypoint calorie column.");
|
||||
db.execSQL("ALTER TABLE " + WaypointsColumns.TABLE_NAME + " ADD "
|
||||
+ WaypointsColumns.CALORIE + " FLOAT");
|
||||
|
||||
Log.w(TAG, "Upgrade DB: Adding waypoint photo url column.");
|
||||
db.execSQL("ALTER TABLE " + WaypointsColumns.TABLE_NAME + " ADD "
|
||||
+ WaypointsColumns.PHOTOURL + " STRING");
|
||||
|
||||
Log.w(TAG, "Upgrade DB: Adding track calorie column.");
|
||||
db.execSQL("ALTER TABLE " + TracksColumns.TABLE_NAME + " ADD " + TracksColumns.CALORIE
|
||||
+ " INTEGER");
|
||||
+ " FLOAT");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +360,8 @@ public class SearchEngine {
|
||||
}
|
||||
|
||||
// Score recent tracks higher.
|
||||
long timeAgoHours = (query.currentTimestamp - timestamp) / (60L * 60L * 1000L);
|
||||
long timeAgoHours = (long) ((query.currentTimestamp - timestamp) * UnitConversions.MS_TO_S
|
||||
* UnitConversions.S_TO_MIN * UnitConversions.MIN_TO_HR);
|
||||
if (timeAgoHours > 0L) {
|
||||
return squash(timeAgoHours);
|
||||
} else {
|
||||
|
||||
@@ -492,52 +492,60 @@ public class TrackDataHub implements DataSourceListener {
|
||||
|
||||
long lastTrackPointId = myTracksProviderUtils.getLastTrackPointId(selectedTrackId);
|
||||
int samplingFrequency = -1;
|
||||
LocationIterator iterator = myTracksProviderUtils.getTrackPointLocationIterator(selectedTrackId,
|
||||
localLastSeenLocationId + 1, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
boolean includeNextPoint = false;
|
||||
while (iterator.hasNext()) {
|
||||
Location location = iterator.next();
|
||||
long locationId = iterator.getLocationId();
|
||||
LocationIterator locationIterator = null;
|
||||
|
||||
// Stop if past the last wanted point
|
||||
if (maxPointId != -1L && locationId > maxPointId) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
locationIterator = myTracksProviderUtils.getTrackPointLocationIterator(selectedTrackId,
|
||||
localLastSeenLocationId + 1, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
|
||||
if (localFirstSeenLocationId == -1) {
|
||||
localFirstSeenLocationId = locationId;
|
||||
}
|
||||
while (locationIterator.hasNext()) {
|
||||
Location location = locationIterator.next();
|
||||
long locationId = locationIterator.getLocationId();
|
||||
|
||||
if (samplingFrequency == -1) {
|
||||
long numTotalPoints = Math.max(0L, lastTrackPointId - localFirstSeenLocationId);
|
||||
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
|
||||
}
|
||||
|
||||
if (!LocationUtils.isValidLocation(location)) {
|
||||
// TODO: also include the last valid point before a split
|
||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||
trackDataListener.onSegmentSplit(location);
|
||||
includeNextPoint = true;
|
||||
// Stop if past the last wanted point
|
||||
if (maxPointId != -1L && locationId > maxPointId) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Also include the last point if the selected track is not recording.
|
||||
if (includeNextPoint || (localNumLoadedPoints % samplingFrequency == 0)
|
||||
|| (locationId == lastTrackPointId && !isSelectedTrackRecording())) {
|
||||
includeNextPoint = false;
|
||||
|
||||
if (localFirstSeenLocationId == -1) {
|
||||
localFirstSeenLocationId = locationId;
|
||||
}
|
||||
|
||||
if (samplingFrequency == -1) {
|
||||
long numTotalPoints = Math.max(0L, lastTrackPointId - localFirstSeenLocationId);
|
||||
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
|
||||
}
|
||||
|
||||
if (!LocationUtils.isValidLocation(location)) {
|
||||
// TODO: also include the last valid point before a split
|
||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||
trackDataListener.onSampledInTrackPoint(location);
|
||||
trackDataListener.onSegmentSplit(location);
|
||||
includeNextPoint = true;
|
||||
}
|
||||
} else {
|
||||
for (TrackDataListener trackDataListener : sampledOutListeners) {
|
||||
trackDataListener.onSampledOutTrackPoint(location);
|
||||
// Also include the last point if the selected track is not recording.
|
||||
if (includeNextPoint || (localNumLoadedPoints % samplingFrequency == 0)
|
||||
|| (locationId == lastTrackPointId && !isSelectedTrackRecording())) {
|
||||
includeNextPoint = false;
|
||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||
trackDataListener.onSampledInTrackPoint(location);
|
||||
}
|
||||
} else {
|
||||
for (TrackDataListener trackDataListener : sampledOutListeners) {
|
||||
trackDataListener.onSampledOutTrackPoint(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localNumLoadedPoints++;
|
||||
localLastSeenLocationId = locationId;
|
||||
localNumLoadedPoints++;
|
||||
localLastSeenLocationId = locationId;
|
||||
}
|
||||
} finally {
|
||||
if (locationIterator != null) {
|
||||
locationIterator.close();
|
||||
}
|
||||
}
|
||||
iterator.close();
|
||||
|
||||
if (updateSamplingState) {
|
||||
numLoadedPoints = localNumLoadedPoints;
|
||||
|
||||
@@ -453,7 +453,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
if (tripStatisticsUpdater != null) {
|
||||
tripStatisticsUpdater.addLocation(location, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
ActivityType.INVALID, PreferencesUtils.WEIGHT_DEFAULT);
|
||||
TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
if (chartByDistance) {
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
|
||||
+115
-15
@@ -16,12 +16,16 @@
|
||||
|
||||
package com.google.android.apps.mytracks.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.os.Bundle;
|
||||
@@ -30,20 +34,22 @@ import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A DialogFragment to choose an activity type.
|
||||
*
|
||||
*
|
||||
* @author apoorvn
|
||||
*/
|
||||
public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
*
|
||||
* @author apoorvn
|
||||
*/
|
||||
public interface ChooseActivityTypeCaller {
|
||||
@@ -51,12 +57,28 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||
/**
|
||||
* Called when choose activity type is done.
|
||||
*/
|
||||
public void onChooseActivityTypeDone(String iconValue);
|
||||
public void onChooseActivityTypeDone(String iconValue, boolean newWeight);
|
||||
}
|
||||
|
||||
public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
|
||||
|
||||
private static final String KEY_CATEGORY = "category";
|
||||
|
||||
private ChooseActivityTypeCaller caller;
|
||||
private ChooseActivityTypeImageAdapter imageAdapter;
|
||||
private AlertDialog alertDialog;
|
||||
private View weightContainer;
|
||||
private TextView weight;
|
||||
private String weightValue;
|
||||
|
||||
public static ChooseActivityTypeDialogFragment newInstance(String category) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_CATEGORY, category);
|
||||
|
||||
ChooseActivityTypeDialogFragment fragment = new ChooseActivityTypeDialogFragment();
|
||||
fragment.setArguments(bundle);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
@@ -71,12 +93,31 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
GridView gridView =
|
||||
(GridView) getActivity().getLayoutInflater().inflate(R.layout.choose_activity_type, null);
|
||||
|
||||
final List<String> iconValues = TrackIconUtils.getAllIconValues();
|
||||
View view = getActivity().getLayoutInflater().inflate(R.layout.choose_activity_type, null);
|
||||
GridView gridView = (GridView) view.findViewById(R.id.choose_activity_type_grid_view);
|
||||
weightContainer = view.findViewById(R.id.choose_activity_type_weight_container);
|
||||
|
||||
TextView weightLabel = (TextView) view.findViewById(R.id.choose_activity_type_weight_label);
|
||||
weightLabel.setText(
|
||||
PreferencesUtils.isMetricUnits(getActivity()) ? R.string.description_weight_metric
|
||||
: R.string.description_weight_imperial);
|
||||
|
||||
weight = (TextView) view.findViewById(R.id.choose_activity_type_weight);
|
||||
ImageButton imageButton = (ImageButton) view.findViewById(
|
||||
R.id.choose_activity_type_weight_image_button);
|
||||
imageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
double weightDefault = PreferencesUtils.WEIGHT_DEFAULT;
|
||||
if (!PreferencesUtils.isMetricUnits(getActivity())) {
|
||||
weightDefault *= UnitConversions.KG_TO_LB;
|
||||
}
|
||||
weight.setText(StringUtils.formatWeight(weightDefault));
|
||||
}
|
||||
});
|
||||
|
||||
List<Integer> imageIds = new ArrayList<Integer>();
|
||||
for (String iconValue : iconValues) {
|
||||
for (String iconValue : TrackIconUtils.getAllIconValues()) {
|
||||
imageIds.add(TrackIconUtils.getIconDrawable(iconValue));
|
||||
}
|
||||
|
||||
@@ -88,18 +129,77 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||
int height = options.outHeight + 2 * padding;
|
||||
gridView.setColumnWidth(width);
|
||||
|
||||
ChooseActivityTypeImageAdapter imageAdapter =
|
||||
new ChooseActivityTypeImageAdapter(getActivity(), imageIds, width, height, padding);
|
||||
imageAdapter = new ChooseActivityTypeImageAdapter(
|
||||
getActivity(), imageIds, width, height, padding);
|
||||
gridView.setAdapter(imageAdapter);
|
||||
gridView.setOnItemClickListener(new OnItemClickListener() {
|
||||
@Override
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
dismiss();
|
||||
caller.onChooseActivityTypeDone(iconValues.get(position));
|
||||
alertDialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(true);
|
||||
imageAdapter.setSelected(position);
|
||||
imageAdapter.notifyDataSetChanged();
|
||||
updateWeightContainer(position);
|
||||
}
|
||||
});
|
||||
|
||||
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
|
||||
.setTitle(R.string.track_edit_activity_type_hint).setView(gridView).create();
|
||||
alertDialog = new AlertDialog.Builder(getActivity()).setNegativeButton(
|
||||
R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.generic_ok, new Dialog.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
boolean newWeight = false;
|
||||
if (weightContainer.getVisibility() == View.VISIBLE) {
|
||||
String newValue = weight.getText().toString();
|
||||
if (!newValue.equals(weightValue)) {
|
||||
newWeight = true;
|
||||
PreferencesUtils.storeWeightValue(getActivity(), newValue);
|
||||
}
|
||||
}
|
||||
int selected = imageAdapter.getSelected();
|
||||
caller.onChooseActivityTypeDone(
|
||||
TrackIconUtils.getAllIconValues().get(selected), newWeight);
|
||||
}
|
||||
}).setTitle(R.string.track_edit_activity_type_hint).setView(view).create();
|
||||
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
int position = getPosition();
|
||||
alertDialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(position != -1);
|
||||
if (position != -1) {
|
||||
imageAdapter.setSelected(position);
|
||||
imageAdapter.notifyDataSetChanged();
|
||||
}
|
||||
updateWeightContainer(position);
|
||||
double weightDisplayValue = PreferencesUtils.getWeightDisplayValue(getActivity());
|
||||
weightValue = StringUtils.formatWeight(weightDisplayValue);
|
||||
weight.setText(weightValue);
|
||||
}
|
||||
});
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
private int getPosition() {
|
||||
String category = getArguments().getString(KEY_CATEGORY);
|
||||
if (category == null) {
|
||||
return -1;
|
||||
}
|
||||
String iconValue = TrackIconUtils.getIconValue(getActivity(), category);
|
||||
if (iconValue.equals("")) {
|
||||
return -1;
|
||||
}
|
||||
List<String> iconValues = TrackIconUtils.getAllIconValues();
|
||||
for (int i = 0; i < iconValues.size(); i++) {
|
||||
if (iconValues.get(i).equals(iconValue)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void updateWeightContainer(int position) {
|
||||
boolean showWeight = position == 0 || position == 1 || position == 2;
|
||||
weightContainer.setVisibility(showWeight ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -16,7 +16,10 @@
|
||||
|
||||
package com.google.android.apps.mytracks.fragments;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
@@ -36,6 +39,7 @@ public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int padding;
|
||||
private int selected = -1;
|
||||
|
||||
public ChooseActivityTypeImageAdapter(
|
||||
Context context, List<Integer> imageIds, int width, int height, int padding) {
|
||||
@@ -61,6 +65,14 @@ public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setSelected(int position) {
|
||||
selected = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ImageView imageView;
|
||||
@@ -69,6 +81,11 @@ public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
} else {
|
||||
imageView = (ImageView) convertView;
|
||||
}
|
||||
if (position == selected) {
|
||||
imageView.setBackgroundResource(R.drawable.list_selector_background_transition_holo_dark);
|
||||
} else {
|
||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
imageView.setImageResource(imageIds.get(position));
|
||||
imageView.setMinimumHeight(height);
|
||||
imageView.setMinimumWidth(width);
|
||||
|
||||
@@ -22,9 +22,10 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
|
||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.apps.mytracks.util.PhotoUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.StatsUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
@@ -128,6 +129,9 @@ public class MarkerDetailFragment extends Fragment {
|
||||
int visibility = waypointInfo.getVisibility() == View.GONE ? View.VISIBLE : View.GONE;
|
||||
textGradient.setVisibility(visibility);
|
||||
waypointInfo.setVisibility(visibility);
|
||||
if (visibility == View.VISIBLE) {
|
||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
||||
}
|
||||
}
|
||||
});
|
||||
textGradient = (ImageView) view.findViewById(R.id.marker_detail_waypoint_text_gradient);
|
||||
@@ -277,8 +281,12 @@ public class MarkerDetailFragment extends Fragment {
|
||||
TextView name = (TextView) getView().findViewById(R.id.marker_detail_statistics_name);
|
||||
setTextView(name, waypoint.getName());
|
||||
|
||||
StatsUtils.setTripStatisticsValues(getActivity(), null, getView(),
|
||||
waypoint.getTripStatistics(), PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
Track track = myTracksProviderUtils.getTrack(waypoint.getTrackId());
|
||||
ActivityType activityType = track != null ? CalorieUtils.getActivityType(
|
||||
getActivity(), track.getCategory())
|
||||
: ActivityType.INVALID;
|
||||
StatsUtils.setTripStatisticsValues(
|
||||
getActivity(), null, getView(), waypoint.getTripStatistics(), activityType, null);
|
||||
StatsUtils.setLocationValues(getActivity(), null, getView(), waypoint.getLocation(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,13 @@ import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||
import com.google.android.apps.mytracks.content.TrackDataType;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.StatsUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.location.Location;
|
||||
@@ -33,9 +37,12 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
@@ -49,13 +56,15 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
public static final String STATS_FRAGMENT_TAG = "statsFragment";
|
||||
|
||||
private static final int ONE_SECOND = 1000;
|
||||
// 1 second in milliseconds
|
||||
private static final long ONE_SECOND = (long) UnitConversions.S_TO_MS;
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
private Handler handler;
|
||||
|
||||
private Location lastLocation = null;
|
||||
private TripStatistics lastTripStatistics = null;
|
||||
private String category = "";
|
||||
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
|
||||
|
||||
// A runnable to update the total time field.
|
||||
@@ -81,13 +90,34 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
handler = new Handler();
|
||||
updateUi(getActivity());
|
||||
|
||||
Spinner activityTypeIcon = (Spinner) getView().findViewById(R.id.stats_activity_type_icon);
|
||||
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getActivity(), ""));
|
||||
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
((TrackDetailActivity) getActivity()).chooseActivityType(category);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
((TrackDetailActivity) getActivity()).chooseActivityType(category);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
updateUi(getActivity());
|
||||
if (isSelectedTrackRecording()) {
|
||||
handler.post(updateTotalTime);
|
||||
}
|
||||
@@ -106,8 +136,9 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
if (isResumed()) {
|
||||
lastTripStatistics = track != null ? track.getTripStatistics() : null;
|
||||
category = track != null ? track.getCategory() : "";
|
||||
updateUi(getActivity());
|
||||
}
|
||||
}
|
||||
@@ -260,9 +291,10 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
* Updates the UI.
|
||||
*/
|
||||
private void updateUi(FragmentActivity activity) {
|
||||
StatsUtils.setTripStatisticsValues(activity, activity, null, lastTripStatistics,
|
||||
trackDataHub == null ? PreferencesUtils.RECORDING_TRACK_ID_DEFAULT
|
||||
: trackDataHub.getSelectedTrackId());
|
||||
ActivityType activityType = CalorieUtils.getActivityType(activity, category);
|
||||
String trackIconValue = TrackIconUtils.getIconValue(activity, category);
|
||||
StatsUtils.setTripStatisticsValues(
|
||||
activity, activity, null, lastTripStatistics, activityType, trackIconValue);
|
||||
StatsUtils.setLocationValues(
|
||||
activity, activity, null, lastLocation, isSelectedTrackRecording());
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public class PreferenceBackupHelper {
|
||||
doNotBackup.add(context.getString(R.string.google_account_key));
|
||||
doNotBackup.add(context.getString(R.string.recording_track_id_key));
|
||||
doNotBackup.add(context.getString(R.string.recording_track_paused_key));
|
||||
doNotBackup.add(context.getString(R.string.weight_key));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
-6
@@ -134,16 +134,18 @@ public class FileTrackExporter implements TrackExporter {
|
||||
boolean wroteSegment = false;
|
||||
boolean isLastLocationValid = false;
|
||||
TrackWriterLocationFactory locationFactory = new TrackWriterLocationFactory();
|
||||
LocationIterator iterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
track.getId(), -1L, false, locationFactory);
|
||||
int locationNumber = 0;
|
||||
LocationIterator locationIterator = null;
|
||||
|
||||
try {
|
||||
int locationNumber = 0;
|
||||
while (iterator.hasNext()) {
|
||||
locationIterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
track.getId(), -1L, false, locationFactory);
|
||||
|
||||
while (locationIterator.hasNext()) {
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
Location location = iterator.next();
|
||||
Location location = locationIterator.next();
|
||||
|
||||
setLocationTime(location, offset);
|
||||
locationNumber++;
|
||||
@@ -195,7 +197,9 @@ public class FileTrackExporter implements TrackExporter {
|
||||
trackWriter.writeEndTrack(track, null);
|
||||
}
|
||||
} finally {
|
||||
iterator.close();
|
||||
if (locationIterator != null) {
|
||||
locationIterator.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -172,7 +173,8 @@ public class TcxTrackWriter implements TrackWriter {
|
||||
if (printWriter != null) {
|
||||
String startTime = StringUtils.formatDateTimeIso8601(
|
||||
track.getTripStatistics().getStartTime());
|
||||
long totalTimeInSeconds = track.getTripStatistics().getTotalTime() / 1000;
|
||||
long totalTimeInSeconds = (long) (track.getTripStatistics().getTotalTime()
|
||||
* UnitConversions.MS_TO_S);
|
||||
|
||||
printWriter.println("<Activities>");
|
||||
printWriter.println("<Activity Sport=\"" + sportType.getName() + "\">");
|
||||
|
||||
+50
-44
@@ -18,27 +18,30 @@ package com.google.android.apps.mytracks.io.file.importer;
|
||||
|
||||
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingService;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsUpdater;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
@@ -105,6 +108,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
private final long importTrackId;
|
||||
private final MyTracksProviderUtils myTracksProviderUtils;
|
||||
private final int recordingDistanceInterval;
|
||||
private final double weight;
|
||||
private final List<Long> trackIds;
|
||||
private final List<Waypoint> waypoints;
|
||||
|
||||
@@ -142,6 +146,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
this.recordingDistanceInterval = PreferencesUtils.getInt(context,
|
||||
R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
this.weight = PreferencesUtils.getFloat(
|
||||
context, R.string.weight_key, PreferencesUtils.WEIGHT_DEFAULT);
|
||||
trackIds = new ArrayList<Long>();
|
||||
waypoints = new ArrayList<Waypoint>();
|
||||
}
|
||||
@@ -209,20 +215,20 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
if (track == null) {
|
||||
return;
|
||||
}
|
||||
Cursor trackPointCursor = null;
|
||||
|
||||
int waypointPosition = -1;
|
||||
Waypoint waypoint = null;
|
||||
Location location = null;
|
||||
TripStatisticsUpdater trackTripStatisticstrackUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
TripStatisticsUpdater markerTripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
LocationIterator locationIterator = null;
|
||||
ActivityType activityType = CalorieUtils.getActivityType(context, track.getCategory());
|
||||
|
||||
try {
|
||||
trackPointCursor = myTracksProviderUtils.getTrackPointCursor(track.getId(), -1L, -1, false);
|
||||
if (trackPointCursor == null) {
|
||||
return;
|
||||
}
|
||||
int waypointPosition = -1;
|
||||
Waypoint waypoint = null;
|
||||
int trackPointPosition = -1;
|
||||
Location trackPoint = null;
|
||||
TripStatisticsUpdater trackTripStatisticstrackUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
TripStatisticsUpdater markerTripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
locationIterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
track.getId(), -1L, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
|
||||
while (true) {
|
||||
if (waypoint == null) {
|
||||
@@ -233,28 +239,25 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (trackPoint == null) {
|
||||
trackPointPosition++;
|
||||
trackPoint = trackPointCursor.moveToPosition(trackPointPosition) ? myTracksProviderUtils
|
||||
.createTrackPoint(trackPointCursor)
|
||||
: null;
|
||||
if (trackPoint == null) {
|
||||
if (location == null) {
|
||||
if (!locationIterator.hasNext()) {
|
||||
// No more track points. Ignore the rest of the waypoints.
|
||||
return;
|
||||
}
|
||||
trackTripStatisticstrackUpdater.addLocation(trackPoint, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
markerTripStatisticsUpdater.addLocation(trackPoint, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
location = locationIterator.next();
|
||||
trackTripStatisticstrackUpdater.addLocation(location, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.WEIGHT_DEFAULT);
|
||||
markerTripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
}
|
||||
if (waypoint.getLocation().getTime() > trackPoint.getTime()) {
|
||||
trackPoint = null;
|
||||
} else if (waypoint.getLocation().getTime() < trackPoint.getTime()) {
|
||||
if (waypoint.getLocation().getTime() > location.getTime()) {
|
||||
location = null;
|
||||
} else if (waypoint.getLocation().getTime() < location.getTime()) {
|
||||
waypoint = null;
|
||||
} else {
|
||||
// The waypoint location time matches the track point time
|
||||
if (trackPoint.getLatitude() == waypoint.getLocation().getLatitude()
|
||||
&& trackPoint.getLongitude() == waypoint.getLocation().getLongitude()) {
|
||||
if (location.getLatitude() == waypoint.getLocation().getLatitude()
|
||||
&& location.getLongitude() == waypoint.getLocation().getLongitude()) {
|
||||
|
||||
// Get tripStatistics, description, and icon
|
||||
TripStatistics tripStatistics;
|
||||
@@ -262,7 +265,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
String icon;
|
||||
if (waypoint.getType() == WaypointType.STATISTICS) {
|
||||
tripStatistics = markerTripStatisticsUpdater.getTripStatistics();
|
||||
markerTripStatisticsUpdater = new TripStatisticsUpdater(trackPoint.getTime());
|
||||
markerTripStatisticsUpdater = new TripStatisticsUpdater(location.getTime());
|
||||
waypointDescription = new DescriptionGeneratorImpl(context)
|
||||
.generateWaypointDescription(tripStatistics);
|
||||
icon = context.getString(R.string.marker_statistics_icon_url);
|
||||
@@ -279,15 +282,15 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
// Insert waypoint
|
||||
Waypoint newWaypoint = new Waypoint(waypoint.getName(), waypointDescription,
|
||||
waypoint.getCategory(), icon, track.getId(), waypoint.getType(), length, duration,
|
||||
-1L, -1L, trackPoint, tripStatistics, waypoint.getPhotoUrl());
|
||||
-1L, -1L, location, tripStatistics, waypoint.getPhotoUrl());
|
||||
myTracksProviderUtils.insertWaypoint(newWaypoint);
|
||||
}
|
||||
waypoint = null;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (trackPointCursor != null) {
|
||||
trackPointCursor.close();
|
||||
if (locationIterator != null) {
|
||||
locationIterator.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,10 +415,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
* last two points. GPS points tend to have some inherent imprecision,
|
||||
* speed and bearing will likely be off, so the statistics for things
|
||||
* like max speed will also be off.
|
||||
*/
|
||||
float speed = trackData.lastLocationInCurrentSegment.distanceTo(location) * 1000.0f
|
||||
/ timeDifference;
|
||||
location.setSpeed(speed);
|
||||
*/
|
||||
double duration = timeDifference * UnitConversions.MS_TO_S;
|
||||
double speed = trackData.lastLocationInCurrentSegment.distanceTo(location) / duration;
|
||||
location.setSpeed((float) speed);
|
||||
}
|
||||
location.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(location));
|
||||
}
|
||||
@@ -469,8 +472,9 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
if (importTrackId == -1L) {
|
||||
return null;
|
||||
}
|
||||
return FileUtils.getPath(
|
||||
String path = FileUtils.getPath(
|
||||
FileUtils.PICTURES_DIR, Long.toString(importTrackId), fileName);
|
||||
return Uri.fromFile(new File(path)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,8 +490,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
latitudeValue = Double.parseDouble(latitude);
|
||||
longitudeValue = Double.parseDouble(longitude);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SAXException(createErrorMessage(
|
||||
String.format("Unable to parse latitude longitude: %s %s", latitude, longitude)), e);
|
||||
throw new SAXException(createErrorMessage(String.format(
|
||||
Locale.US, "Unable to parse latitude longitude: %s %s", latitude, longitude)), e);
|
||||
}
|
||||
Double altitudeValue = null;
|
||||
if (altitude != null) {
|
||||
@@ -495,7 +499,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
altitudeValue = Double.parseDouble(altitude);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SAXException(
|
||||
createErrorMessage(String.format("Unable to parse altitude: %s", altitude)), e);
|
||||
createErrorMessage(String.format(Locale.US, "Unable to parse altitude: %s", altitude)),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +512,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
timeValue = StringUtils.getTime(time);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new SAXException(
|
||||
createErrorMessage(String.format("Unable to parse time: %s", time)), e);
|
||||
createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", time)), e);
|
||||
}
|
||||
}
|
||||
return createLocation(latitudeValue, longitudeValue, altitudeValue, timeValue);
|
||||
@@ -548,8 +553,9 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
trackData.tripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
location.getTime() != -1L ? location.getTime() : trackData.importTime);
|
||||
}
|
||||
trackData.tripStatisticsUpdater.addLocation(location, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
ActivityType activityType = CalorieUtils.getActivityType(context, category);
|
||||
trackData.tripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
|
||||
trackData.bufferedLocations[trackData.numBufferedLocations] = location;
|
||||
trackData.numBufferedLocations++;
|
||||
|
||||
+1
@@ -156,6 +156,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
name = null;
|
||||
description = null;
|
||||
category = null;
|
||||
photoUrl = null;
|
||||
latitude = attributes.getValue(ATTRIBUTE_LAT);
|
||||
longitude = attributes.getValue(ATTRIBUTE_LON);
|
||||
altitude = null;
|
||||
|
||||
+10
-1
@@ -91,6 +91,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
public void startElement(String uri, String localName, String tag, Attributes attributes)
|
||||
throws SAXException {
|
||||
if (tag.equals(TAG_PLACEMARK) || tag.equals(TAG_PHOTO_OVERLAY)) {
|
||||
/*
|
||||
* Note that a track is contained in a Placemark, calling onWaypointStart
|
||||
* will clear various track variables like name, category, and
|
||||
* description.
|
||||
*/
|
||||
onWaypointStart();
|
||||
} else if (tag.equals(TAG_GX_MULTI_TRACK)) {
|
||||
trackStarted = true;
|
||||
@@ -110,6 +115,10 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
if (tag.equals(TAG_KML)) {
|
||||
onFileEnd();
|
||||
} else if (tag.equals(TAG_PLACEMARK) || tag.equals(TAG_PHOTO_OVERLAY)) {
|
||||
/*
|
||||
* Note that a track is contained in a Placemark, calling onWaypointend is
|
||||
* save since waypointType is not set for a track.
|
||||
*/
|
||||
onWaypointEnd();
|
||||
} else if (localName.equals(TAG_COORDINATES)) {
|
||||
onWaypointLocationEnd();
|
||||
@@ -159,12 +168,12 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
name = null;
|
||||
description = null;
|
||||
category = null;
|
||||
photoUrl = null;
|
||||
latitude = null;
|
||||
longitude = null;
|
||||
altitude = null;
|
||||
time = null;
|
||||
waypointType = null;
|
||||
photoUrl = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-19
@@ -6,6 +6,7 @@ import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGenerator;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
@@ -227,23 +228,19 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
*/
|
||||
private boolean uploadAllTrackPoints(Fusiontables fusiontables, String tableId, Track track)
|
||||
throws IOException {
|
||||
Cursor cursor = null;
|
||||
int numberOfPoints = track.getNumberOfPoints();
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastValidLocation = null;
|
||||
boolean sentStartMarker = false;
|
||||
int readCount = 0;
|
||||
|
||||
LocationIterator locationIterator = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getTrackPointCursor(trackId, -1L, -1, false);
|
||||
if (cursor == null) {
|
||||
Log.d(TAG, "Location cursor is null");
|
||||
return false;
|
||||
}
|
||||
locationIterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
trackId, -1L, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
|
||||
int count = cursor.getCount();
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastValidLocation = null;
|
||||
boolean sentStartMarker = false;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
cursor.moveToPosition(i);
|
||||
|
||||
Location location = myTracksProviderUtils.createTrackPoint(cursor);
|
||||
while (locationIterator.hasNext()) {
|
||||
Location location = locationIterator.next();
|
||||
locations.add(location);
|
||||
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
@@ -258,13 +255,13 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
|
||||
// Upload periodically
|
||||
int readCount = i + 1;
|
||||
readCount++;
|
||||
if (readCount % MAX_POINTS_PER_UPLOAD == 0) {
|
||||
if (!prepareAndUploadPoints(fusiontables, tableId, track, locations, false)) {
|
||||
Log.d(TAG, "Unable to upload points");
|
||||
return false;
|
||||
}
|
||||
updateProgress(readCount, count);
|
||||
updateProgress(readCount, numberOfPoints);
|
||||
locations.clear();
|
||||
}
|
||||
}
|
||||
@@ -285,8 +282,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
if (locationIterator != null) {
|
||||
locationIterator.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGenerator;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
|
||||
@@ -235,33 +236,29 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean uploadAllTrackPoints(Track track) {
|
||||
Cursor cursor = null;
|
||||
int numberOfPoints = track.getNumberOfPoints();
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastValidLocation = null;
|
||||
boolean sentStartMarker = false;
|
||||
|
||||
// For chart server, limit the number of elevation readings to 250.
|
||||
int elevationSamplingFrequency = Math.max(1, (int) (numberOfPoints / 250.0));
|
||||
Vector<Double> distances = new Vector<Double>();
|
||||
Vector<Double> elevations = new Vector<Double>();
|
||||
TripStatisticsUpdater tripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
int recordingDistanceInterval = PreferencesUtils.getInt(context,
|
||||
R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
int readCount = 0;
|
||||
LocationIterator locationIterator = null;
|
||||
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getTrackPointCursor(trackId, -1L, -1, false);
|
||||
if (cursor == null) {
|
||||
Log.d(TAG, "Location cursor is null");
|
||||
return false;
|
||||
}
|
||||
locationIterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
trackId, -1L, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
|
||||
int count = cursor.getCount();
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastValidLocation = null;
|
||||
boolean sentStartMarker = false;
|
||||
|
||||
// For chart server, limit the number of elevation readings to 250.
|
||||
int elevationSamplingFrequency = Math.max(1, (int) (count / 250.0));
|
||||
Vector<Double> distances = new Vector<Double>();
|
||||
Vector<Double> elevations = new Vector<Double>();
|
||||
TripStatisticsUpdater tripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
int recordingDistanceInterval = PreferencesUtils.getInt(context,
|
||||
R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
cursor.moveToPosition(i);
|
||||
|
||||
Location location = myTracksProviderUtils.createTrackPoint(cursor);
|
||||
while (locationIterator.hasNext()) {
|
||||
Location location = locationIterator.next();
|
||||
locations.add(location);
|
||||
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
@@ -279,20 +276,20 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
|
||||
tripStatisticsUpdater.addLocation(location, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
if (i % elevationSamplingFrequency == 0) {
|
||||
ActivityType.INVALID, PreferencesUtils.WEIGHT_DEFAULT);
|
||||
if (readCount % elevationSamplingFrequency == 0) {
|
||||
distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance());
|
||||
elevations.add(tripStatisticsUpdater.getSmoothedElevation());
|
||||
}
|
||||
|
||||
// Upload periodically
|
||||
int readCount = i + 1;
|
||||
readCount++;
|
||||
if (readCount % MAX_POINTS_PER_UPLOAD == 0) {
|
||||
if (!prepareAndUploadPoints(track, locations, false)) {
|
||||
Log.d(TAG, "Unable to upload points");
|
||||
return false;
|
||||
}
|
||||
updateProgress(readCount, count);
|
||||
updateProgress(readCount, numberOfPoints);
|
||||
locations.clear();
|
||||
}
|
||||
}
|
||||
@@ -318,8 +315,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
if (locationIterator != null) {
|
||||
locationIterator.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProvider;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
@@ -44,6 +45,7 @@ import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackNameUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
|
||||
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
|
||||
@@ -59,7 +61,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
@@ -101,9 +102,14 @@ public class TrackRecordingService extends Service {
|
||||
public static final double MAX_NO_MOVEMENT_SPEED = 0.224;
|
||||
|
||||
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
||||
private static final long ONE_SECOND = 1000; // in milliseconds
|
||||
private static final long ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
|
||||
|
||||
// 1 second in milliseconds
|
||||
private static final long ONE_SECOND = (long) UnitConversions.S_TO_MS;
|
||||
|
||||
// 1 minute in milliseconds
|
||||
private static final long ONE_MINUTE = (long) (UnitConversions.MIN_TO_S
|
||||
* UnitConversions.S_TO_MS);
|
||||
|
||||
@VisibleForTesting
|
||||
static final int MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS = 3;
|
||||
|
||||
@@ -126,7 +132,8 @@ public class TrackRecordingService extends Service {
|
||||
private int recordingGpsAccuracy;
|
||||
private int autoResumeTrackTimeout;
|
||||
private long currentRecordingInterval;
|
||||
|
||||
private double weight;
|
||||
|
||||
// The following variables are set when recording:
|
||||
private TripStatisticsUpdater trackTripStatisticsUpdater;
|
||||
private TripStatisticsUpdater markerTripStatisticsUpdater;
|
||||
@@ -224,6 +231,10 @@ public class TrackRecordingService extends Service {
|
||||
R.string.auto_resume_track_timeout_key,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.weight_key))) {
|
||||
weight = PreferencesUtils.getFloat(
|
||||
context, R.string.weight_key, PreferencesUtils.WEIGHT_DEFAULT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -650,31 +661,27 @@ public class TrackRecordingService extends Service {
|
||||
}
|
||||
markerTripStatisticsUpdater = new TripStatisticsUpdater(markerStartTime);
|
||||
|
||||
Cursor cursor = null;
|
||||
ActivityType activityType = CalorieUtils.getActivityType(context, track.getCategory());
|
||||
|
||||
LocationIterator locationIterator = null;
|
||||
try {
|
||||
// TODO: how to handle very long track.
|
||||
cursor = myTracksProviderUtils.getTrackPointCursor(
|
||||
recordingTrackId, -1L, Constants.MAX_LOADED_TRACK_POINTS, true);
|
||||
if (cursor == null) {
|
||||
Log.e(TAG, "Cursor is null.");
|
||||
} else {
|
||||
if (cursor.moveToLast()) {
|
||||
do {
|
||||
Location location = myTracksProviderUtils.createTrackPoint(cursor);
|
||||
trackTripStatisticsUpdater.addLocation(location, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
if (location.getTime() > markerStartTime) {
|
||||
markerTripStatisticsUpdater.addLocation(location, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
}
|
||||
} while (cursor.moveToPrevious());
|
||||
locationIterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
track.getId(), -1L, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
|
||||
while (locationIterator.hasNext()) {
|
||||
Location location = locationIterator.next();
|
||||
trackTripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
if (location.getTime() > markerStartTime) {
|
||||
markerTripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "RuntimeException", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
if (locationIterator != null) {
|
||||
locationIterator.close();
|
||||
}
|
||||
}
|
||||
startRecording(true);
|
||||
@@ -1010,11 +1017,11 @@ public class TrackRecordingService extends Service {
|
||||
try {
|
||||
Uri uri = myTracksProviderUtils.insertTrackPoint(location, track.getId());
|
||||
long trackPointId = Long.parseLong(uri.getLastPathSegment());
|
||||
trackTripStatisticsUpdater.addLocation(location, recordingDistanceInterval, true,
|
||||
CalorieUtils.getActivityType(context, track.getCategory()), PreferencesUtils.getInt(context,
|
||||
R.string.stats_weight_key, PreferencesUtils.STATS_WEIGHT_DEFAULT));
|
||||
markerTripStatisticsUpdater.addLocation(location, recordingDistanceInterval, false,
|
||||
ActivityType.INVALID, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
ActivityType activityType = CalorieUtils.getActivityType(context, track.getCategory());
|
||||
trackTripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
markerTripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
updateRecordingTrack(track, trackPointId, LocationUtils.isValidLocation(location));
|
||||
} catch (SQLiteException e) {
|
||||
/*
|
||||
@@ -1377,23 +1384,14 @@ public class TrackRecordingService extends Service {
|
||||
return;
|
||||
}
|
||||
|
||||
double calorie = CalorieUtils.getTrackCalorie(context, track, -1L);
|
||||
|
||||
// Update to database
|
||||
track.getTripStatistics().setCalorie(calorie);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
|
||||
|
||||
double[] calories = CalorieUtils.updateTrackCalorie(context, track);
|
||||
|
||||
// Update track statistics
|
||||
trackTripStatisticsUpdater.updateCalorie(calorie);
|
||||
trackTripStatisticsUpdater.updateCalorie(calories[0]);
|
||||
|
||||
// Update marker statistics
|
||||
Waypoint wayPoint = myTracksProviderUtils.getLastWaypoint(
|
||||
recordingTrackId, WaypointType.STATISTICS);
|
||||
long trackPointId = wayPoint != null ? myTracksProviderUtils.getTrackPointId(
|
||||
recordingTrackId, wayPoint.getLocation())
|
||||
: -1L;
|
||||
calorie = CalorieUtils.getTrackCalorie(context, track, trackPointId);
|
||||
markerTripStatisticsUpdater.updateCalorie(calorie);
|
||||
// Update marker statistics
|
||||
markerTripStatisticsUpdater.updateCalorie(calories[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+4
-1
@@ -19,6 +19,7 @@ package com.google.android.apps.mytracks.services.sensors;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorState;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
@@ -39,6 +40,8 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class BluetoothSensorManager extends SensorManager {
|
||||
|
||||
// 1 second in milliseconds
|
||||
private static final long ONE_SECOND = (long) UnitConversions.S_TO_MS;
|
||||
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
|
||||
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||
|
||||
@@ -69,7 +72,7 @@ public class BluetoothSensorManager extends SensorManager {
|
||||
while (adapters.isEmpty()) {
|
||||
synchronized (mutex) {
|
||||
try {
|
||||
mutex.wait(1000L);
|
||||
mutex.wait(ONE_SECOND);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Interrupted while waiting for default bluetooth adapter", e);
|
||||
}
|
||||
|
||||
+3
-1
@@ -38,6 +38,7 @@ import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Ant Sensor Manager.
|
||||
@@ -396,7 +397,8 @@ public class AntSensorManager extends SensorManager {
|
||||
if (channelConfig[channel].isInitializing()) {
|
||||
if (message[AntMesg.MESG_DATA_OFFSET + 2] != 0) {
|
||||
// Error response
|
||||
Log.e(TAG, String.format("Error code(%#02x) on message ID(%#02x) on channel %d",
|
||||
Log.e(TAG, String.format(Locale.US,
|
||||
"Error code(%#02x) on message ID(%#02x) on channel %d",
|
||||
message[AntMesg.MESG_DATA_OFFSET + 2], message[AntMesg.MESG_DATA_OFFSET + 1],
|
||||
channel));
|
||||
} else {
|
||||
|
||||
+4
-3
@@ -50,8 +50,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
static final float TTS_SPEECH_RATE = 0.9f;
|
||||
|
||||
private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName();
|
||||
private static final long HOUR_TO_MILLISECOND = 60 * 60 * 1000;
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
static final HashMap<String, String> SPEECH_PARAMS = new HashMap<String, String>();
|
||||
static {
|
||||
@@ -249,7 +248,9 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
} else {
|
||||
speed = speed == 0 ? 0.0 : 1 / speed;
|
||||
int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
|
||||
rate = context.getString(paceId, getAnnounceTime((long) (speed * HOUR_TO_MILLISECOND)));
|
||||
long time = (long) (speed * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S
|
||||
* UnitConversions.S_TO_MS);
|
||||
rate = context.getString(paceId, getAnnounceTime(time));
|
||||
}
|
||||
|
||||
int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers
|
||||
|
||||
+12
-169
@@ -17,22 +17,12 @@
|
||||
package com.google.android.apps.mytracks.settings;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.widget.EditText;
|
||||
|
||||
/**
|
||||
* An activity for accessing stats settings.
|
||||
@@ -41,25 +31,12 @@ import android.widget.EditText;
|
||||
*/
|
||||
public class StatsSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
private static final String TAG = MapSettingsActivity.class.getSimpleName();
|
||||
|
||||
private CheckBoxPreference caloriePreference;
|
||||
private EditTextPreference weightPreference;
|
||||
|
||||
private static final int WEIGHT_INPUT_DIALOG = 1;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.stats_settings);
|
||||
|
||||
caloriePreference = (CheckBoxPreference) findPreference(getString(R.string.stats_show_calorie_key));
|
||||
weightPreference = (EditTextPreference) findPreference(getString(R.string.stats_weight_key));
|
||||
|
||||
configCaloriePreference();
|
||||
configWeightPreference();
|
||||
updateWeightSummary();
|
||||
/*
|
||||
* Note configureUnitsListPreference will trigger
|
||||
* configureRateListPreference
|
||||
@@ -72,27 +49,19 @@ public class StatsSettingsActivity extends AbstractSettingsActivity {
|
||||
*/
|
||||
private void configUnitsListPreference() {
|
||||
@SuppressWarnings("deprecation")
|
||||
ListPreference listPreference = (ListPreference) findPreference(getString(R.string.stats_units_key));
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_units_key));
|
||||
OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
configRateListPreference(PreferencesUtils.STATS_UNITS_DEFAULT.equals((String) newValue));
|
||||
|
||||
/*
|
||||
* It is necessary to change the weight summary as they are in the same
|
||||
* activity.
|
||||
*/
|
||||
updateWeightSummary(PreferencesUtils.STATS_UNITS_DEFAULT.equals((String) newValue));
|
||||
// Recreate weight value dialog.
|
||||
removeDialog(WEIGHT_INPUT_DIALOG);
|
||||
onCreateDialog(WEIGHT_INPUT_DIALOG);
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
boolean metricUnits = PreferencesUtils.STATS_UNITS_DEFAULT.equals((String) newValue);
|
||||
configRateListPreference(metricUnits);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
String value = PreferencesUtils.getString(this, R.string.stats_units_key,
|
||||
PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_units_values);
|
||||
String[] options = getResources().getStringArray(R.array.stats_units_options);
|
||||
configureListPreference(listPreference, options, options, values, value, listener);
|
||||
@@ -105,139 +74,13 @@ public class StatsSettingsActivity extends AbstractSettingsActivity {
|
||||
*/
|
||||
private void configRateListPreference(boolean metricUnits) {
|
||||
@SuppressWarnings("deprecation")
|
||||
ListPreference listPreference = (ListPreference) findPreference(getString(R.string.stats_rate_key));
|
||||
String value = PreferencesUtils.getString(this, R.string.stats_rate_key,
|
||||
PreferencesUtils.STATS_RATE_DEFAULT);
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_rate_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_rate_key, PreferencesUtils.STATS_RATE_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_rate_values);
|
||||
String[] options = getResources().getStringArray(
|
||||
metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
|
||||
configureListPreference(listPreference, options, options, values, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the calorie preference.
|
||||
*/
|
||||
private void configCaloriePreference() {
|
||||
caloriePreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
boolean value = (Boolean) newValue;
|
||||
if (value) {
|
||||
showDialog(WEIGHT_INPUT_DIALOG);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
Dialog dialog = null;
|
||||
switch (id) {
|
||||
case WEIGHT_INPUT_DIALOG:
|
||||
final EditText weightInput = new EditText(this);
|
||||
weightInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||
weightInput.setText(Integer.toString(getWeightDisplayValue(PreferencesUtils
|
||||
.isMetricUnits(getApplicationContext()))));
|
||||
weightInput.setSelectAllOnFocus(true);
|
||||
dialog = (new AlertDialog.Builder(this))
|
||||
.setMessage(R.string.settings_stats_calorie_weight_description)
|
||||
.setNegativeButton(R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.generic_ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialogInterface, int number) {
|
||||
storeWeightValue(weightInput.getText().toString());
|
||||
updateWeightSummary();
|
||||
dialogInterface.cancel();
|
||||
}
|
||||
}).setTitle(R.string.settings_stats_calorie_weight).setView(weightInput).create();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the weight preference.
|
||||
*/
|
||||
private void configWeightPreference() {
|
||||
weightPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
storeWeightValue((String) newValue);
|
||||
updateWeightSummary();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
weightPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference pref) {
|
||||
int value = getWeightDisplayValue(PreferencesUtils.isMetricUnits(getApplicationContext()));
|
||||
((EditTextPreference) pref).getEditText().setText(String.valueOf(value));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the weight summary.
|
||||
*/
|
||||
private void updateWeightSummary() {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(this);
|
||||
updateWeightSummary(metricUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the weight summary.
|
||||
*
|
||||
* @param metricUnits true if metric units
|
||||
*/
|
||||
private void updateWeightSummary(boolean metricUnits) {
|
||||
int displayValue = getWeightDisplayValue(metricUnits);
|
||||
weightPreference.setSummary(getString(metricUnits ? R.string.value_integer_kilogram
|
||||
: R.string.value_integer_pound, displayValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight display value in metric or imperial depending on the
|
||||
* preferred units.
|
||||
*
|
||||
* @param metricUnits true if metric units
|
||||
*/
|
||||
private int getWeightDisplayValue(boolean metricUnits) {
|
||||
int value = PreferencesUtils.getInt(this, R.string.stats_weight_key,
|
||||
PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
if (!metricUnits) {
|
||||
value = (int) Math.round(value * UnitConversions.KG_TO_LB);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the weight value, always in metric units.
|
||||
*
|
||||
* @param displayValue the display value
|
||||
*/
|
||||
private void storeWeightValue(String displayValue) {
|
||||
/*
|
||||
* TODO add a method to an abstract class or an utility class to avoid
|
||||
* duplicating store preference logic in MapSettingsActivity.java.
|
||||
*/
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(displayValue);
|
||||
if (!PreferencesUtils.isMetricUnits(this)) {
|
||||
value = (int) (value * UnitConversions.LB_TO_KG);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "invalid value " + displayValue);
|
||||
value = PreferencesUtils.STATS_WEIGHT_DEFAULT;
|
||||
}
|
||||
|
||||
PreferencesUtils.setInt(this, R.string.stats_weight_key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ public class TripStatisticsUpdater {
|
||||
* calorie
|
||||
*/
|
||||
public void addLocation(Location location, int minRecordingDistance,
|
||||
boolean calculateCalorie, ActivityType activityType, int weight) {
|
||||
boolean calculateCalorie, ActivityType activityType, double weight) {
|
||||
// Always update time
|
||||
updateTime(location.getTime());
|
||||
if (!LocationUtils.isValidLocation(location)) {
|
||||
|
||||
@@ -18,11 +18,13 @@ package com.google.android.apps.mytracks.util;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsUpdater;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
|
||||
/**
|
||||
@@ -36,7 +38,7 @@ public class CalorieUtils {
|
||||
* Activity types.
|
||||
*/
|
||||
public enum ActivityType {
|
||||
CYCLING, FOOT, INVALID
|
||||
CYCLING, RUNNING, WALKING, INVALID
|
||||
}
|
||||
|
||||
private CalorieUtils() {}
|
||||
@@ -47,15 +49,10 @@ public class CalorieUtils {
|
||||
*/
|
||||
private static final double RESTING_VO2 = 3.5;
|
||||
|
||||
/**
|
||||
* Ratio to convert liter to kcal.
|
||||
*/
|
||||
private static final double L_TO_KCAL = 5.0;
|
||||
|
||||
/**
|
||||
* Standard gravity in meters per second squared (m/s^2).
|
||||
*/
|
||||
private static final double EARTH_GRAVITY = 9.80665;
|
||||
private static final double EARTH_GRAVITY = 9.81;
|
||||
|
||||
/**
|
||||
* Lumped constant for all frictional losses (tires, bearings, chain).
|
||||
@@ -63,22 +60,10 @@ public class CalorieUtils {
|
||||
private static final double K1 = 0.0053;
|
||||
|
||||
/**
|
||||
* Lumped constant for aerodynamic drag.
|
||||
* Lumped constant for aerodynamic drag (kg/m)
|
||||
*/
|
||||
private static final double K2 = 0.185;
|
||||
|
||||
/**
|
||||
* Critical speed running in meters per minute. Converts 4.5 miles per hour to
|
||||
* meters per minute. 4 miles per hour is regarded as the max speed of walking
|
||||
* and 5 miles per hour is regarded as the minimal speed of running, so we use
|
||||
* 4.5 miles per hour as the critical speed. <a href=
|
||||
* "http://www.ideafit.com/fitness-library/calculating-caloric-expenditure-0"
|
||||
* >Reference</a>
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final double CRTICAL_SPEED_RUNNING = 4.5 * UnitConversions.MI_TO_KM
|
||||
* UnitConversions.KM_TO_M / UnitConversions.HR_TO_MIN;
|
||||
|
||||
/**
|
||||
* Gets the activity type.
|
||||
*
|
||||
@@ -86,9 +71,13 @@ public class CalorieUtils {
|
||||
* @param activityType the activity type
|
||||
*/
|
||||
public static ActivityType getActivityType(Context context, String activityType) {
|
||||
if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.WALK)
|
||||
|| TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.RUN)) {
|
||||
return ActivityType.FOOT;
|
||||
if (activityType == null || activityType.equals("")) {
|
||||
return ActivityType.INVALID;
|
||||
}
|
||||
if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.WALK)) {
|
||||
return ActivityType.WALKING;
|
||||
} else if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.RUN)) {
|
||||
return ActivityType.RUNNING;
|
||||
} else if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.BIKE)) {
|
||||
return ActivityType.CYCLING;
|
||||
}
|
||||
@@ -96,34 +85,79 @@ public class CalorieUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the track calorie in kcal.
|
||||
* Updates calories for a track and its waypoints.
|
||||
*
|
||||
* @param context the context
|
||||
* @param track the track
|
||||
* @param startTrackPointId the starting track point id. -1L to calculate the
|
||||
* entire track
|
||||
* @return an array of two doubles, first is the track calorie, second is the
|
||||
* last statistics waypoint calorie
|
||||
*/
|
||||
public static double getTrackCalorie(Context context, Track track, long startTrackPointId) {
|
||||
public static double[] updateTrackCalorie(Context context, Track track) {
|
||||
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
ActivityType activityType = getActivityType(context, track.getCategory());
|
||||
|
||||
if (activityType == ActivityType.INVALID) {
|
||||
return 0.0;
|
||||
clearCalorie(myTracksProviderUtils, track);
|
||||
return new double[] { 0.0, 0.0 };
|
||||
}
|
||||
|
||||
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
LocationIterator iterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
track.getId(), startTrackPointId, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
TripStatisticsUpdater tripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
TripStatisticsUpdater trackTripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
TripStatisticsUpdater markerTripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
int recordingDistanceInterval = PreferencesUtils.getInt(context,
|
||||
R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
int statsWeight = PreferencesUtils.getInt(
|
||||
context, R.string.stats_weight_key, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
while (iterator.hasNext()) {
|
||||
tripStatisticsUpdater.addLocation(
|
||||
iterator.next(), recordingDistanceInterval, true, activityType, statsWeight);
|
||||
double weight = PreferencesUtils.getFloat(
|
||||
context, R.string.weight_key, PreferencesUtils.WEIGHT_DEFAULT);
|
||||
LocationIterator locationIterator = null;
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
Waypoint waypoint = null;
|
||||
|
||||
locationIterator = myTracksProviderUtils.getTrackPointLocationIterator(
|
||||
track.getId(), -1L, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
cursor = myTracksProviderUtils.getWaypointCursor(track.getId(), -1L, -1);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
/*
|
||||
* Yes, this will skip the first waypoint and that is intentional as the
|
||||
* first waypoint holds the stats for the track.
|
||||
*/
|
||||
waypoint = getNextStatisticsWaypoint(myTracksProviderUtils, cursor);
|
||||
}
|
||||
|
||||
while (locationIterator.hasNext()) {
|
||||
Location location = locationIterator.next();
|
||||
trackTripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
markerTripStatisticsUpdater.addLocation(
|
||||
location, recordingDistanceInterval, true, activityType, weight);
|
||||
|
||||
if (waypoint != null && waypoint.getLocation().getTime() == location.getTime()
|
||||
&& waypoint.getLocation().getLatitude() == location.getLatitude()
|
||||
&& waypoint.getLocation().getLongitude() == location.getLongitude()) {
|
||||
waypoint.getTripStatistics()
|
||||
.setCalorie(markerTripStatisticsUpdater.getTripStatistics().getCalorie());
|
||||
myTracksProviderUtils.updateWaypoint(waypoint);
|
||||
markerTripStatisticsUpdater = new TripStatisticsUpdater(location.getTime());
|
||||
waypoint = getNextStatisticsWaypoint(myTracksProviderUtils, cursor);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (locationIterator != null) {
|
||||
locationIterator.close();
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return tripStatisticsUpdater.getTripStatistics().getCalorie();
|
||||
double trackCalorie = trackTripStatisticsUpdater.getTripStatistics().getCalorie();
|
||||
track.getTripStatistics().setCalorie(trackCalorie);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
return new double[] {
|
||||
trackCalorie, markerTripStatisticsUpdater.getTripStatistics().getCalorie() };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,141 +171,137 @@ public class CalorieUtils {
|
||||
* @param activityType the activity type
|
||||
*/
|
||||
public static double getCalorie(
|
||||
Location start, Location stop, double grade, int weight, ActivityType activityType) {
|
||||
Location start, Location stop, double grade, double weight, ActivityType activityType) {
|
||||
if (activityType == ActivityType.INVALID) {
|
||||
return 0.0;
|
||||
}
|
||||
return ActivityType.CYCLING == activityType ? getCyclingCalorie(start, stop, grade, weight)
|
||||
: getFootCalorie(start, stop, grade, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cycling calorie in kcal between two locations.
|
||||
*
|
||||
* @param start the start location
|
||||
* @param stop the stop location
|
||||
* @param grade the grade
|
||||
* @param weight the weight in kilogram of the rider plus bike
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getCyclingCalorie(Location start, Location stop, double grade, int weight) {
|
||||
// Gets duration in seconds
|
||||
double duration = (double) (stop.getTime() - start.getTime()) * UnitConversions.MS_TO_S;
|
||||
// Get speed in meters per second
|
||||
double speed = (start.getSpeed() + stop.getSpeed()) / 2.0;
|
||||
|
||||
if (grade < 0) {
|
||||
grade = 0.0;
|
||||
}
|
||||
return getCyclingCalorie(speed, grade, weight, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cycling calorie in kcal using the following equation: <br>
|
||||
* P = g * m * Vg * (K1 + s) + K2 * (Va)^2 * Vg
|
||||
* <ul>
|
||||
* <li>P - Power in watt (Joule/second)</li>
|
||||
* <li>g - gravity, using 9.80665 meter/second^2</li>
|
||||
* <li>m - mass of the rider plus bike in kilogram</li>
|
||||
* <li>s - surface grade</li>
|
||||
* <li>Vg - ground relative speed in meter/second</li>
|
||||
* <li>Va - air relative speed. We assume the air speed is zero, so Va is
|
||||
* equal to Vg.</li>
|
||||
* <li>K1 - a constant which represents frictional losses, using 0.0053.</li>
|
||||
* <li>K2 - a constant representing aerodynamic drag, using 0.185.</li>
|
||||
* <li></li>
|
||||
* </ul>
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param grade the grade
|
||||
* @param weight weight in kilogram of the rider plus bike
|
||||
* @param duration the duration in seconds
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getCyclingCalorie(double speed, double grade, int weight, double duration) {
|
||||
// Get the power in watt (Joule/second)
|
||||
double power = EARTH_GRAVITY * weight * speed * (K1 + grade) + K2 * (speed * speed * speed);
|
||||
|
||||
// Get the calories in kcal
|
||||
return power * duration * UnitConversions.J_TO_KCAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the foot calorie in kcal between two locations.
|
||||
*
|
||||
* @param start the start location
|
||||
* @param stop the stop location
|
||||
* @param grade the grade
|
||||
* @param weight the weight of the user in kilogram
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getFootCalorie(Location start, Location stop, double grade, int weight) {
|
||||
// Get speed in meters per second
|
||||
double averageSpeed = (start.getSpeed() + stop.getSpeed()) / 2.0;
|
||||
// Get VO2 in mL/kg/min
|
||||
double vo2 = getFootVo2(averageSpeed, grade);
|
||||
// Duration in minutes
|
||||
// Speed in m/s
|
||||
double speed = (start.getSpeed() + stop.getSpeed()) / 2.0;
|
||||
// Duration in min
|
||||
double duration = (double) (stop.getTime() - start.getTime()) * UnitConversions.MS_TO_S
|
||||
* UnitConversions.S_TO_MIN;
|
||||
/*
|
||||
* Get the calorie. The unit of calorie is kcal which is came from
|
||||
* (mL/kg/min * min * kg * L/mL * kcal/L)
|
||||
*/
|
||||
return vo2 * duration * weight * UnitConversions.ML_TO_L * L_TO_KCAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the foot VO2 value in ml/kg/min.
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param grade the grade
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getFootVo2(double speed, double grade) {
|
||||
if (grade < 0) {
|
||||
grade = 0.0;
|
||||
if (activityType == ActivityType.CYCLING) {
|
||||
/*
|
||||
* Power in watt (Joule/second). See
|
||||
* http://en.wikipedia.org/wiki/Bicycle_performance.
|
||||
*/
|
||||
double power = EARTH_GRAVITY * weight * speed * (K1 + grade) + K2 * (speed * speed * speed);
|
||||
|
||||
// WorkRate in kgm/min
|
||||
double workRate = power * UnitConversions.W_TO_KGM;
|
||||
|
||||
/*
|
||||
* VO2 in kgm/min/kg 1.8 = oxygen cost of producing 1 kgm/min of power
|
||||
* output. 7 = oxygen cost of unloaded cycling plus resting oxygen
|
||||
* consumption
|
||||
*/
|
||||
double vo2 = (1.8 * workRate / weight) + 7;
|
||||
|
||||
// Calorie in kcal
|
||||
return vo2 * duration * weight * UnitConversions.KGM_TO_KCAL;
|
||||
} else {
|
||||
double vo2 = activityType == ActivityType.RUNNING ? getRunningVo2(speed, grade)
|
||||
: getWalkingVo2(speed, grade);
|
||||
|
||||
/*
|
||||
* Calorie in kcal (mL/kg/min * min * kg * L/mL * kcal/L)
|
||||
*/
|
||||
return vo2 * duration * weight * UnitConversions.ML_TO_L * UnitConversions.L_TO_KCAL;
|
||||
}
|
||||
return speed > CRTICAL_SPEED_RUNNING ? getHighSpeedFootVo2(speed, grade)
|
||||
: getLowSpeedFootVo2(speed, grade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the high speed foot VO2 in ml/kg/min. This equation is appropriate for
|
||||
* speeds greater than 4.5 mph like running.
|
||||
* Clears calorie in the track and its waypoints.
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param grade the grade
|
||||
* @param myTracksProviderUtils the my tracks provider utils
|
||||
* @param track the track
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getHighSpeedFootVo2(double speed, double grade) {
|
||||
// Change from meters per second to meters per minute
|
||||
speed = speed / UnitConversions.S_TO_MIN;
|
||||
|
||||
/*
|
||||
* 0.2 means oxygen cost per meter of moving each kilogram (kg) of body
|
||||
* weight while running (horizontally). 0.9 means oxygen cost per meter of
|
||||
* moving total body mass against gravity (vertically).
|
||||
*/
|
||||
return 0.2 * speed + 0.9 * speed * Math.abs(grade) + RESTING_VO2;
|
||||
private static void clearCalorie(MyTracksProviderUtils myTracksProviderUtils, Track track) {
|
||||
track.getTripStatistics().setCalorie(0);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getWaypointCursor(track.getId(), -1L, -1);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
/*
|
||||
* Yes, this will skip the first waypoint and that is intentional as the
|
||||
* first waypoint holds the stats for the track.
|
||||
*/
|
||||
while (cursor.moveToNext()) {
|
||||
Waypoint waypoint = myTracksProviderUtils.createWaypoint(cursor);
|
||||
waypoint.getTripStatistics().setCalorie(0);
|
||||
myTracksProviderUtils.updateWaypoint(waypoint);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the low speed foot VO2 in ml/kg/min. This equation is appropriate for
|
||||
* speed less than 4.5 mph like walking.
|
||||
* Gets the next statistics waypoint from a cursor.
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param myTracksProviderUtils the my tracks provider utils
|
||||
* @param cursor the cursor
|
||||
*/
|
||||
private static Waypoint getNextStatisticsWaypoint(
|
||||
MyTracksProviderUtils myTracksProviderUtils, Cursor cursor) {
|
||||
if (cursor == null) {
|
||||
return null;
|
||||
}
|
||||
while (cursor.moveToNext()) {
|
||||
Waypoint waypoint = myTracksProviderUtils.createWaypoint(cursor);
|
||||
if (waypoint.getType() == WaypointType.STATISTICS) {
|
||||
return waypoint;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the running VO2 in ml/kg/min. This equation is appropriate for speeds
|
||||
* greater than 5 mi/hr (or 3 mi/hr or greater if the subject is truly
|
||||
* jogging).
|
||||
*
|
||||
* @param speed the speed in m/s
|
||||
* @param grade the grade
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getLowSpeedFootVo2(double speed, double grade) {
|
||||
// Change from meters per second to meters per minute
|
||||
private static double getRunningVo2(double speed, double grade) {
|
||||
// Change from m/s to m/min
|
||||
speed = speed / UnitConversions.S_TO_MIN;
|
||||
|
||||
/*
|
||||
* 0.1 means oxygen cost per meter of moving each kilogram (kg) of body
|
||||
* weight while walking (horizontally). 1.8 means oxygen cost per meter of
|
||||
* moving total body mass against gravity (vertically).
|
||||
* 0.2 = oxygen cost per meter of moving each kg of body weight while
|
||||
* running (horizontally). 0.9 = oxygen cost per meter of moving total body
|
||||
* mass against gravity (vertically).
|
||||
*/
|
||||
return 0.1 * speed + 1.8 * speed * Math.abs(grade) + RESTING_VO2;
|
||||
return 0.2 * speed + 0.9 * speed * grade + RESTING_VO2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the walking VO2 in ml/kg/min. This equation is appropriate for speed
|
||||
* from 1.9 to 4 mi/hr.
|
||||
*
|
||||
* @param speed the speed in m/s
|
||||
* @param grade the grade
|
||||
*/
|
||||
private static double getWalkingVo2(double speed, double grade) {
|
||||
// Change from m/s to m/min
|
||||
speed = speed / UnitConversions.S_TO_MIN;
|
||||
|
||||
/*
|
||||
* 0.1 = oxygen cost per meter of moving each kilogram (kg) of body weight
|
||||
* while walking (horizontally). 1.8 = oxygen cost per meter of moving total
|
||||
* body mass against gravity (vertically).
|
||||
*/
|
||||
return 0.1 * speed + 1.8 * speed * grade + RESTING_VO2;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,9 @@ public class LocationUtils {
|
||||
|
||||
private static final String TAG = LocationUtils.class.getSimpleName();
|
||||
|
||||
private static final long MAX_LOCATION_AGE_MS = 60 * 1000; // 1 minute
|
||||
// 1 minute in milliseconds
|
||||
private static final long MAX_LOCATION_AGE_MS = (long) (UnitConversions.MIN_TO_S
|
||||
* UnitConversions.S_TO_MS);
|
||||
|
||||
private LocationUtils() {}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Utilities to access preferences stored in {@link SharedPreferences}.
|
||||
@@ -106,8 +107,6 @@ public class PreferencesUtils {
|
||||
public static final boolean STATS_SHOW_COORDINATE_DEFAULT = false;
|
||||
public static final boolean STATS_SHOW_GRADE_ELEVATION_DEFAULT = false;
|
||||
public static final String STATS_UNITS_DEFAULT = "METRIC";
|
||||
public static final boolean STATS_SHOW_CALORIE_DEFAULT = false;
|
||||
public static final int STATS_WEIGHT_DEFAULT = 65; // in kilogram
|
||||
|
||||
// Track color
|
||||
public static final String TRACK_COLOR_MODE_DEFAULT = "SINGLE";
|
||||
@@ -124,6 +123,10 @@ public class PreferencesUtils {
|
||||
public static final int TRACK_WIDGET_ITEM4_DEFAULT = 2; // average speed
|
||||
public static final int VOICE_FREQUENCY_DEFAULT = 0;
|
||||
|
||||
public static final float WEIGHT_DEFAULT = 65.0f; // in kilogram
|
||||
|
||||
private static final String TAG = PreferencesUtils.class.getSimpleName();
|
||||
|
||||
private PreferencesUtils() {}
|
||||
|
||||
/**
|
||||
@@ -194,6 +197,35 @@ public class PreferencesUtils {
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a float preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
public static float getFloat(Context context, int keyId, float defaultValue) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
return sharedPreferences.getFloat(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a float preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
@SuppressLint("CommitPrefEdits")
|
||||
public static void setFloat(Context context, int keyId, float value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putFloat(getKey(context, keyId), value);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a long preference value.
|
||||
*
|
||||
@@ -304,4 +336,37 @@ public class PreferencesUtils {
|
||||
}
|
||||
setString(context, keyId, list + ";" + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the weight value, always in metric units.
|
||||
*
|
||||
* @param displayValue the display value
|
||||
*/
|
||||
public static void storeWeightValue(Context context, String displayValue) {
|
||||
double value;
|
||||
try {
|
||||
value = Double.parseDouble(displayValue);
|
||||
if (!PreferencesUtils.isMetricUnits(context)) {
|
||||
value = value * UnitConversions.LB_TO_KG;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "invalid value " + displayValue);
|
||||
value = PreferencesUtils.WEIGHT_DEFAULT;
|
||||
}
|
||||
PreferencesUtils.setFloat(context, R.string.weight_key, (float) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight display value.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static double getWeightDisplayValue(Context context) {
|
||||
double value = PreferencesUtils.getFloat(
|
||||
context, R.string.weight_key, PreferencesUtils.WEIGHT_DEFAULT);
|
||||
if (!PreferencesUtils.isMetricUnits(context)) {
|
||||
value = value * UnitConversions.KG_TO_LB;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
@@ -26,6 +24,7 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.view.View;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Locale;
|
||||
@@ -40,9 +39,8 @@ public class StatsUtils {
|
||||
private static final String COORDINATE_DEGREE = "\u00B0";
|
||||
private static final String GRADE_PERCENTAGE = "%";
|
||||
|
||||
private static final String ELEVATION_FORMAT = "%1$.2f";
|
||||
private static final String GRADE_FORMAT = "%1$d";
|
||||
private static final String CALORIES_FORMAT = "%1$.0f";
|
||||
private static final String CALORIES_FORMAT = "%1$,.0f";
|
||||
|
||||
private StatsUtils() {}
|
||||
|
||||
@@ -63,44 +61,48 @@ public class StatsUtils {
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(context);
|
||||
|
||||
// Set speed/pace
|
||||
double speed = isRecording && location != null && location.hasSpeed() ? location.getSpeed()
|
||||
: Double.NaN;
|
||||
setSpeed(context, getView(activity, view, R.id.stats_speed), R.string.stats_speed,
|
||||
R.string.stats_pace, speed, metricUnits, reportSpeed);
|
||||
View speed = getView(activity, view, R.id.stats_speed);
|
||||
speed.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
if (isRecording) {
|
||||
double value = isRecording && location != null && location.hasSpeed() ? location.getSpeed()
|
||||
: Double.NaN;
|
||||
setSpeed(context, speed, R.string.stats_speed, R.string.stats_pace, value, metricUnits,
|
||||
reportSpeed);
|
||||
}
|
||||
|
||||
// Set elevation
|
||||
boolean showGradeElevation = PreferencesUtils.getBoolean(context,
|
||||
R.string.stats_show_grade_elevation_key,
|
||||
PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT);
|
||||
boolean showGradeElevation = PreferencesUtils.getBoolean(
|
||||
context, R.string.stats_show_grade_elevation_key,
|
||||
PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT) && isRecording;
|
||||
View elevation = getView(activity, view, R.id.stats_elevation);
|
||||
elevation.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showGradeElevation && isRecording) {
|
||||
if (showGradeElevation) {
|
||||
double altitude = location != null && location.hasAltitude() ? location.getAltitude()
|
||||
: Double.NaN;
|
||||
elevation.setVisibility(View.VISIBLE);
|
||||
setElevationValue(context, elevation, -1, altitude, metricUnits);
|
||||
} else {
|
||||
elevation.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Set coordinate
|
||||
boolean showCoordinate = PreferencesUtils.getBoolean(context,
|
||||
R.string.stats_show_coordinate_key, PreferencesUtils.STATS_SHOW_COORDINATE_DEFAULT);
|
||||
View coordinateHorizontalLine = getView(activity, view, R.id.stats_coordinate_horizontal_line);
|
||||
boolean showCoordinate = PreferencesUtils.getBoolean(
|
||||
context, R.string.stats_show_coordinate_key, PreferencesUtils.STATS_SHOW_COORDINATE_DEFAULT)
|
||||
&& isRecording;
|
||||
View coordinateSeparator = getView(activity, view, R.id.stats_coordinate_separator);
|
||||
View coordinateContainer = getView(activity, view, R.id.stats_coordinate_container);
|
||||
|
||||
if (showCoordinate && isRecording) {
|
||||
if (coordinateSeparator != null) {
|
||||
coordinateSeparator.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
coordinateContainer.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showCoordinate) {
|
||||
double latitude = location != null ? location.getLatitude() : Double.NaN;
|
||||
double longitude = location != null ? location.getLongitude() : Double.NaN;
|
||||
coordinateHorizontalLine.setVisibility(View.VISIBLE);
|
||||
coordinateContainer.setVisibility(View.VISIBLE);
|
||||
setCoordinateValue(
|
||||
context, getView(activity, view, R.id.stats_latitude), R.string.stats_latitude, latitude);
|
||||
setCoordinateValue(context, getView(activity, view, R.id.stats_longitude),
|
||||
R.string.stats_longitude, longitude);
|
||||
} else {
|
||||
coordinateHorizontalLine.setVisibility(View.GONE);
|
||||
coordinateContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +126,12 @@ public class StatsUtils {
|
||||
* @param view the containing view for finding views. If null, the activity
|
||||
* cannot be null
|
||||
* @param tripStatistics the trip statistics
|
||||
* @param trackId the id of track, which is used to set calorie value. Does
|
||||
* not handle the calorie If the value is
|
||||
* {@link PreferencesUtils#RECORDING_TRACK_ID_DEFAULT}
|
||||
* @param activityType the activity type
|
||||
* @param trackIconValue the track icon value or null to hide the track icon
|
||||
* spinner
|
||||
*/
|
||||
public static void setTripStatisticsValues(
|
||||
Context context, Activity activity, View view, TripStatistics tripStatistics, long trackId) {
|
||||
public static void setTripStatisticsValues(Context context, Activity activity, View view,
|
||||
TripStatistics tripStatistics, ActivityType activityType, String trackIconValue) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(context);
|
||||
|
||||
@@ -138,43 +140,54 @@ public class StatsUtils {
|
||||
setDistanceValue(
|
||||
context, getView(activity, view, R.id.stats_distance), totalDistance, metricUnits);
|
||||
|
||||
// Set calorie
|
||||
double calorie = tripStatistics == null || activityType == ActivityType.INVALID ? Double.NaN
|
||||
: tripStatistics.getCalorie();
|
||||
setCalorie(context, getView(activity, view, R.id.stats_calorie), calorie);
|
||||
|
||||
Spinner spinner = (Spinner) getView(activity, view, R.id.stats_activity_type_icon);
|
||||
spinner.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
|
||||
if (trackIconValue != null) {
|
||||
TrackIconUtils.setIconSpinner(spinner, trackIconValue);
|
||||
}
|
||||
|
||||
// Set total time
|
||||
setTimeValue(context, getView(activity, view, R.id.stats_total_time), R.string.stats_total_time,
|
||||
tripStatistics != null ? tripStatistics.getTotalTime() : -1L);
|
||||
|
||||
// Set moving time
|
||||
setTimeValue(context, getView(activity, view, R.id.stats_moving_time),
|
||||
R.string.stats_moving_time, tripStatistics != null ? tripStatistics.getMovingTime() : -1L);
|
||||
|
||||
// Set average speed/pace
|
||||
double averageSpeed = tripStatistics != null ? tripStatistics.getAverageSpeed() : Double.NaN;
|
||||
setSpeed(context, getView(activity, view, R.id.stats_average_speed),
|
||||
R.string.stats_average_speed, R.string.stats_average_pace, averageSpeed, metricUnits,
|
||||
reportSpeed);
|
||||
|
||||
// Set moving time
|
||||
setTimeValue(context, getView(activity, view, R.id.stats_moving_time),
|
||||
R.string.stats_moving_time, tripStatistics != null ? tripStatistics.getMovingTime() : -1L);
|
||||
|
||||
// Set average moving speed/pace
|
||||
double averageMovingSpeed = tripStatistics != null ? tripStatistics.getAverageMovingSpeed()
|
||||
: Double.NaN;
|
||||
setSpeed(context, getView(activity, view, R.id.stats_average_moving_time),
|
||||
R.string.stats_average_moving_speed, R.string.stats_average_moving_pace, averageMovingSpeed,
|
||||
metricUnits, reportSpeed);
|
||||
|
||||
// Set max speed/pace
|
||||
double maxSpeed = tripStatistics == null ? Double.NaN : tripStatistics.getMaxSpeed();
|
||||
setSpeed(context, getView(activity, view, R.id.stats_max_speed), R.string.stats_max_speed,
|
||||
R.string.stats_fastest_pace, maxSpeed, metricUnits, reportSpeed);
|
||||
|
||||
// Set average moving speed/pace
|
||||
double averageMovingSpeed = tripStatistics != null ? tripStatistics.getAverageMovingSpeed()
|
||||
: Double.NaN;
|
||||
setSpeed(context, getView(activity, view, R.id.stats_average_moving_speed),
|
||||
R.string.stats_average_moving_speed, R.string.stats_average_moving_pace, averageMovingSpeed,
|
||||
metricUnits, reportSpeed);
|
||||
|
||||
// Set grade/elevation
|
||||
boolean showGradeElevation = PreferencesUtils.getBoolean(context,
|
||||
R.string.stats_show_grade_elevation_key,
|
||||
PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT);
|
||||
View gradeElevationHorizontalLine = getView(
|
||||
activity, view, R.id.stats_grade_elevation_horizontal_line);
|
||||
View gradeElevationSeparator = getView(activity, view, R.id.stats_grade_elevation_separator);
|
||||
View gradeElevationContainer = getView(activity, view, R.id.stats_grade_elevation_container);
|
||||
|
||||
gradeElevationSeparator.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE);
|
||||
gradeElevationContainer.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showGradeElevation) {
|
||||
gradeElevationHorizontalLine.setVisibility(View.VISIBLE);
|
||||
gradeElevationContainer.setVisibility(View.VISIBLE);
|
||||
// Set grade
|
||||
double minGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMinGrade();
|
||||
double maxGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMaxGrade();
|
||||
@@ -194,33 +207,6 @@ public class StatsUtils {
|
||||
R.string.stats_min, minElevation, metricUnits);
|
||||
setElevationValue(context, getView(activity, view, R.id.stats_elevation_max),
|
||||
R.string.stats_max, maxElevation, metricUnits);
|
||||
} else {
|
||||
gradeElevationHorizontalLine.setVisibility(View.GONE);
|
||||
gradeElevationContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (trackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
|
||||
// Set calories
|
||||
boolean showCalorie = PreferencesUtils.getBoolean(
|
||||
context, R.string.stats_show_calorie_key, PreferencesUtils.STATS_SHOW_CALORIE_DEFAULT);
|
||||
View calorieHorizontalLine = getView(activity, view, R.id.stats_calorie_horizontal_line);
|
||||
View calorieContainer = getView(activity, view, R.id.stats_calorie_container);
|
||||
if (showCalorie) {
|
||||
calorieHorizontalLine.setVisibility(View.VISIBLE);
|
||||
calorieContainer.setVisibility(View.VISIBLE);
|
||||
double calories = Double.NaN;
|
||||
if (tripStatistics != null) {
|
||||
calories = tripStatistics.getCalorie();
|
||||
}
|
||||
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||
ActivityType activityType = track == null ? ActivityType.INVALID
|
||||
: CalorieUtils.getActivityType(context, track.getCategory());
|
||||
setCalorie(context, getView(activity, view, R.id.stats_calorie), activityType, calories);
|
||||
} else {
|
||||
calorieHorizontalLine.setVisibility(View.GONE);
|
||||
calorieContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,6 +227,19 @@ public class StatsUtils {
|
||||
setItem(context, view, reportSpeed ? speedLabelId : paceLabelId, parts[0], parts[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets calorie.
|
||||
*
|
||||
* @param context the context
|
||||
* @param view the containing view
|
||||
* @param calorie the value of calorie
|
||||
*/
|
||||
private static void setCalorie(Context context, View view, double calorie) {
|
||||
String value = Double.isNaN(calorie) ? null
|
||||
: String.format(Locale.getDefault(), CALORIES_FORMAT, calorie);
|
||||
setItem(context, view, R.string.stats_calorie, value, context.getString(R.string.unit_calorie));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets distance value.
|
||||
*
|
||||
@@ -286,11 +285,11 @@ public class StatsUtils {
|
||||
unit = null;
|
||||
} else {
|
||||
if (metricUnits) {
|
||||
value = String.format(Locale.getDefault(), ELEVATION_FORMAT, elevation);
|
||||
value = StringUtils.formatDecimal(elevation);
|
||||
unit = context.getString(R.string.unit_meter);
|
||||
} else {
|
||||
elevation *= UnitConversions.M_TO_FT;
|
||||
value = String.format(Locale.getDefault(), ELEVATION_FORMAT, elevation);
|
||||
value = StringUtils.formatDecimal(elevation);
|
||||
unit = context.getString(R.string.unit_feet);
|
||||
}
|
||||
}
|
||||
@@ -322,28 +321,8 @@ public class StatsUtils {
|
||||
private static void setCoordinateValue(
|
||||
Context context, View view, int labelId, double coordinate) {
|
||||
String value = Double.isNaN(coordinate) || Double.isInfinite(coordinate) ? null
|
||||
: Location.convert(coordinate, Location.FORMAT_DEGREES);
|
||||
setItem(context, view, labelId, value, COORDINATE_DEGREE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets calorie.
|
||||
*
|
||||
* @param context the context
|
||||
* @param view the containing view
|
||||
* @param activityType the activity type
|
||||
* @param calorie the value of calorie
|
||||
*/
|
||||
private static void setCalorie(
|
||||
Context context, View view, ActivityType activityType, double calorie) {
|
||||
if (activityType == ActivityType.INVALID) {
|
||||
view.setVisibility(View.GONE);
|
||||
} else {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
setItem(context, view, R.string.stats_calorie,
|
||||
String.format(Locale.getDefault(), CALORIES_FORMAT, calorie),
|
||||
context.getString(R.string.unit_calorie));
|
||||
}
|
||||
: Location.convert(coordinate, Location.FORMAT_DEGREES) + COORDINATE_DEGREE;
|
||||
setItem(context, view, labelId, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,7 +334,8 @@ public class StatsUtils {
|
||||
* @param value the value, can be null
|
||||
* @param unit the unit. Null to hide the unit
|
||||
*/
|
||||
private static void setItem(Context context, View view, int labelId, String value, String unit) {
|
||||
private static void setItem(
|
||||
Context context, View view, int labelId, CharSequence value, CharSequence unit) {
|
||||
TextView labelTextView = (TextView) view.findViewById(R.id.stats_label);
|
||||
TextView valueTextView = (TextView) view.findViewById(R.id.stats_value);
|
||||
TextView unitTextView = (TextView) view.findViewById(R.id.stats_unit);
|
||||
|
||||
@@ -81,7 +81,7 @@ public class StringUtils {
|
||||
*/
|
||||
public static String formatElapsedTime(long time) {
|
||||
/*
|
||||
* Temporary workaround for DateUtils.formatElapsedTime(time / 1000). In API
|
||||
* Temporary workaround for DateUtils.formatElapsedTime(time * MS_TO_S). In API
|
||||
* level 17, it returns strings like "1:0:00" instead of "1:00:00", which
|
||||
* breaks several unit tests.
|
||||
*/
|
||||
@@ -91,7 +91,7 @@ public class StringUtils {
|
||||
long hours = 0;
|
||||
long minutes = 0;
|
||||
long seconds = 0;
|
||||
long elapsedSeconds = time / 1000;
|
||||
long elapsedSeconds = (long) (time * UnitConversions.MS_TO_S);
|
||||
|
||||
if (elapsedSeconds >= 3600) {
|
||||
hours = elapsedSeconds / 3600;
|
||||
@@ -149,6 +149,19 @@ public class StringUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatWeight(double value) {
|
||||
return formatDecimal(value, 1);
|
||||
}
|
||||
|
||||
public static String formatDecimal(double value) {
|
||||
return formatDecimal(value, 2);
|
||||
}
|
||||
|
||||
private static String formatDecimal(double value, int precision) {
|
||||
String result = String.format(Locale.getDefault(), "%1$,." + precision + "f", value);
|
||||
return result.replaceAll("[0]*$", "").replaceAll("\\.$", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the distance in an array of two strings. The first string is the
|
||||
* distance. The second string is the unit. The first string is null if the
|
||||
@@ -183,7 +196,7 @@ public class StringUtils {
|
||||
unitId = R.string.unit_feet;
|
||||
}
|
||||
}
|
||||
result[0] = String.format("%.2f", distance);
|
||||
result[0] = formatDecimal(distance);
|
||||
result[1] = context.getString(unitId);
|
||||
return result;
|
||||
}
|
||||
@@ -217,13 +230,13 @@ public class StringUtils {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
if (reportSpeed) {
|
||||
result[0] = String.format("%.2f", speed);
|
||||
result[0] = StringUtils.formatDecimal(speed);
|
||||
} else {
|
||||
// convert from hours to minutes
|
||||
double pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
int minutes = (int) pace;
|
||||
int seconds = (int) Math.round((pace - minutes) * 60.0);
|
||||
result[0] = String.format("%d:%02d", minutes, seconds);
|
||||
result[0] = String.format(Locale.US, "%d:%02d", minutes, seconds);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -301,7 +314,7 @@ public class StringUtils {
|
||||
if (fractional != null) {
|
||||
// Regex ensures fractional part is in (0,1)
|
||||
float fractionalSeconds = Float.parseFloat(fractional);
|
||||
long fractionalMillis = (long) (fractionalSeconds * 1000.0f);
|
||||
long fractionalMillis = (long) (fractionalSeconds * UnitConversions.S_TO_MS);
|
||||
time += fractionalMillis;
|
||||
}
|
||||
|
||||
@@ -350,7 +363,7 @@ public class StringUtils {
|
||||
}
|
||||
int[] parts = new int[3];
|
||||
|
||||
long seconds = time / 1000;
|
||||
long seconds = (long) (time * UnitConversions.MS_TO_S);
|
||||
parts[0] = (int) (seconds % 60);
|
||||
int minutes = (int) (seconds / 60);
|
||||
parts[1] = minutes % 60;
|
||||
|
||||
@@ -21,8 +21,13 @@ import com.google.android.maps.mytracks.R;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -100,10 +105,10 @@ public class TrackIconUtils {
|
||||
*/
|
||||
public static int getIconDrawable(String iconValue) {
|
||||
if (iconValue == null || iconValue.equals("")) {
|
||||
return R.drawable.ic_track_walk;
|
||||
return R.drawable.ic_track_generic;
|
||||
}
|
||||
Pair<Integer, Integer> pair = map.get(iconValue);
|
||||
return pair == null ? R.drawable.ic_track_walk : pair.second;
|
||||
return pair == null ? R.drawable.ic_track_generic : pair.second;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,6 +142,9 @@ public class TrackIconUtils {
|
||||
* @param activityType the activity type
|
||||
*/
|
||||
public static String getIconValue(Context context, String activityType) {
|
||||
if (activityType == null || activityType.equals("")) {
|
||||
return "";
|
||||
}
|
||||
if (inList(context, activityType, airplane)) {
|
||||
return AIRPLANE;
|
||||
}
|
||||
@@ -196,6 +204,35 @@ public class TrackIconUtils {
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void setIconSpinner(Spinner spinner, String iconValue) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayAdapter<StringBuilder> adapter = (ArrayAdapter<StringBuilder>) spinner.getAdapter();
|
||||
StringBuilder stringBuilder = adapter.getItem(0);
|
||||
stringBuilder.delete(0, stringBuilder.length());
|
||||
stringBuilder.append(iconValue);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public static ArrayAdapter<StringBuilder> getIconSpinnerAdapter(
|
||||
final Context context, String iconValue) {
|
||||
return new ArrayAdapter<StringBuilder>(context, android.R.layout.simple_spinner_item,
|
||||
new StringBuilder[] { new StringBuilder(iconValue) }) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, android.view.ViewGroup parent) {
|
||||
ImageView imageView = convertView != null ? (ImageView) convertView
|
||||
: new ImageView(getContext());
|
||||
Bitmap source = BitmapFactory.decodeResource(
|
||||
context.getResources(), TrackIconUtils.getIconDrawable(getItem(position).toString()));
|
||||
if (ApiAdapterFactory.getApiAdapter().isSpinnerBackgroundLight()) {
|
||||
source = TrackIconUtils.invertBitmap(source);
|
||||
}
|
||||
imageView.setImageBitmap(source);
|
||||
imageView.setPadding(4, 4, -4, -4);
|
||||
return imageView;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the activity type is in the list.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2013 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.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Utilities for updating track.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class TrackUtils {
|
||||
|
||||
private TrackUtils() {}
|
||||
|
||||
public static void updateTrack(Context context, Track track, String name, String category,
|
||||
String description, MyTracksProviderUtils myTracksProviderUtils,
|
||||
TrackRecordingServiceConnection trackRecordingServiceConnection, boolean newWeight) {
|
||||
if (name != null) {
|
||||
track.setName(name);
|
||||
}
|
||||
boolean updateCalorie = false;
|
||||
if (category != null) {
|
||||
updateCalorie = !category.equals(track.getCategory()) || newWeight;
|
||||
track.setCategory(category);
|
||||
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
||||
}
|
||||
|
||||
if (description != null) {
|
||||
track.setDescription(description);
|
||||
}
|
||||
track.setModifiedTime(System.currentTimeMillis());
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
|
||||
if (updateCalorie) {
|
||||
if (track.getId() == PreferencesUtils.getLong(context, R.string.recording_track_id_key)) {
|
||||
// Update calorie through track recording service
|
||||
TrackRecordingServiceConnectionUtils.updateCalorie(trackRecordingServiceConnection);
|
||||
} else {
|
||||
CalorieUtils.updateTrackCalorie(context, track);
|
||||
}
|
||||
}
|
||||
|
||||
boolean driveSync = PreferencesUtils.getBoolean(
|
||||
context, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT);
|
||||
if (driveSync) {
|
||||
PreferencesUtils.addToList(context, R.string.drive_edited_list_key,
|
||||
PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT, String.valueOf(track.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,13 @@ public class UnitConversions {
|
||||
|
||||
private UnitConversions() {}
|
||||
|
||||
// multiplication factor to convert milliliters to liters
|
||||
public static final double ML_TO_L = 1 / 1000.0;
|
||||
// Time
|
||||
|
||||
// multiplication factor to convert seconds to milliseconds
|
||||
public static final double S_TO_MS = 1000.0;
|
||||
|
||||
// multiplication factor to convert milliseconds to seconds
|
||||
public static final double MS_TO_S = 1 / 1000.0;
|
||||
public static final double MS_TO_S = 1 / S_TO_MS;
|
||||
|
||||
// multiplication factor to convert minutes to seconds
|
||||
public static final double MIN_TO_S = 60.0;
|
||||
@@ -42,15 +44,11 @@ public class UnitConversions {
|
||||
// multiplication factor to convert minutes to hours
|
||||
public static final double MIN_TO_HR = 1 / HR_TO_MIN;
|
||||
|
||||
// Distance
|
||||
|
||||
// multiplication factor to convert kilometers to miles
|
||||
public static final double KM_TO_MI = 0.621371192;
|
||||
|
||||
// multiplication factor to convert kilograms to pounds
|
||||
public static final double KG_TO_LB = 2.2046;
|
||||
|
||||
// multiplication factor to convert pounds to kilograms
|
||||
public static final double LB_TO_KG = 1 / KG_TO_LB;
|
||||
|
||||
// multiplication factor to convert miles to kilometers
|
||||
public static final double MI_TO_KM = 1 / KM_TO_MI;
|
||||
|
||||
@@ -60,13 +58,11 @@ public class UnitConversions {
|
||||
// multiplication factor to convert feet to miles
|
||||
public static final double FT_TO_MI = 1 / MI_TO_FT;
|
||||
|
||||
// multiplication factor to covert kilometers to meters
|
||||
public static final double KM_TO_M = 1000.0;
|
||||
|
||||
// multiplication factor to convert meters to kilometers
|
||||
public static final double M_TO_KM = 1 / 1000.0;
|
||||
|
||||
public static final double KM_TO_M = 1 / M_TO_KM;
|
||||
|
||||
// multiplication factor to convert meters per second to kilometers per hour
|
||||
public static final double MS_TO_KMH = 3.6;
|
||||
public static final double M_TO_KM = 1 / KM_TO_M;
|
||||
|
||||
// multiplication factor to convert meters to miles
|
||||
public static final double M_TO_MI = M_TO_KM * KM_TO_MI;
|
||||
@@ -74,12 +70,33 @@ public class UnitConversions {
|
||||
// multiplication factor to convert meters to feet
|
||||
public static final double M_TO_FT = M_TO_MI * MI_TO_FT;
|
||||
|
||||
// Weight
|
||||
|
||||
// multiplication factor to convert kilograms to pounds
|
||||
public static final double KG_TO_LB = 2.2046;
|
||||
|
||||
// multiplication factor to convert pounds to kilograms
|
||||
public static final double LB_TO_KG = 1 / KG_TO_LB;
|
||||
|
||||
// Calories
|
||||
|
||||
// multiplication factor to convert milliliters to liters
|
||||
public static final double ML_TO_L = 1 / 1000.0;
|
||||
|
||||
// multiplication factor to convert liter to kcal
|
||||
public static final double L_TO_KCAL = 5.0;
|
||||
|
||||
// multiplication factor to convert watt to kgm/min
|
||||
public static final double W_TO_KGM = 6.12;
|
||||
|
||||
// multiplication factor to convert kgm to kcal
|
||||
public static final double KGM_TO_KCAL = 1 / 427.0;
|
||||
|
||||
// Others
|
||||
|
||||
// multiplication factor to convert meters per second to kilometers per hour
|
||||
public static final double MS_TO_KMH = M_TO_KM / (S_TO_MIN * MIN_TO_HR);
|
||||
|
||||
// multiplication factor to convert degrees to radians
|
||||
public static final double DEG_TO_RAD = Math.PI / 180.0;
|
||||
|
||||
// multiplication factor to convert Kcal to joules.
|
||||
public static final double KCAL_TO_J = 4184;
|
||||
|
||||
// multiplication factor to convert joules to Kcal.
|
||||
public static final double J_TO_KCAL = 1 / KCAL_TO_J;
|
||||
}
|
||||
|
||||
+6
-1
@@ -394,6 +394,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
int elevationGainIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ELEVATIONGAIN);
|
||||
int minGradeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MINGRADE);
|
||||
int maxGradeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXGRADE);
|
||||
int calorieIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.CALORIE);
|
||||
int photoUrlIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.PHOTOURL);
|
||||
|
||||
Waypoint waypoint = new Waypoint();
|
||||
@@ -496,7 +497,10 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
tripStatistics.setMaxGrade(cursor.getFloat(maxGradeIndex));
|
||||
hasTripStatistics = true;
|
||||
}
|
||||
|
||||
if (!cursor.isNull(calorieIndex)) {
|
||||
tripStatistics.setCalorie(cursor.getFloat(calorieIndex));
|
||||
hasTripStatistics = true;
|
||||
}
|
||||
if (hasTripStatistics) {
|
||||
waypoint.setTripStatistics(tripStatistics);
|
||||
}
|
||||
@@ -739,6 +743,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
values.put(WaypointsColumns.ELEVATIONGAIN, tripStatistics.getTotalElevationGain());
|
||||
values.put(WaypointsColumns.MINGRADE, tripStatistics.getMinGrade());
|
||||
values.put(WaypointsColumns.MAXGRADE, tripStatistics.getMaxGrade());
|
||||
values.put(WaypointsColumns.CALORIE, tripStatistics.getCalorie());
|
||||
}
|
||||
|
||||
values.put(WaypointsColumns.PHOTOURL, waypoint.getPhotoUrl());
|
||||
|
||||
@@ -124,7 +124,7 @@ public interface TracksColumns extends BaseColumns {
|
||||
+ MODIFIEDTIME + " INTEGER, " // modified time
|
||||
+ SHAREDWITHME + " INTEGER, " // shared with me
|
||||
+ SHAREDOWNER + " STRING, " // shared owner
|
||||
+ CALORIE + " INTEGER);"; // calorie
|
||||
+ CALORIE + " FLOAT);"; // calorie
|
||||
|
||||
public static final String[] COLUMNS = { _ID, // id
|
||||
NAME, // name
|
||||
@@ -190,6 +190,6 @@ public interface TracksColumns extends BaseColumns {
|
||||
ContentTypeIds.LONG_TYPE_ID, // modified time
|
||||
ContentTypeIds.BOOLEAN_TYPE_ID, // shared with me
|
||||
ContentTypeIds.STRING_TYPE_ID, // shared owner
|
||||
ContentTypeIds.INT_TYPE_ID // calorie
|
||||
ContentTypeIds.FLOAT_TYPE_ID // calorie
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@ public interface WaypointsColumns extends BaseColumns {
|
||||
public static final String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
||||
public static final String MINGRADE = "mingrade"; // min grade
|
||||
public static final String MAXGRADE = "maxgrade"; // max grade
|
||||
|
||||
public static final String CALORIE = "calorie"; // calorie
|
||||
|
||||
public static final String PHOTOURL = "photoUrl"; // url for the photo
|
||||
|
||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
@@ -104,6 +105,7 @@ public interface WaypointsColumns extends BaseColumns {
|
||||
+ ELEVATIONGAIN + " FLOAT, "
|
||||
+ MINGRADE + " FLOAT, "
|
||||
+ MAXGRADE + " FLOAT, "
|
||||
+ CALORIE + " FLOAT, "
|
||||
+ PHOTOURL + " STRING"
|
||||
+ ");";
|
||||
|
||||
@@ -138,6 +140,7 @@ public interface WaypointsColumns extends BaseColumns {
|
||||
ELEVATIONGAIN,
|
||||
MINGRADE,
|
||||
MAXGRADE,
|
||||
CALORIE,
|
||||
PHOTOURL
|
||||
};
|
||||
|
||||
@@ -172,6 +175,7 @@ public interface WaypointsColumns extends BaseColumns {
|
||||
FLOAT_TYPE_ID, // elevation gain
|
||||
FLOAT_TYPE_ID, // min grade
|
||||
FLOAT_TYPE_ID, // max grade
|
||||
FLOAT_TYPE_ID, // calorie
|
||||
STRING_TYPE_ID // photo url
|
||||
};
|
||||
}
|
||||
|
||||
+6
-6
@@ -225,8 +225,8 @@ public class TripStatisticsUpdaterTest extends TestCase {
|
||||
Location location = getLocation(i + locationOffset, (i + locationOffset) * .001, MOVING_SPEED,
|
||||
startTime + (timeOffset + i) * TEN_SECONDS);
|
||||
tripStatisticsUpdater.addLocation(location,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.FOOT,
|
||||
PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.WALKING,
|
||||
PreferencesUtils.WEIGHT_DEFAULT);
|
||||
tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
|
||||
assertEquals((timeOffset + i) * TEN_SECONDS, tripStatistics.getTotalTime());
|
||||
@@ -272,8 +272,8 @@ public class TripStatisticsUpdaterTest extends TestCase {
|
||||
Location location = getLocation(
|
||||
locationOffset, locationOffset * .001, 0, startTime + (i + timeOffset) * TEN_SECONDS);
|
||||
tripStatisticsUpdater.addLocation(location,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, false, ActivityType.FOOT,
|
||||
PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, false, ActivityType.WALKING,
|
||||
PreferencesUtils.WEIGHT_DEFAULT);
|
||||
|
||||
tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
assertEquals((i + timeOffset) * TEN_SECONDS, tripStatistics.getTotalTime());
|
||||
@@ -310,8 +310,8 @@ public class TripStatisticsUpdaterTest extends TestCase {
|
||||
Location location = getLocation(i + locationOffset, (i + locationOffset) * .001, 99999,
|
||||
startTime + (timeOffset + i) * TEN_SECONDS);
|
||||
tripStatisticsUpdater.addLocation(location,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.FOOT,
|
||||
PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.WALKING,
|
||||
PreferencesUtils.WEIGHT_DEFAULT);
|
||||
tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
|
||||
assertTrue(tripStatistics.getMovingTime() <= tripStatistics.getTotalTime());
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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 android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* A unit test for {@link CalorieUtils}.
|
||||
*
|
||||
* @author youtaol
|
||||
*/
|
||||
public class CalorieUtilsTest extends TestCase {
|
||||
|
||||
Location start = new Location(LocationManager.GPS_PROVIDER);
|
||||
Location stop = new Location(LocationManager.GPS_PROVIDER);
|
||||
private double grade = 0.07;
|
||||
private int weight = 80;
|
||||
private final long TIME_INTERVAL = 1000l;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// Make the time interval is not 0
|
||||
stop.setTime(start.getTime() + TIME_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using foot calculation equation.
|
||||
*/
|
||||
public void testGetCalories_foot() {
|
||||
double actual = CalorieUtils.getCalorie(start, stop, grade, weight,
|
||||
CalorieUtils.ActivityType.FOOT);
|
||||
double expected = CalorieUtils.getFootCalorie(start, stop, grade, weight);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether using foot calculation equation while grade is negative.
|
||||
*/
|
||||
public void testGetCalories_footNegativeGrade() {
|
||||
double actualGrade = -5;
|
||||
double expectGrade = 0;
|
||||
double actual = CalorieUtils.getCalorie(start, stop, actualGrade, weight,
|
||||
CalorieUtils.ActivityType.FOOT);
|
||||
double expected = CalorieUtils.getFootCalorie(start, stop, expectGrade, weight);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using cycling calculation equation.
|
||||
*/
|
||||
public void testGetCalories_cycling() {
|
||||
double actual = CalorieUtils.getCalorie(start, stop, grade, weight,
|
||||
CalorieUtils.ActivityType.CYCLING);
|
||||
double expected = CalorieUtils.getCyclingCalorie(start, stop, grade, weight);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using running VO2 equation.
|
||||
*/
|
||||
public void testGetVO2_running() {
|
||||
double actual = CalorieUtils.getFootVo2(CalorieUtils.CRTICAL_SPEED_RUNNING * 2, grade);
|
||||
double expected = CalorieUtils.getHighSpeedFootVo2(CalorieUtils.CRTICAL_SPEED_RUNNING * 2,
|
||||
grade);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using walking VO2 equation.
|
||||
*/
|
||||
public void testGetVO2_walking() {
|
||||
// Test at half the critical speed
|
||||
double footSpeed = CalorieUtils.CRTICAL_SPEED_RUNNING / 2.0;
|
||||
|
||||
double actual = CalorieUtils.getFootVo2(footSpeed, grade);
|
||||
double expected = CalorieUtils.getLowSpeedFootVo2(footSpeed, grade);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks calculating cycling calorie. 175 is the watt usage for a 90 kg bike
|
||||
* + rider to go 9 m/s (20 mph or 32 km/h) on the flat. <a href=
|
||||
* "http://www.ideafit.com/fitness-library/calculating-caloric-expenditure-0"
|
||||
* >Reference</a>
|
||||
*/
|
||||
public void testCalculateCalorieCycling() {
|
||||
grade = 0;
|
||||
weight = 90;
|
||||
start.setSpeed(9);
|
||||
stop.setSpeed(9);
|
||||
double expected = 175.0 * (TIME_INTERVAL / 1000) * UnitConversions.J_TO_KCAL;
|
||||
double actual = CalorieUtils.getCyclingCalorie(start, stop, grade, weight);
|
||||
assertTrue((actual - expected) / expected < 0.02);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user