Merge heads

This commit is contained in:
youtaol
2013-05-09 11:27:28 +08:00
44 changed files with 903 additions and 803 deletions
+2 -9
View File
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/ScrollView" > style="@style/StatsScrollView" >
<LinearLayout <LinearLayout
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -44,14 +44,7 @@ limitations under the License.
<TextView <TextView
android:text="@string/marker_detail_statistics_title" android:text="@string/marker_detail_statistics_title"
style="@style/TextLarge.WithTopMargin" /> style="@style/TextLarge.WithTopMargin" />
<TableLayout <include layout="@layout/stats_content" />
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:paddingTop="8dp"
android:shrinkColumns="0,2"
android:stretchColumns="0,2" >
<include layout="@layout/stats_core" />
</TableLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
+7 -36
View File
@@ -15,42 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/ScrollView" > style="@style/StatsScrollView" >
<TableLayout <LinearLayout
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:padding="8dp" android:orientation="vertical"
android:shrinkColumns="0,2" android:paddingLeft="8dp"
android:stretchColumns="0,2" > android:paddingRight="8dp" >
<!-- Speed on one line --> <include layout="@layout/stats_content" />
<TableRow> </LinearLayout>
<TextView
android:id="@+id/stats_speed_label"
style="@style/StatsLabel" />
</TableRow>
<TableRow>
<TextView
android:id="@+id/stats_speed_value"
style="@style/StatsValue" />
</TableRow>
<include layout="@layout/stats_core" />
<TableRow android:id="@+id/stats_coordinate_label_table_row" >
<TextView
android:text="@string/stats_latitude"
style="@style/StatsLabel" />
<TextView
android:layout_column="2"
android:text="@string/stats_longitude"
style="@style/StatsLabel" />
</TableRow>
<TableRow android:id="@+id/stats_coordinate_value_table_row" >
<TextView
android:id="@+id/stats_latitude_value"
style="@style/StatsValue" />
<TextView style="@style/StatsSpacer" />
<TextView
android:id="@+id/stats_longitude_value"
style="@style/StatsValue" />
</TableRow>
</TableLayout>
</ScrollView> </ScrollView>
+132
View File
@@ -0,0 +1,132 @@
<?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" >
<!-- Row 1: distance and speed -->
<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" />
</LinearLayout>
<!-- Row 2: total time and average speed -->
<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" />
</LinearLayout>
<!-- Row 4: empty and max 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" />
</LinearLayout>
<!-- Row 5: horizontal line -->
<View
android:id="@+id/stats_grade_elevation_horizontal_line"
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" >
<!-- 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" />
</LinearLayout>
<View style="@style/StatsVerticalSpacer" />
<!-- Elevation -->
<LinearLayout style="@style/StatsSmallItemContainer" >
<!-- 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" />
</LinearLayout>
</LinearLayout>
<!-- Row 6: horizontal line -->
<View
android:id="@+id/stats_coordinate_horizontal_line"
style="@style/StatsHorizontalLine" />
<!-- Row 6: 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" />
<View style="@style/StatsVerticalSpacer" />
<include
android:id="@+id/stats_longitude"
layout="@layout/stats_small_item" />
</LinearLayout>
</merge>
-140
View File
@@ -1,140 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2012 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" >
<!-- Total distance -->
<TableRow>
<TextView
android:text="@string/stats_distance"
style="@style/StatsLabel" />
</TableRow>
<TableRow>
<TextView
android:id="@+id/stats_distance_value"
style="@style/StatsValue" />
</TableRow>
<!-- Total time/moving time -->
<TableRow>
<TextView
android:text="@string/stats_total_time"
style="@style/StatsLabel" />
<TextView
android:id="@+id/stats_moving_time_label"
android:layout_column="2"
android:text="@string/stats_moving_time"
style="@style/StatsLabel" />
</TableRow>
<TableRow>
<TextView
android:id="@+id/stats_total_time_value"
style="@style/StatsValue" />
<TextView
android:id="@+id/stats_moving_time_spacer"
style="@style/StatsSpacer" />
<TextView
android:id="@+id/stats_moving_time_value"
style="@style/StatsValue" />
</TableRow>
<!-- Average speed/average moving speed (average pace/average moving pace) -->
<TableRow>
<TextView
android:id="@+id/stats_average_speed_label"
style="@style/StatsLabel" />
<TextView
android:id="@+id/stats_average_moving_speed_label"
android:layout_column="2"
style="@style/StatsLabel" />
</TableRow>
<TableRow>
<TextView
android:id="@+id/stats_average_speed_value"
style="@style/StatsValue" />
<TextView
android:id="@+id/stats_average_moving_speed_spacer"
style="@style/StatsSpacer" />
<TextView
android:id="@+id/stats_average_moving_speed_value"
style="@style/StatsValue" />
</TableRow>
<!-- Max speed (fastest pace) -->
<TableRow>
<TextView
android:id="@+id/stats_max_speed_label"
style="@style/StatsLabel" />
</TableRow>
<TableRow>
<TextView
android:id="@+id/stats_max_speed_value"
style="@style/StatsValue" />
</TableRow>
<!-- Elevation and elevation gain -->
<TableRow android:id="@+id/stats_elevation_label_table_row1" >
<TextView
android:text="@string/stats_elevation"
style="@style/StatsLabel" />
<TextView
android:layout_column="2"
android:text="@string/stats_elevation_gain"
style="@style/StatsLabel" />
</TableRow>
<TableRow android:id="@+id/stats_elevation_value_table_row1" >
<TextView
android:id="@+id/stats_elevation_value"
style="@style/StatsValue" />
<TextView style="@style/StatsSpacer" />
<TextView
android:id="@+id/stats_elevation_gain_value"
style="@style/StatsValue" />
</TableRow>
<!-- Min and max elevation -->
<TableRow android:id="@+id/stats_elevation_label_table_row2" >
<TextView
android:text="@string/stats_min_elevation"
style="@style/StatsLabel" />
<TextView
android:layout_column="2"
android:text="@string/stats_max_elevation"
style="@style/StatsLabel" />
</TableRow>
<TableRow android:id="@+id/stats_elevation_value_table_row2" >
<TextView
android:id="@+id/stats_min_elevation_value"
style="@style/StatsValue" />
<TextView style="@style/StatsSpacer" />
<TextView
android:id="@+id/stats_max_elevation_value"
style="@style/StatsValue" />
</TableRow>
<!-- Min and max grade -->
<TableRow android:id="@+id/stats_grade_label_table_row" >
<TextView
android:text="@string/stats_min_grade"
style="@style/StatsLabel" />
<TextView
android:layout_column="2"
android:text="@string/stats_max_grade"
style="@style/StatsLabel" />
</TableRow>
<TableRow android:id="@+id/stats_grade_value_table_row" >
<TextView
android:id="@+id/stats_min_grade_value"
style="@style/StatsValue" />
<TextView style="@style/StatsSpacer" />
<TextView
android:id="@+id/stats_max_grade_value"
style="@style/StatsValue" />
</TableRow>
</merge>
+33
View File
@@ -0,0 +1,33 @@
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stats_large_item_id"
style="@style/StatsLargeItemContainer" >
<!-- Distance label -->
<TextView
android:id="@+id/stats_label"
style="@style/StatsLargeLabel" />
<!-- Distance value -->
<LinearLayout style="@style/StatsLargeValueContainer" >
<TextView
android:id="@+id/stats_value"
style="@style/StatsLargeValue" />
<TextView
android:id="@+id/stats_unit"
style="@style/StatsUnit" />
</LinearLayout>
</LinearLayout>
+32
View File
@@ -0,0 +1,32 @@
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stats_small_item_id"
style="@style/StatsSmallItemContainer" >
<TextView
android:id="@+id/stats_label"
style="@style/StatsSmallLabel" />
<!-- Distance value -->
<LinearLayout style="@style/StatsSmallValueContainer" >
<TextView
android:id="@+id/stats_value"
style="@style/StatsSmallValue" />
<TextView
android:id="@+id/stats_unit"
style="@style/StatsUnit" />
</LinearLayout>
</LinearLayout>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright 2012 Google Inc. Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -14,14 +14,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/ScrollView"> android:id="@+id/stats_small_1line_id"
<TableLayout style="@style/StatsSmallValueContainer" >
android:layout_height="wrap_content" <TextView
android:layout_width="match_parent" android:id="@+id/stats_value"
android:padding="8dp" style="@style/StatsSmallValue" />
android:shrinkColumns="0,2" <TextView
android:stretchColumns="0,2" > android:id="@+id/stats_unit"
<include layout="@layout/stats_core" /> style="@style/StatsUnit" />
</TableLayout> <TextView
</ScrollView> android:id="@+id/stats_label"
android:layout_gravity="bottom"
android:paddingLeft="8dp"
style="@style/StatsSmallLabel" />
</LinearLayout>
+5
View File
@@ -15,6 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:icon="@drawable/menu_play"
android:id="@+id/list_context_menu_play"
android:showAsAction="ifRoom"
android:title="@string/menu_play"/>
<item <item
android:icon="@drawable/menu_share" android:icon="@drawable/menu_share"
android:id="@+id/list_context_menu_share" android:id="@+id/list_context_menu_share"
+1 -1
View File
@@ -31,7 +31,7 @@ limitations under the License.
</style> </style>
<style name="ListItem"> <style name="ListItem">
<item name="android:background">?android:attr/activatedBackgroundIndicator</item> <item name="android:background">?android:attr/activatedBackgroundIndicator</item>
</style> </style>
<style name="MyTracksActionBar" parent="@android:style/Widget.Holo.ActionBar"> <style name="MyTracksActionBar" parent="@android:style/Widget.Holo.ActionBar">
<!-- Same image as the source except using a grey line instead of a blue line --> <!-- Same image as the source except using a grey line instead of a blue line -->
<item name="android:background">@drawable/ab_transparent_dark_holo</item> <item name="android:background">@drawable/ab_transparent_dark_holo</item>
+18 -1
View File
@@ -30,6 +30,23 @@ limitations under the License.
<item name="android:orientation">horizontal</item> <item name="android:orientation">horizontal</item>
<item name="android:measureWithLargestChild">true</item> <item name="android:measureWithLargestChild">true</item>
</style> </style>
<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:singleLine">false</item>
<item name="android:textAllCaps">true</item>
<item name="android:textStyle">bold</item>
</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: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 name="TextChooseActivity">
<!-- Same as TextChooseActivity in values/styles.xml except removing textSize and using textAppearance --> <!-- Same as TextChooseActivity in values/styles.xml except removing textSize and using textAppearance -->
<item name="android:textAppearance">?android:attr/textAppearanceListItemSmall</item> <item name="android:textAppearance">?android:attr/textAppearanceListItemSmall</item>
@@ -58,7 +75,7 @@ limitations under the License.
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item> <item name="android:layout_width">wrap_content</item>
<item name="android:singleLine">true</item> <item name="android:singleLine">true</item>
<item name="android:textColor">@color/track_widget_label</item> <item name="android:textColor">@color/stats_large_label</item>
<item name="android:textSize">10dp</item> <item name="android:textSize">10dp</item>
<item name="android:textStyle">bold</item> <item name="android:textStyle">bold</item>
<item name="android:textAllCaps">true</item> <item name="android:textAllCaps">true</item>
+2 -2
View File
@@ -28,11 +28,11 @@ limitations under the License.
<color name="chart_speed_border">#FF0066FF</color> <!-- blue --> <color name="chart_speed_border">#FF0066FF</color> <!-- blue -->
<color name="chart_speed_fill">#40C8E0FF</color> <!-- transparent blue --> <color name="chart_speed_fill">#40C8E0FF</color> <!-- transparent blue -->
<color name="recording_text">#FFBE161B</color> <!-- red --> <color name="recording_text">#FFBE161B</color> <!-- red -->
<color name="stats_large_label">#FFFAAD2C</color> <!-- orange -->
<color name="stats_separator">#99999999</color> <!-- light gray -->
<color name="track_color_fast">#F7DF0101</color> <!-- red color --> <color name="track_color_fast">#F7DF0101</color> <!-- red color -->
<color name="track_color_normal">#F75858FA</color> <!-- blue color --> <color name="track_color_normal">#F75858FA</color> <!-- blue color -->
<color name="track_color_slow">#F774DF00</color> <!-- green color --> <color name="track_color_slow">#F774DF00</color> <!-- green color -->
<color name="track_controller_background">#FF272727</color> <!-- dark gray --> <color name="track_controller_background">#FF272727</color> <!-- dark gray -->
<color name="track_widget_label">#FFFAAD2C</color> <!-- orange -->
<color name="track_widget_line">#99999999</color> <!-- light gray -->
<color name="warning_background">#FFFF8800</color> <!-- orange dark --> <color name="warning_background">#FFFF8800</color> <!-- orange dark -->
</resources> </resources>
+1 -3
View File
@@ -53,9 +53,7 @@ limitations under the License.
<string name="share_track_key">shareTrack</string> <string name="share_track_key">shareTrack</string>
<string name="split_frequency_key">splitFrequency</string> <string name="split_frequency_key">splitFrequency</string>
<string name="stats_show_coordinate_key">statsShowCoordinate</string> <string name="stats_show_coordinate_key">statsShowCoordinate</string>
<string name="stats_show_elevation_key">statsShowElevation</string> <string name="stats_show_grade_elevation_key">statsShowGradeElevation</string>
<string name="stats_show_grade_key">statsShowGrade</string>
<string name="stats_show_moving_time_key">statsShowMovingTime</string>
<string name="track_color_mode_medium_key">trackColorModeMedium</string> <string name="track_color_mode_medium_key">trackColorModeMedium</string>
<string name="track_color_mode_percentage_key">trackColorModePercentage</string> <string name="track_color_mode_percentage_key">trackColorModePercentage</string>
<string name="track_color_mode_slow_key">trackColorModeSlow</string> <string name="track_color_mode_slow_key">trackColorModeSlow</string>
+19 -53
View File
@@ -2089,12 +2089,12 @@ limitations under the License.
Character limit: ~23 (1 line)"> Character limit: ~23 (1 line)">
Latitude/Longitude Latitude/Longitude
</string> </string>
<string name="settings_stats_grade" <string name="settings_stats_grade_elevation"
translation_description="In the 'Stats settings, the option to show grade. Grade is the amount translation_description="In the 'Stats settings, the option to show grade and elevation. Grade
of inclination to the horizontal, also known as slope or incline. is the amount of inclination to the horizontal, also known as slope or incline.
Character limit: ~23 (1 line)"> Character limit: ~23 (1 line)">
Grade Grade/Elevation
</string> </string>
<string name="settings_stats_rate_title" <string name="settings_stats_rate_title"
translation_description="In the 'Stats' settings, the option to change the preferred rate, translation_description="In the 'Stats' settings, the option to change the preferred rate,
@@ -2209,14 +2209,19 @@ limitations under the License.
Character limit: ~23 (1 line)"> Character limit: ~23 (1 line)">
Elevation Elevation
</string> </string>
<string name="stats_elevation_gain"
translation_description="In the 'Stats' page, the elevation gain.">
Elevation gain
</string>
<string name="stats_fastest_pace" <string name="stats_fastest_pace"
translation_description="In the 'Stats' page, the fastest pace."> translation_description="In the 'Stats' page, the fastest pace.">
Fastest pace Fastest pace
</string> </string>
<string name="stats_gain"
translation_description="In the 'Stats' page, the gain of a value. E.g, gain in elevation.">
Gain
</string>
<string name="stats_grade"
translation_description="In the 'Stats' page, the grade. Grade is the amount of inclination to
the horizontal, also known as slope or incline.">
Grade
</string>
<string name="stats_latitude" <string name="stats_latitude"
translation_description="In the 'Stats' page, the latitude."> translation_description="In the 'Stats' page, the latitude.">
Latitude Latitude
@@ -2225,25 +2230,17 @@ limitations under the License.
translation_description="In the 'Stats' page, the longitude."> translation_description="In the 'Stats' page, the longitude.">
Longitude Longitude
</string> </string>
<string name="stats_max_elevation" <string name="stats_max"
translation_description="In the 'Stats' page, the maximum elevation."> translation_description="In the 'Stats' page, the maximum of a value.">
Max elevation Max
</string>
<string name="stats_max_grade"
translation_description="In the 'Stats' page, the maximum grade.">
Max grade
</string> </string>
<string name="stats_max_speed" <string name="stats_max_speed"
translation_description="In the 'Stats' page, the maximum speed."> translation_description="In the 'Stats' page, the maximum speed.">
Max speed Max speed
</string> </string>
<string name="stats_min_elevation" <string name="stats_min"
translation_description="In the 'Stats' page, the minimum elevation."> translation_description="In the 'Stats' page, the minimum of a value.">
Min elevation Min
</string>
<string name="stats_min_grade"
translation_description="In the 'Stats' page, the minimum grade.">
Min grade
</string> </string>
<string name="stats_moving_time" <string name="stats_moving_time"
translation_description="In the 'Stats' page, the moving time."> translation_description="In the 'Stats' page, the moving time.">
@@ -2464,13 +2461,6 @@ limitations under the License.
translation_description="In a setting picker, the value to always enable the option."> translation_description="In a setting picker, the value to always enable the option.">
Always Always
</string> </string>
<string name="value_coordinate_degree"
translation_description="To display a latitude or longitude coordinate in degree. E.g.,
'2.2 \u00B0'. \u00B0 is the unicode value for the degree symbol.
%1$s: a string value of the coordinate. E.g., '2.2'.">
%1$s \u00B0
</string>
<string name="value_float_feet" <string name="value_float_feet"
translation_description="Display a float value in feet (ft). translation_description="Display a float value in feet (ft).
@@ -2483,12 +2473,6 @@ limitations under the License.
%1$.2f: a float number. E.g., '2.22'."> %1$.2f: a float number. E.g., '2.22'.">
%1$.2f km %1$.2f km
</string> </string>
<string name="value_float_kilometer_hour"
translation_description="Display a float value in kilometers per hour (km/h).
%1$.2f: a float number. E.g., '2.22'.">
%1$.2f km/h
</string>
<string name="value_float_meter" <string name="value_float_meter"
translation_description="Display a float value in meters (m). translation_description="Display a float value in meters (m).
@@ -2501,12 +2485,6 @@ limitations under the License.
%1$.2f: a float number. E.g., '2.22'."> %1$.2f: a float number. E.g., '2.22'.">
%1$.2f mi %1$.2f mi
</string> </string>
<string name="value_float_mile_hour"
translation_description="Display a float value in miles per hour (mi/h).
%1$.2f: a float number. E.g., '2.22'.">
%1$.2f mi/h
</string>
<string name="value_float_mile_poor_gps" <string name="value_float_mile_poor_gps"
translation_description="In a setting picker, the string format to display a float value in translation_description="In a setting picker, the string format to display a float value in
miles (mi), recommended when having poor GPS signal. miles (mi), recommended when having poor GPS signal.
@@ -2514,18 +2492,6 @@ limitations under the License.
%1$.1f: a float number. E.g., '2.2'."> %1$.1f: a float number. E.g., '2.2'.">
%1$.1f mi (poor GPS) %1$.1f mi (poor GPS)
</string> </string>
<string name="value_float_minute_kilometer"
translation_description="Display a float value in minutes per kilometer (min/km).
%1$.2f: a float number. E.g., '2.22'.">
%1$.2f min/km
</string>
<string name="value_float_minute_mile"
translation_description="Display a float value in minutes per mile (min/mi).
%1$.2f: a float number. E.g., '2.22'.">
%1$.2f min/mi
</string>
<string name="value_integer_feet" <string name="value_integer_feet"
translation_description="In a setting picker, the string format to display an integer value in translation_description="In a setting picker, the string format to display an integer value in
feet (ft). feet (ft).
+77 -19
View File
@@ -130,24 +130,82 @@ limitations under the License.
<item name="android:paddingRight">8dp</item> <item name="android:paddingRight">8dp</item>
<item name="android:textColor">@android:color/primary_text_light</item> <item name="android:textColor">@android:color/primary_text_light</item>
</style> </style>
<!-- stats label --> <!-- stats horizontal line -->
<style name="StatsLabel" parent="@style/TextMedium"> <style name="StatsHorizontalLine">
<item name="android:layout_weight">1</item> <item name="android:background">@color/stats_separator</item>
<item name="android:layout_width">0dp</item> <item name="android:layout_height">1dp</item>
<item name="android:layout_width">match_parent</item>
</style> </style>
<!-- stats spacer --> <!-- stats large item container -->
<style name="StatsSpacer"> <style name="StatsLargeItemContainer">
<item name="android:layout_height">match_parent</item> <item name="android:layout_gravity">top</item>
<item name="android:layout_width">8dp</item> <item name="android:layout_height">wrap_content</item>
</style> <item name="android:layout_weight">10</item>
<!-- stats value -->
<style name="StatsValue" parent="@style/TextLarge">
<item name="android:background">@android:color/background_light</item>
<item name="android:gravity">right</item>
<item name="android:layout_weight">1</item>
<item name="android:layout_width">0dp</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:paddingRight">8dp</item>
<item name="android:textColor">@android:color/primary_text_light</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: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_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:singleLine">true</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:textSize">32sp</item>
<item name="android:textStyle">bold</item>
</style>
<!-- stats large value container -->
<style name="StatsLargeValueContainer">
<item name="android:layout_gravity">right</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">-4dp</item>
<item name="android:layout_width">wrap_content</item>
</style>
<!-- stats scroll view -->
<style name="StatsScrollView" parent="@style/ScrollView">
<item name="android:scrollbarStyle">outsideOverlay</item>
</style>
<!-- stats small item container -->
<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:singleLine">false</item>
<item name="android:textStyle">bold</item>
</style>
<!-- stats small value -->
<style name="StatsSmallValue" parent="@style/TextLarge">
<item name="android:singleLine">true</item>
<item name="android:textColor">@android:color/white</item>
</style>
<!-- stats small value container -->
<style name="StatsSmallValueContainer" parent="@style/StatsLargeValueContainer">
<item name="android:layout_marginTop">-2dp</item>
</style>
<!-- stats unit -->
<style name="StatsUnit" parent="@style/TextSmall">
<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>
<!-- text choose activity --> <!-- text choose activity -->
<style name="TextChooseActivity"> <style name="TextChooseActivity">
@@ -239,7 +297,7 @@ limitations under the License.
</style> </style>
<!-- track widget horizontal line --> <!-- track widget horizontal line -->
<style name="TrackWidgetHorizontalLine"> <style name="TrackWidgetHorizontalLine">
<item name="android:background">@color/track_widget_line</item> <item name="android:background">@color/stats_separator</item>
<item name="android:layout_height">1dp</item> <item name="android:layout_height">1dp</item>
<item name="android:layout_width">0dp</item> <item name="android:layout_width">0dp</item>
</style> </style>
@@ -285,7 +343,7 @@ limitations under the License.
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item> <item name="android:layout_width">wrap_content</item>
<item name="android:singleLine">true</item> <item name="android:singleLine">true</item>
<item name="android:textColor">@color/track_widget_label</item> <item name="android:textColor">@color/stats_large_label</item>
<item name="android:textSize">10dp</item> <item name="android:textSize">10dp</item>
<item name="android:textStyle">bold</item> <item name="android:textStyle">bold</item>
</style> </style>
@@ -342,7 +400,7 @@ limitations under the License.
<item name="android:textSize">18dp</item> <item name="android:textSize">18dp</item>
</style> </style>
<!-- track widget value --> <!-- track widget value -->
<style name="TrackWidgetValue"> <style name="TrackWidgetValue">
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">2</item> <item name="android:layout_weight">2</item>
<item name="android:layout_width">0dp</item> <item name="android:layout_width">0dp</item>
@@ -373,7 +431,7 @@ limitations under the License.
</style> </style>
<!-- track widget vertical line --> <!-- track widget vertical line -->
<style name="TrackWidgetVerticalLine"> <style name="TrackWidgetVerticalLine">
<item name="android:background">@color/track_widget_line</item> <item name="android:background">@color/stats_separator</item>
<item name="android:layout_height">0dp</item> <item name="android:layout_height">0dp</item>
<item name="android:layout_width">1dp</item> <item name="android:layout_width">1dp</item>
</style> </style>
+2 -10
View File
@@ -29,16 +29,8 @@ limitations under the License.
<PreferenceCategory android:title="@string/settings_show" > <PreferenceCategory android:title="@string/settings_show" >
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"
android:key="@string/stats_show_moving_time_key" android:key="@string/stats_show_grade_elevation_key"
android:title="@string/stats_moving_time" /> android:title="@string/settings_stats_grade_elevation" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/stats_show_elevation_key"
android:title="@string/stats_elevation" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/stats_show_grade_key"
android:title="@string/settings_stats_grade" />
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"
android:key="@string/stats_show_coordinate_key" android:key="@string/stats_show_coordinate_key"
@@ -26,6 +26,7 @@ import com.google.android.apps.mytracks.fragments.ChooseActivityDialogFragment;
import com.google.android.apps.mytracks.fragments.ChooseActivityDialogFragment.ChooseActivityCaller; import com.google.android.apps.mytracks.fragments.ChooseActivityDialogFragment.ChooseActivityCaller;
import com.google.android.apps.mytracks.fragments.ConfirmDialogFragment; import com.google.android.apps.mytracks.fragments.ConfirmDialogFragment;
import com.google.android.apps.mytracks.fragments.ConfirmDialogFragment.ConfirmCaller; import com.google.android.apps.mytracks.fragments.ConfirmDialogFragment.ConfirmCaller;
import com.google.android.apps.mytracks.fragments.InstallEarthDialogFragment;
import com.google.android.apps.mytracks.io.drive.SendDriveActivity; import com.google.android.apps.mytracks.io.drive.SendDriveActivity;
import com.google.android.apps.mytracks.io.file.SaveActivity; import com.google.android.apps.mytracks.io.file.SaveActivity;
import com.google.android.apps.mytracks.io.file.TrackFileFormat; import com.google.android.apps.mytracks.io.file.TrackFileFormat;
@@ -39,6 +40,7 @@ import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
import com.google.android.apps.mytracks.io.spreadsheets.SendSpreadsheetsActivity; import com.google.android.apps.mytracks.io.spreadsheets.SendSpreadsheetsActivity;
import com.google.android.apps.mytracks.io.sync.SyncUtils; import com.google.android.apps.mytracks.io.sync.SyncUtils;
import com.google.android.apps.mytracks.util.AnalyticsUtils; import com.google.android.apps.mytracks.util.AnalyticsUtils;
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.apps.mytracks.util.IntentUtils; import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.StringUtils;
@@ -338,6 +340,28 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
Toast.makeText(this, R.string.send_google_no_account_permission, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.send_google_no_account_permission, Toast.LENGTH_LONG).show();
} }
/**
* Confirm playing tracks in Google Earth.
*
* @param trackIds the track ids
*/
protected void confirmPlay(long[] trackIds) {
if (GoogleEarthUtils.isEarthInstalled(this)) {
ConfirmDialogFragment.newInstance(R.string.confirm_play_earth_key,
PreferencesUtils.CONFIRM_PLAY_EARTH_DEFAULT,
getString(R.string.track_detail_play_confirm_message), trackIds)
.show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG);
} else {
new InstallEarthDialogFragment().show(
getSupportFragmentManager(), InstallEarthDialogFragment.INSTALL_EARTH_DIALOG_TAG);
}
}
/**
* Confirm sharing a track.
*
* @param trackId the track id
*/
protected void confirmShare(long trackId) { protected void confirmShare(long trackId) {
String shareTrack = PreferencesUtils.getString( String shareTrack = PreferencesUtils.getString(
this, R.string.share_track_key, PreferencesUtils.SHARE_TRACK_DEFAULT); this, R.string.share_track_key, PreferencesUtils.SHARE_TRACK_DEFAULT);
@@ -346,35 +370,46 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
if (shareTrack.equals(driveValue)) { if (shareTrack.equals(driveValue)) {
ConfirmDialogFragment.newInstance(R.string.confirm_share_drive_key, ConfirmDialogFragment.newInstance(R.string.confirm_share_drive_key,
PreferencesUtils.CONFIRM_SHARE_DRIVE_DEFAULT, PreferencesUtils.CONFIRM_SHARE_DRIVE_DEFAULT,
getString(R.string.share_track_drive_confirm_message), trackId) getString(R.string.share_track_drive_confirm_message), new long[] { trackId })
.show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG); .show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG);
} else if (shareTrack.equals(mapsValue)) { } else if (shareTrack.equals(mapsValue)) {
ConfirmDialogFragment.newInstance(R.string.confirm_share_maps_key, ConfirmDialogFragment.newInstance(R.string.confirm_share_maps_key,
PreferencesUtils.CONFIRM_SHARE_MAPS_DEFAULT, StringUtils.getHtml( PreferencesUtils.CONFIRM_SHARE_MAPS_DEFAULT, StringUtils.getHtml(
this, R.string.share_track_maps_confirm_message, R.string.maps_public_unlisted_url), this, R.string.share_track_maps_confirm_message, R.string.maps_public_unlisted_url),
trackId).show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG); new long[] { trackId })
.show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG);
} else { } else {
ConfirmDialogFragment.newInstance(R.string.confirm_share_file_key, ConfirmDialogFragment.newInstance(R.string.confirm_share_file_key,
PreferencesUtils.CONFIRM_SHARE_FILE_DEFAULT, PreferencesUtils.CONFIRM_SHARE_FILE_DEFAULT,
getString(R.string.share_track_file_confirm_message), trackId) getString(R.string.share_track_file_confirm_message), new long[] { trackId })
.show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG); .show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG);
} }
} }
@Override @Override
public void onConfirmDone(int confirmId, long trackId) { public void onConfirmDone(int confirmId, long[] trackIds) {
Intent intent;
SendRequest newRequest; SendRequest newRequest;
switch (confirmId) { switch (confirmId) {
case R.string.confirm_play_earth_key:
AnalyticsUtils.sendPageViews(this, "/action/play");
intent = IntentUtils.newIntent(this, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_TRACK_IDS, trackIds)
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) TrackFileFormat.KML)
.putExtra(SaveActivity.EXTRA_PLAY_TRACK, true);
startActivity(intent);
break;
case R.string.confirm_share_drive_key: case R.string.confirm_share_drive_key:
AnalyticsUtils.sendPageViews(this, "/action/share_drive"); AnalyticsUtils.sendPageViews(this, "/action/share_drive");
newRequest = new SendRequest(trackId); newRequest = new SendRequest(trackIds[0]);
newRequest.setSendDrive(true); newRequest.setSendDrive(true);
newRequest.setDriveShare(true); newRequest.setDriveShare(true);
sendToGoogle(newRequest); sendToGoogle(newRequest);
break; break;
case R.string.confirm_share_maps_key: case R.string.confirm_share_maps_key:
AnalyticsUtils.sendPageViews(this, "/action/share_maps"); AnalyticsUtils.sendPageViews(this, "/action/share_maps");
newRequest = new SendRequest(trackId); newRequest = new SendRequest(trackIds[0]);
newRequest.setSendMaps(true); newRequest.setSendMaps(true);
newRequest.setMapsShare(true); newRequest.setMapsShare(true);
sendToGoogle(newRequest); sendToGoogle(newRequest);
@@ -392,8 +427,8 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
} else { } else {
trackFileFormat = TrackFileFormat.TCX; trackFileFormat = TrackFileFormat.TCX;
} }
Intent intent = IntentUtils.newIntent(this, SaveActivity.class) intent = IntentUtils.newIntent(this, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_TRACK_ID, trackId) .putExtra(SaveActivity.EXTRA_TRACK_IDS, trackIds)
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat) .putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat)
.putExtra(SaveActivity.EXTRA_SHARE_TRACK, true); .putExtra(SaveActivity.EXTRA_SHARE_TRACK, true);
startActivity(intent); startActivity(intent);
@@ -42,7 +42,7 @@ public class AggregatedStatsActivity extends AbstractMyTracksActivity {
@Override @Override
protected int getLayoutResId() { protected int getLayoutResId() {
return R.layout.aggregated_stats; return R.layout.stats;
} }
/** /**
@@ -70,6 +70,7 @@ public class MarkerListActivity extends AbstractMyTracksActivity implements Dele
contextualActionModeCallback = new ContextualActionModeCallback() { contextualActionModeCallback = new ContextualActionModeCallback() {
@Override @Override
public void onPrepare(Menu menu, int[] positions, long[] ids) { public void onPrepare(Menu menu, int[] positions, long[] ids) {
menu.findItem(R.id.list_context_menu_play).setVisible(false);
menu.findItem(R.id.list_context_menu_share).setVisible(false); menu.findItem(R.id.list_context_menu_share).setVisible(false);
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(ids.length == 1); menu.findItem(R.id.list_context_menu_show_on_map).setVisible(ids.length == 1);
menu.findItem(R.id.list_context_menu_edit) menu.findItem(R.id.list_context_menu_edit)
@@ -142,6 +142,9 @@ public class SearchListActivity extends AbstractSendToGoogleActivity
shareWithMe = track.isSharedWithMe(); shareWithMe = track.isSharedWithMe();
markerId = (Long) item.get(MARKER_ID_FIELD); markerId = (Long) item.get(MARKER_ID_FIELD);
} }
// Only one item, the item is a track
menu.findItem(R.id.list_context_menu_play)
.setVisible(positions.length == 1 && markerId == null);
// Only one item, the item is a track, and the item is not a sharedWithMe track // Only one item, the item is a track, and the item is not a sharedWithMe track
menu.findItem(R.id.list_context_menu_share) menu.findItem(R.id.list_context_menu_share)
.setVisible(positions.length == 1 && markerId == null && !shareWithMe); .setVisible(positions.length == 1 && markerId == null && !shareWithMe);
@@ -338,6 +341,9 @@ public class SearchListActivity extends AbstractSendToGoogleActivity
Long markerId = (Long) item.get(MARKER_ID_FIELD); Long markerId = (Long) item.get(MARKER_ID_FIELD);
Intent intent; Intent intent;
switch (itemId) { switch (itemId) {
case R.id.list_context_menu_play:
confirmPlay(new long[] {trackId});
return true;
case R.id.list_context_menu_share: case R.id.list_context_menu_share:
confirmShare(trackId); confirmShare(trackId);
return true; return true;
@@ -24,13 +24,11 @@ import com.google.android.apps.mytracks.content.WaypointCreationRequest;
import com.google.android.apps.mytracks.fragments.ChartFragment; import com.google.android.apps.mytracks.fragments.ChartFragment;
import com.google.android.apps.mytracks.fragments.ChooseUploadServiceDialogFragment; import com.google.android.apps.mytracks.fragments.ChooseUploadServiceDialogFragment;
import com.google.android.apps.mytracks.fragments.ChooseUploadServiceDialogFragment.ChooseUploadServiceCaller; import com.google.android.apps.mytracks.fragments.ChooseUploadServiceDialogFragment.ChooseUploadServiceCaller;
import com.google.android.apps.mytracks.fragments.ConfirmDialogFragment;
import com.google.android.apps.mytracks.fragments.DeleteTrackDialogFragment; import com.google.android.apps.mytracks.fragments.DeleteTrackDialogFragment;
import com.google.android.apps.mytracks.fragments.DeleteTrackDialogFragment.DeleteTrackCaller; import com.google.android.apps.mytracks.fragments.DeleteTrackDialogFragment.DeleteTrackCaller;
import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment; import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment;
import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment.FileTypeCaller; import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment.FileTypeCaller;
import com.google.android.apps.mytracks.fragments.FrequencyDialogFragment; import com.google.android.apps.mytracks.fragments.FrequencyDialogFragment;
import com.google.android.apps.mytracks.fragments.InstallEarthDialogFragment;
import com.google.android.apps.mytracks.fragments.MyTracksMapFragment; import com.google.android.apps.mytracks.fragments.MyTracksMapFragment;
import com.google.android.apps.mytracks.fragments.StatsFragment; import com.google.android.apps.mytracks.fragments.StatsFragment;
import com.google.android.apps.mytracks.io.file.SaveActivity; import com.google.android.apps.mytracks.io.file.SaveActivity;
@@ -49,8 +47,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.v4.app.TaskStackBuilder; import android.support.v4.app.TaskStackBuilder;
@@ -64,7 +60,6 @@ import android.view.View.OnClickListener;
import android.widget.TabHost; import android.widget.TabHost;
import android.widget.TabHost.TabSpec; import android.widget.TabHost.TabSpec;
import java.util.List;
import java.util.Locale; import java.util.Locale;
/** /**
@@ -319,7 +314,7 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency); splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency);
feedbackMenuItem = menu.findItem(R.id.track_detail_split_frequency); feedbackMenuItem = menu.findItem(R.id.track_detail_split_frequency);
feedbackMenuItem.setVisible(GoogleFeedbackUtils.isAvailable(this)); feedbackMenuItem.setVisible(GoogleFeedbackUtils.isAvailable(this));
updateMenuItems(trackId == recordingTrackId, recordingTrackPaused); updateMenuItems(trackId == recordingTrackId, recordingTrackPaused);
return true; return true;
} }
@@ -344,18 +339,10 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.track_detail_play: case R.id.track_detail_play:
if (isEarthInstalled()) { confirmPlay(new long[] {trackId});
ConfirmDialogFragment.newInstance(R.string.confirm_play_earth_key,
PreferencesUtils.CONFIRM_PLAY_EARTH_DEFAULT,
getString(R.string.track_detail_play_confirm_message), trackId)
.show(getSupportFragmentManager(), ConfirmDialogFragment.CONFIRM_DIALOG_TAG);
} else {
new InstallEarthDialogFragment().show(
getSupportFragmentManager(), InstallEarthDialogFragment.INSTALL_EARTH_DIALOG_TAG);
}
return true; return true;
case R.id.track_detail_share: case R.id.track_detail_share:
confirmShare(trackId); confirmShare(trackId);
return true; return true;
case R.id.track_detail_markers: case R.id.track_detail_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class) intent = IntentUtils.newIntent(this, MarkerListActivity.class)
@@ -384,7 +371,7 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
FileTypeDialogFragment.newInstance(R.id.track_detail_save, R.string.save_selection_title, FileTypeDialogFragment.newInstance(R.id.track_detail_save, R.string.save_selection_title,
R.string.save_selection_option, 4) R.string.save_selection_option, 4)
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG); .show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
return true; return true;
case R.id.track_detail_edit: case R.id.track_detail_edit:
intent = IntentUtils.newIntent(this, TrackEditActivity.class) intent = IntentUtils.newIntent(this, TrackEditActivity.class)
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId); .putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
@@ -426,22 +413,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
return super.onTrackballEvent(event); return super.onTrackballEvent(event);
} }
@Override
public void onConfirmDone(int confirmId, long confirmTrackId) {
switch (confirmId) {
case R.string.confirm_play_earth_key:
AnalyticsUtils.sendPageViews(this, "/action/play");
Intent intent = IntentUtils.newIntent(this, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_TRACK_ID, confirmTrackId)
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) TrackFileFormat.KML)
.putExtra(SaveActivity.EXTRA_PLAY_TRACK, true);
startActivity(intent);
break;
default:
super.onConfirmDone(confirmId, confirmTrackId);
}
}
@Override @Override
public void onChooseUploadServiceDone(boolean sendDrive, boolean sendMaps, public void onChooseUploadServiceDone(boolean sendDrive, boolean sendMaps,
boolean sendFusionTables, boolean sendSpreadsheets, boolean mapsExistingMap) { boolean sendFusionTables, boolean sendSpreadsheets, boolean mapsExistingMap) {
@@ -480,7 +451,7 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
} }
}); });
} }
@Override @Override
public void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat) { public void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat) {
switch (menuId) { switch (menuId) {
@@ -488,7 +459,7 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
AnalyticsUtils.sendPageViews( AnalyticsUtils.sendPageViews(
this, "/action/save_" + trackFileFormat.name().toLowerCase(Locale.US)); this, "/action/save_" + trackFileFormat.name().toLowerCase(Locale.US));
Intent intent = IntentUtils.newIntent(this, SaveActivity.class) Intent intent = IntentUtils.newIntent(this, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_TRACK_ID, trackId) .putExtra(SaveActivity.EXTRA_TRACK_IDS, new long[] {trackId})
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat); .putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat);
startActivity(intent); startActivity(intent);
break; break;
@@ -590,20 +561,4 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
} }
setTitle(title); setTitle(title);
} }
/**
* Returns true if Google Earth app is installed.
*/
private boolean isEarthInstalled() {
List<ResolveInfo> infos = getPackageManager().queryIntentActivities(
new Intent().setType(SaveActivity.GOOGLE_EARTH_KML_MIME_TYPE),
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : infos) {
if (info.activityInfo != null && info.activityInfo.packageName != null
&& info.activityInfo.packageName.equals(SaveActivity.GOOGLE_EARTH_PACKAGE)) {
return true;
}
}
return false;
}
} }
@@ -366,13 +366,13 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
ApiAdapterFactory.getApiAdapter().enableStrictMode(); ApiAdapterFactory.getApiAdapter().enableStrictMode();
} }
Intent intent = new Intent(this, RemoveTempFilesService.class); Intent intent = new Intent(this, RemoveTempFilesService.class);
startService(intent); startService(intent);
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this); myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this);
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE); sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
@@ -385,13 +385,13 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
// Show trackController when search dialog is dismissed // Show trackController when search dialog is dismissed
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
searchManager.setOnDismissListener(new SearchManager.OnDismissListener() { searchManager.setOnDismissListener(new SearchManager.OnDismissListener() {
@Override @Override
public void onDismiss() { public void onDismiss() {
trackController.show(); trackController.show();
} }
}); });
listView = (ListView) findViewById(R.id.track_list); listView = (ListView) findViewById(R.id.track_list);
listView.setEmptyView(findViewById(R.id.track_list_empty_view)); listView.setEmptyView(findViewById(R.id.track_list_empty_view));
listView.setOnItemClickListener(new OnItemClickListener() { listView.setOnItemClickListener(new OnItemClickListener() {
@@ -415,7 +415,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME); int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION); int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION);
int sharedOwnerIndex = cursor.getColumnIndex(TracksColumns.SHAREDOWNER); int sharedOwnerIndex = cursor.getColumnIndex(TracksColumns.SHAREDOWNER);
boolean isRecording = cursor.getLong(idIndex) == recordingTrackId; boolean isRecording = cursor.getLong(idIndex) == recordingTrackId;
String icon = cursor.getString(iconIndex); String icon = cursor.getString(iconIndex);
int iconId = TrackIconUtils.getIconDrawable(icon); int iconId = TrackIconUtils.getIconDrawable(icon);
@@ -540,13 +540,13 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
@Override @Override
protected boolean configureActionBarHomeAsUp() { protected boolean configureActionBarHomeAsUp() {
return false; return false;
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_list, menu); getMenuInflater().inflate(R.menu.track_list, menu);
searchMenuItem = menu.findItem(R.id.track_list_search); searchMenuItem = menu.findItem(R.id.track_list_search);
startGpsMenuItem = menu.findItem(R.id.track_list_start_gps); startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
importAllMenuItem = menu.findItem(R.id.track_list_import_all); importAllMenuItem = menu.findItem(R.id.track_list_import_all);
@@ -620,7 +620,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
@@ -649,7 +649,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
} }
return super.onKeyUp(keyCode, event); return super.onKeyUp(keyCode, event);
} }
@Override @Override
public boolean onSearchRequested() { public boolean onSearchRequested() {
// Hide trackController when search dialog is shown // Hide trackController when search dialog is shown
@@ -666,7 +666,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
public void onDeleteTrackDone() { public void onDeleteTrackDone() {
// Do nothing // Do nothing
} }
@Override @Override
public void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat) { public void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat) {
Intent intent; Intent intent;
@@ -675,6 +675,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
AnalyticsUtils.sendPageViews( AnalyticsUtils.sendPageViews(
this, "/action/save_all_" + trackFileFormat.name().toLowerCase(Locale.US)); this, "/action/save_all_" + trackFileFormat.name().toLowerCase(Locale.US));
intent = IntentUtils.newIntent(this, SaveActivity.class) intent = IntentUtils.newIntent(this, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_TRACK_IDS, new long[] {-1L})
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat); .putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat);
startActivity(intent); startActivity(intent);
break; break;
@@ -718,7 +719,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
showStartupDialogs(); showStartupDialogs();
return; return;
} }
finish(); finish();
} }
private void checkGooglePlayServices() { private void checkGooglePlayServices() {
@@ -812,6 +813,9 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
*/ */
private boolean handleContextItem(int itemId, long[] trackIds) { private boolean handleContextItem(int itemId, long[] trackIds) {
switch (itemId) { switch (itemId) {
case R.id.list_context_menu_play:
confirmPlay(trackIds);
return true;
case R.id.list_context_menu_share: case R.id.list_context_menu_share:
confirmShare(trackIds[0]); confirmShare(trackIds[0]);
return true; return true;
@@ -47,7 +47,7 @@ public class ConfirmDialogFragment extends DialogFragment {
/** /**
* Called when confirm is done. * Called when confirm is done.
*/ */
public void onConfirmDone(int confirmId, long trackId); public void onConfirmDone(int confirmId, long[] trackIds);
} }
public static final String CONFIRM_DIALOG_TAG = "confirmDialog"; public static final String CONFIRM_DIALOG_TAG = "confirmDialog";
@@ -55,17 +55,17 @@ public class ConfirmDialogFragment extends DialogFragment {
private static final String KEY_CONFIRM_ID = "confirmId"; private static final String KEY_CONFIRM_ID = "confirmId";
private static final String KEY_DEFAULT_VALUE = "defaultValue"; private static final String KEY_DEFAULT_VALUE = "defaultValue";
private static final String KEY_MESSAGE = "message"; private static final String KEY_MESSAGE = "message";
private static final String KEY_TRACK_ID = "trackId"; private static final String KEY_TRACK_IDS = "trackIds";
private CheckBox checkBox; private CheckBox checkBox;
public static ConfirmDialogFragment newInstance( public static ConfirmDialogFragment newInstance(
int confirmId, boolean defaultValue, CharSequence message, long trackId) { int confirmId, boolean defaultValue, CharSequence message, long[] trackIds) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putInt(KEY_CONFIRM_ID, confirmId); bundle.putInt(KEY_CONFIRM_ID, confirmId);
bundle.putBoolean(KEY_DEFAULT_VALUE, defaultValue); bundle.putBoolean(KEY_DEFAULT_VALUE, defaultValue);
bundle.putCharSequence(KEY_MESSAGE, message); bundle.putCharSequence(KEY_MESSAGE, message);
bundle.putLong(KEY_TRACK_ID, trackId); bundle.putLongArray(KEY_TRACK_IDS, trackIds);
ConfirmDialogFragment confirmDialogFragment = new ConfirmDialogFragment(); ConfirmDialogFragment confirmDialogFragment = new ConfirmDialogFragment();
confirmDialogFragment.setArguments(bundle); confirmDialogFragment.setArguments(bundle);
@@ -93,9 +93,9 @@ public class ConfirmDialogFragment extends DialogFragment {
boolean defaultValue = getArguments().getBoolean(KEY_DEFAULT_VALUE); boolean defaultValue = getArguments().getBoolean(KEY_DEFAULT_VALUE);
fragmentActivity = getActivity(); fragmentActivity = getActivity();
if (!PreferencesUtils.getBoolean(fragmentActivity, confirmId, defaultValue)) { if (!PreferencesUtils.getBoolean(fragmentActivity, confirmId, defaultValue)) {
long trackId = getArguments().getLong(KEY_TRACK_ID); long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
dismiss(); dismiss();
caller.onConfirmDone(confirmId, trackId); caller.onConfirmDone(confirmId, trackIds);
} }
} }
@@ -106,15 +106,14 @@ public class ConfirmDialogFragment extends DialogFragment {
textView.setText(getArguments().getCharSequence(KEY_MESSAGE)); textView.setText(getArguments().getCharSequence(KEY_MESSAGE));
checkBox = (CheckBox) view.findViewById(R.id.confirm_dialog_check_box); checkBox = (CheckBox) view.findViewById(R.id.confirm_dialog_check_box);
return new AlertDialog.Builder(fragmentActivity).setNegativeButton( return new AlertDialog.Builder(fragmentActivity).setNegativeButton(R.string.generic_no, null)
R.string.generic_no, null)
.setPositiveButton(R.string.generic_yes, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.generic_yes, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
int confirmId = getArguments().getInt(KEY_CONFIRM_ID); int confirmId = getArguments().getInt(KEY_CONFIRM_ID);
long trackId = getArguments().getLong(KEY_TRACK_ID); long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
PreferencesUtils.setBoolean(fragmentActivity, confirmId, !checkBox.isChecked()); PreferencesUtils.setBoolean(fragmentActivity, confirmId, !checkBox.isChecked());
caller.onConfirmDone(confirmId, trackId); caller.onConfirmDone(confirmId, trackIds);
} }
}).setTitle(R.string.generic_confirm_title).setView(view).create(); }).setTitle(R.string.generic_confirm_title).setView(view).create();
} }
@@ -87,6 +87,13 @@ public class EnableSyncDialogFragment extends DialogFragment {
}).setPositiveButton(R.string.generic_yes, new OnClickListener() { }).setPositiveButton(R.string.generic_yes, new OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
/*
* google_account_key can be set from the android backup service. If
* the user wants to enable sync, make sure google_account_key is
* cleared so the user can choose an account.
*/
PreferencesUtils.setString(fragmentActivity, R.string.google_account_key,
PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
caller.onEnableSyncDone(true); caller.onEnableSyncDone(true);
} }
}).setTitle(R.string.enable_sync_title).create(); }).setTitle(R.string.enable_sync_title).create();
@@ -16,7 +16,7 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.io.file.SaveActivity; import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.AlertDialog; import android.app.AlertDialog;
@@ -32,7 +32,7 @@ import android.widget.Toast;
/** /**
* A DialogFragment to install Google Earth. * A DialogFragment to install Google Earth.
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public class InstallEarthDialogFragment extends DialogFragment { public class InstallEarthDialogFragment extends DialogFragment {
@@ -40,17 +40,17 @@ public class InstallEarthDialogFragment extends DialogFragment {
public static final String INSTALL_EARTH_DIALOG_TAG = "installEarthDialog"; public static final String INSTALL_EARTH_DIALOG_TAG = "installEarthDialog";
private FragmentActivity fragmentActivity; private FragmentActivity fragmentActivity;
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
fragmentActivity = getActivity(); fragmentActivity = getActivity();
return new AlertDialog.Builder(fragmentActivity) return new AlertDialog.Builder(fragmentActivity).setMessage(
.setMessage(R.string.track_detail_install_earth_message) R.string.track_detail_install_earth_message).setNegativeButton(R.string.generic_no, null)
.setNegativeButton(R.string.generic_no, null)
.setPositiveButton(R.string.generic_yes, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.generic_yes, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent().setData(Uri.parse(SaveActivity.GOOGLE_EARTH_MARKET_URL)); Intent intent = new Intent().setData(
Uri.parse(GoogleEarthUtils.GOOGLE_EARTH_MARKET_URL));
try { try {
startActivity(intent); startActivity(intent);
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
@@ -59,8 +59,6 @@ public class InstallEarthDialogFragment extends DialogFragment {
.show(); .show();
} }
} }
}) }).setTitle(R.string.generic_confirm_title).create();
.setTitle(R.string.generic_confirm_title)
.create();
} }
} }
@@ -102,7 +102,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
public void run() { public void run() {
if (isResumed()) { if (isResumed()) {
lastLocation = null; lastLocation = null;
StatsUtils.setLocationValues(getActivity(), lastLocation, true); StatsUtils.setLocationValues(getActivity(), lastLocation, isSelectedTrackRecording());
} }
} }
}); });
@@ -118,11 +118,12 @@ public class StatsFragment extends Fragment implements TrackDataListener {
if (isResumed()) { if (isResumed()) {
if (isSelectedTrackRecording() && !isSelectedTrackPaused()) { if (isSelectedTrackRecording() && !isSelectedTrackPaused()) {
lastLocation = location; lastLocation = location;
StatsUtils.setLocationValues(getActivity(), location, true); StatsUtils.setLocationValues(getActivity(), location, isSelectedTrackRecording());
} else { } else {
if (lastLocation != null) { if (lastLocation != null) {
lastLocation = null; lastLocation = null;
StatsUtils.setLocationValues(getActivity(), lastLocation, true); StatsUtils.setLocationValues(
getActivity(), lastLocation, isSelectedTrackRecording());
} }
} }
} }
@@ -277,6 +278,6 @@ public class StatsFragment extends Fragment implements TrackDataListener {
*/ */
private void updateUi(FragmentActivity activity) { private void updateUi(FragmentActivity activity) {
StatsUtils.setTripStatisticsValues(activity, lastTripStatistics); StatsUtils.setTripStatisticsValues(activity, lastTripStatistics);
StatsUtils.setLocationValues(activity, lastLocation, true); StatsUtils.setLocationValues(activity, lastLocation, isSelectedTrackRecording());
} }
} }
@@ -58,7 +58,6 @@ public class CsvTrackWriter implements TrackFormatWriter {
private final Context context; private final Context context;
private PrintWriter printWriter; private PrintWriter printWriter;
private Track track;
private int segmentIndex; private int segmentIndex;
private int pointIndex; private int pointIndex;
@@ -72,8 +71,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void prepare(Track aTrack, OutputStream out) { public void prepare(OutputStream out) {
track = aTrack;
printWriter = new PrintWriter(out); printWriter = new PrintWriter(out);
segmentIndex = 0; segmentIndex = 0;
pointIndex = 0; pointIndex = 0;
@@ -85,7 +83,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeHeader() { public void writeHeader(Track track) {
writeCommaSeparatedLine(context.getString(R.string.generic_name), writeCommaSeparatedLine(context.getString(R.string.generic_name),
context.getString(R.string.track_edit_activity_type_hint), context.getString(R.string.track_edit_activity_type_hint),
context.getString(R.string.generic_description)); context.getString(R.string.generic_description));
@@ -133,7 +131,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeBeginTrack(Location firstPoint) { public void writeBeginTrack(Track track, Location firstPoint) {
writeCommaSeparatedLine(context.getString(R.string.description_track_segment), writeCommaSeparatedLine(context.getString(R.string.description_track_segment),
context.getString(R.string.description_track_point), context.getString(R.string.description_track_point),
context.getString(R.string.description_location_latitude), context.getString(R.string.description_location_latitude),
@@ -149,7 +147,7 @@ public class CsvTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeEndTrack(Location lastPoint) { public void writeEndTrack(Track track, Location lastPoint) {
// Do nothing // Do nothing
} }
@@ -52,7 +52,6 @@ public class GpxTrackWriter implements TrackFormatWriter {
} }
private final Context context; private final Context context;
private Track track;
private PrintWriter printWriter; private PrintWriter printWriter;
public GpxTrackWriter(Context context) { public GpxTrackWriter(Context context) {
@@ -65,8 +64,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void prepare(Track aTrack, OutputStream outputStream) { public void prepare(OutputStream outputStream) {
this.track = aTrack;
this.printWriter = new PrintWriter(outputStream); this.printWriter = new PrintWriter(outputStream);
} }
@@ -79,7 +77,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeHeader() { public void writeHeader(Track track) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
printWriter.println("<gpx"); printWriter.println("<gpx");
@@ -109,7 +107,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeBeginTrack(Location firstLocation) { public void writeBeginTrack(Track track, Location firstLocation) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("<trk>"); printWriter.println("<trk>");
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>"); printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
@@ -120,7 +118,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeEndTrack(Location lastLocation) { public void writeEndTrack(Track track, Location lastLocation) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("</trk>"); printWriter.println("</trk>");
} }
@@ -24,6 +24,7 @@ import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
@@ -42,11 +43,6 @@ import java.util.ArrayList;
*/ */
public class KmlTrackWriter implements TrackFormatWriter { public class KmlTrackWriter implements TrackFormatWriter {
/**
* ID of the KML feature to play a tour.
*/
public static final String TOUR_FEATURE_ID = "tour";
private static final String WAYPOINT_STYLE = "waypoint"; private static final String WAYPOINT_STYLE = "waypoint";
private static final String STATISTICS_STYLE = "statistics"; private static final String STATISTICS_STYLE = "statistics";
private static final String START_STYLE = "start"; private static final String START_STYLE = "start";
@@ -70,7 +66,6 @@ public class KmlTrackWriter implements TrackFormatWriter {
private final Context context; private final Context context;
private final DescriptionGenerator descriptionGenerator; private final DescriptionGenerator descriptionGenerator;
private Track track;
private PrintWriter printWriter; private PrintWriter printWriter;
private ArrayList<Integer> powerList = new ArrayList<Integer>(); private ArrayList<Integer> powerList = new ArrayList<Integer>();
private ArrayList<Integer> cadenceList = new ArrayList<Integer>(); private ArrayList<Integer> cadenceList = new ArrayList<Integer>();
@@ -95,8 +90,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void prepare(Track aTrack, OutputStream outputStream) { public void prepare(OutputStream outputStream) {
this.track = aTrack;
this.printWriter = new PrintWriter(outputStream); this.printWriter = new PrintWriter(outputStream);
} }
@@ -109,12 +103,12 @@ public class KmlTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeHeader() { public void writeHeader(Track track) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\""); printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
printWriter.println("xmlns:gx=\"http://www.google.com/kml/ext/2.2\""); printWriter.println("xmlns:gx=\"http://www.google.com/kml/ext/2.2\"");
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\">"); printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\">");
printWriter.println("<Document>"); printWriter.println("<Document>");
printWriter.println("<open>1</open>"); printWriter.println("<open>1</open>");
printWriter.println("<visibility>1</visibility>"); printWriter.println("<visibility>1</visibility>");
@@ -170,11 +164,11 @@ public class KmlTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeBeginTrack(Location firstLocation) { public void writeBeginTrack(Track track, Location firstLocation) {
if (printWriter != null) { if (printWriter != null) {
String name = context.getString(R.string.marker_label_start, track.getName()); String name = context.getString(R.string.marker_label_start, track.getName());
writePlacemark(name, "", "", START_STYLE, firstLocation); writePlacemark(name, "", "", START_STYLE, firstLocation);
printWriter.println("<Placemark id=\"" + TOUR_FEATURE_ID + "\">"); printWriter.println("<Placemark id=\"" + GoogleEarthUtils.TOUR_FEATURE_ID_VALUE + "\">");
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>"); printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
printWriter.println( printWriter.println(
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>"); "<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
@@ -187,7 +181,7 @@ public class KmlTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeEndTrack(Location lastLocation) { public void writeEndTrack(Track track, Location lastLocation) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("</gx:MultiTrack>"); printWriter.println("</gx:MultiTrack>");
printWriter.println("</Placemark>"); printWriter.println("</Placemark>");
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.io.file;
import com.google.android.apps.mytracks.util.DialogUtils; import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.FileUtils; import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.apps.mytracks.util.IntentUtils; import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
@@ -27,7 +28,6 @@ import android.app.Dialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Toast; import android.widget.Toast;
@@ -42,23 +42,15 @@ import java.io.File;
public class SaveActivity extends Activity { public class SaveActivity extends Activity {
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format"; public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
public static final String EXTRA_TRACK_ID = "track_id"; public static final String EXTRA_TRACK_IDS = "track_ids";
public static final String EXTRA_PLAY_TRACK = "play_track"; public static final String EXTRA_PLAY_TRACK = "play_track";
public static final String EXTRA_SHARE_TRACK = "share_track"; public static final String EXTRA_SHARE_TRACK = "share_track";
public static final String GOOGLE_EARTH_KML_MIME_TYPE = "application/vnd.google-earth.kml+xml";
public static final String GOOGLE_EARTH_PACKAGE = "com.google.earth";
public static final String GOOGLE_EARTH_MARKET_URL = "market://details?id="
+ GOOGLE_EARTH_PACKAGE;
private static final String
GOOGLE_EARTH_TOUR_FEATURE_ID = "com.google.earth.EXTRA.tour_feature_id";
private static final String GOOGLE_EARTH_CLASS = "com.google.earth.EarthActivity";
private static final int DIALOG_PROGRESS_ID = 0; private static final int DIALOG_PROGRESS_ID = 0;
private static final int DIALOG_RESULT_ID = 1; private static final int DIALOG_RESULT_ID = 1;
private TrackFileFormat trackFileFormat; private TrackFileFormat trackFileFormat;
private long trackId; private long[] trackIds;
private boolean playTrack; private boolean playTrack;
private boolean shareTrack; private boolean shareTrack;
private String directoryName; private String directoryName;
@@ -78,7 +70,7 @@ public class SaveActivity extends Activity {
Intent intent = getIntent(); Intent intent = getIntent();
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT); trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L); trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false); playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
shareTrack = intent.getBooleanExtra(EXTRA_SHARE_TRACK, false); shareTrack = intent.getBooleanExtra(EXTRA_SHARE_TRACK, false);
@@ -104,7 +96,7 @@ public class SaveActivity extends Activity {
saveAsyncTask = (SaveAsyncTask) retained; saveAsyncTask = (SaveAsyncTask) retained;
saveAsyncTask.setActivity(this); saveAsyncTask.setActivity(this);
} else { } else {
saveAsyncTask = new SaveAsyncTask(this, trackFileFormat, trackId, directory); saveAsyncTask = new SaveAsyncTask(this, trackFileFormat, trackIds, directory);
saveAsyncTask.execute(); saveAsyncTask.execute();
} }
} }
@@ -175,23 +167,17 @@ public class SaveActivity extends Activity {
removeDialog(DIALOG_PROGRESS_ID); removeDialog(DIALOG_PROGRESS_ID);
if (successCount == 1 && totalCount == 1 && savedPath != null) { if (successCount == 1 && totalCount == 1 && savedPath != null) {
if (playTrack) { if (playTrack) {
Intent intent = new Intent().addFlags( startActivity(GoogleEarthUtils.getPlayInEarthIntent(savedPath));
Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(GOOGLE_EARTH_TOUR_FEATURE_ID, KmlTrackWriter.TOUR_FEATURE_ID)
.setClassName(GOOGLE_EARTH_PACKAGE, GOOGLE_EARTH_CLASS)
.setDataAndType(Uri.fromFile(new File(savedPath)), GOOGLE_EARTH_KML_MIME_TYPE);
startActivity(intent);
finish(); finish();
return; return;
} else if (shareTrack) { } else if (shareTrack) {
Intent intent = IntentUtils.newShareFileIntent(this, trackId, savedPath, trackFileFormat); Intent intent = IntentUtils.newShareFileIntent(this, trackIds[0], savedPath, trackFileFormat);
startActivity( startActivity(Intent.createChooser(intent, getString(R.string.share_track_picker_title)));
Intent.createChooser(intent, getString(R.string.share_track_picker_title)));
finish(); finish();
return; return;
} }
} }
showDialog(DIALOG_RESULT_ID); showDialog(DIALOG_RESULT_ID);
} }
/** /**
@@ -46,7 +46,7 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private SaveActivity saveActivity; private SaveActivity saveActivity;
private final TrackFileFormat trackFileFormat; private final TrackFileFormat trackFileFormat;
private final long trackId; private final long[] trackIds;
private final File directory; private final File directory;
private final Context context; private final Context context;
private final MyTracksProviderUtils myTracksProviderUtils; private final MyTracksProviderUtils myTracksProviderUtils;
@@ -75,10 +75,10 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
* @param directory the directory to save to * @param directory the directory to save to
*/ */
public SaveAsyncTask( public SaveAsyncTask(
SaveActivity saveActivity, TrackFileFormat trackFileFormat, long trackId, File directory) { SaveActivity saveActivity, TrackFileFormat trackFileFormat, long[] trackIds, File directory) {
this.saveActivity = saveActivity; this.saveActivity = saveActivity;
this.trackFileFormat = trackFileFormat; this.trackFileFormat = trackFileFormat;
this.trackId = trackId; this.trackIds = trackIds;
this.directory = directory; this.directory = directory;
context = saveActivity.getApplicationContext(); context = saveActivity.getApplicationContext();
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context); myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
@@ -118,16 +118,24 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
@Override @Override
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
try { try {
if (trackId != -1L) { if (trackIds.length == 1 && trackIds[0] == -1L) {
return saveAllTracks();
} else {
totalCount = 1; totalCount = 1;
if (saveOneTrack(trackId)) { Track[] tracks = new Track[trackIds.length];
for (int i = 0; i < trackIds.length; i++) {
tracks[i] = myTracksProviderUtils.getTrack(trackIds[i]);
if (tracks[i] == null) {
Log.d(TAG, "No track for " + trackIds[i]);
return false;
}
}
if (saveTracks(tracks)) {
successCount = 1; successCount = 1;
return true; return true;
} else { } else {
return false; return false;
} }
} else {
return saveAllTracks();
} }
} finally { } finally {
// Release the wake lock if obtained // Release the wake lock if obtained
@@ -160,17 +168,15 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
} }
/** /**
* Saves one track. * Saves tracks to one file.
* *
* @param id the track id * @param tracks the tracks
*/ */
private Boolean saveOneTrack(long id) { private Boolean saveTracks(Track[] tracks) {
Track track = myTracksProviderUtils.getTrack(id); if (tracks.length == 0) {
if (track == null) {
Log.d(TAG, "No track for " + id);
return false; return false;
} }
Track track = tracks[0];
// Make sure the file doesn't exist yet (possibly by changing the filename) // Make sure the file doesn't exist yet (possibly by changing the filename)
String fileName = FileUtils.buildUniqueFileName( String fileName = FileUtils.buildUniqueFileName(
directory, track.getName(), trackFileFormat.getExtension()); directory, track.getName(), trackFileFormat.getExtension());
@@ -178,21 +184,21 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
Log.d(TAG, "Unable to get a unique filename for " + track.getName()); Log.d(TAG, "Unable to get a unique filename for " + track.getName());
return false; return false;
} }
trackWriter = new TrackWriter( trackWriter = new TrackWriter(
context, myTracksProviderUtils, track, trackFileFormat, new TrackWriter.OnWriteListener() { context, myTracksProviderUtils, tracks, trackFileFormat, new TrackWriter.OnWriteListener() {
@Override @Override
public void onWrite(int number, int max) { public void onWrite(int number, int max) {
/* /*
* If only saving one track, update the progress dialog once every * If only saving one track, update the progress dialog once every
* 500 points * 500 points
*/ */
if (trackId != -1L && number % 500 == 0) { if (trackIds.length == 1 && trackIds[0] != -1L && number % 500 == 0) {
publishProgress(number, max); publishProgress(number, max);
} }
} }
}); });
File file = null; File file = null;
try { try {
file = new File(directory, fileName); file = new File(directory, fileName);
@@ -201,8 +207,8 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Log.d(TAG, "File not found " + fileName, e); Log.d(TAG, "File not found " + fileName, e);
return false; return false;
} }
if (trackWriter.wasSuccess()) { if (trackWriter.wasSuccess()) {
savedPath = file.getAbsolutePath(); savedPath = file.getAbsolutePath();
} else { } else {
@@ -224,14 +230,13 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
return false; return false;
} }
totalCount = cursor.getCount(); totalCount = cursor.getCount();
int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID);
for (int i = 0; i < totalCount; i++) { for (int i = 0; i < totalCount; i++) {
if (isCancelled()) { if (isCancelled()) {
return false; return false;
} }
cursor.moveToPosition(i); cursor.moveToPosition(i);
long id = cursor.getLong(idIndex); Track track = myTracksProviderUtils.createTrack(cursor);
if (saveOneTrack(id)) { if (track != null && saveTracks(new Track[] { track })) {
successCount++; successCount++;
} }
publishProgress(i + 1, totalCount); publishProgress(i + 1, totalCount);
@@ -84,7 +84,6 @@ public class TcxTrackWriter implements TrackFormatWriter {
R.string.activity_type_walking }; R.string.activity_type_walking };
private final Context context; private final Context context;
private Track track;
private PrintWriter printWriter; private PrintWriter printWriter;
private SportType sportType; private SportType sportType;
@@ -93,10 +92,8 @@ public class TcxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void prepare(Track aTrack, OutputStream out) { public void prepare(OutputStream out) {
this.track = aTrack;
this.printWriter = new PrintWriter(out); this.printWriter = new PrintWriter(out);
this.sportType = getSportType(track.getCategory());
} }
@Override @Override
@@ -113,7 +110,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeHeader() { public void writeHeader(Track track) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
printWriter.println("<TrainingCenterDatabase" printWriter.println("<TrainingCenterDatabase"
@@ -144,7 +141,8 @@ public class TcxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeBeginTrack(Location firstPoint) { public void writeBeginTrack(Track track, Location firstPoint) {
sportType = getSportType(track.getCategory());
if (printWriter != null) { if (printWriter != null) {
String startTime = StringUtils.formatDateTimeIso8601( String startTime = StringUtils.formatDateTimeIso8601(
track.getTripStatistics().getStartTime()); track.getTripStatistics().getStartTime());
@@ -165,7 +163,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
} }
@Override @Override
public void writeEndTrack(Location lastPoint) { public void writeEndTrack(Track track, Location lastPoint) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("</Lap>"); printWriter.println("</Lap>");
printWriter.println("<Notes>" + StringUtils.formatCData(track.getDescription()) + "</Notes>"); printWriter.println("<Notes>" + StringUtils.formatCData(track.getDescription()) + "</Notes>");
@@ -23,26 +23,30 @@ import android.location.Location;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* Interface for writing a track to file. * Interface for writing tracks to a file.
* *
* The expected sequence of calls is: * The expected sequence of calls is:
* <ol> * <ul>
* <li>{@link #prepare} * <li>{@link #prepare}</li>
* <li>{@link #writeHeader} * <li>{@link #writeHeader}</li>
* <li>{@link #writeBeginWaypoints} * <li>For each track:</li>
* <li>For each waypoint: {@link #writeWaypoint} * <ul>
* <li>{@link #writeEndWaypoints} * <li>{@link #writeBeginWaypoints}
* <li>{@link #writeBeginTrack} * <li>For each waypoint: {@link #writeWaypoint}
* <li>For each segment: * <li>{@link #writeEndWaypoints}
* <ol> * <li>{@link #writeBeginTrack}
* <li>{@link #writeOpenSegment} * <li>For each segment:
* <li>For each location in the segment: {@link #writeLocation} * <ul>
* <li>{@link #writeCloseSegment} * <li>{@link #writeOpenSegment}</li>
* </ol> * <li>For each location in the segment: {@link #writeLocation}</li>
* <li>{@link #writeEndTrack} * <li>{@link #writeCloseSegment}</li>
* <li>{@link #writeFooter} * </ul>
* <li>{@link #close} * </li>
* </ol> * <li>{@link #writeEndTrack}</li>
* </ul>
* <li>{@link #writeFooter}</li>
* <li>{@link #close}</li>
* </ul>
* *
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
@@ -54,12 +58,11 @@ public interface TrackFormatWriter {
public String getExtension(); public String getExtension();
/** /**
* Sets up the writer to write the given track. * Sets up the file handler.
* *
* @param track the track to write * @param outputStream the output stream for the file handler
* @param outputStream the output stream to write the track to
*/ */
public void prepare(Track track, OutputStream outputStream); public void prepare(OutputStream outputStream);
/** /**
* Closes the underlying file handler. * Closes the underlying file handler.
@@ -67,9 +70,11 @@ public interface TrackFormatWriter {
public void close(); public void close();
/** /**
* Writes the header. * Writes the header for a file
*
* @param track the track
*/ */
public void writeHeader(); public void writeHeader(Track track);
/** /**
* Writes the footer. * Writes the footer.
@@ -96,16 +101,18 @@ public interface TrackFormatWriter {
/** /**
* Writes the beginning of the track. * Writes the beginning of the track.
* *
* @param track the track
* @param firstLocation the first location * @param firstLocation the first location
*/ */
public void writeBeginTrack(Location firstLocation); public void writeBeginTrack(Track track, Location firstLocation);
/** /**
* Writes the end of the track. * Writes the end of the track.
* *
* @param track the track
* @param lastLocation the last location * @param lastLocation the last location
*/ */
public void writeEndTrack(Location lastLocation); public void writeEndTrack(Track track, Location lastLocation);
/** /**
* Writes the statements necessary to open a new segment. * Writes the statements necessary to open a new segment.
@@ -33,7 +33,7 @@ import android.util.Log;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* Track Writer for writing a track to an {@link OutputStream}. * Track Writer for writing tracks to an {@link OutputStream}.
* *
* @author Sandor Dornbush * @author Sandor Dornbush
* @author Rodrigo Damazio * @author Rodrigo Damazio
@@ -49,7 +49,7 @@ public class TrackWriter {
* When a track location is written. * When a track location is written.
* *
* @param number the location number * @param number the location number
* @param max the maximum number of locations, for calculation of completion * @param max the maximum number of locations in a track, for calculation of completion
* percentage * percentage
*/ */
public void onWrite(int number, int max); public void onWrite(int number, int max);
@@ -58,7 +58,7 @@ public class TrackWriter {
private final static String TAG = TrackWriter.class.getSimpleName(); private final static String TAG = TrackWriter.class.getSimpleName();
private final MyTracksProviderUtils myTracksProviderUtils; private final MyTracksProviderUtils myTracksProviderUtils;
private final Track track; private final Track[] tracks;
private final TrackFormatWriter trackFormatWriter; private final TrackFormatWriter trackFormatWriter;
private final OnWriteListener onWriteListener; private final OnWriteListener onWriteListener;
@@ -70,20 +70,20 @@ public class TrackWriter {
* *
* @param context the context * @param context the context
* @param myTracksProviderUtils the my tracks provider utils * @param myTracksProviderUtils the my tracks provider utils
* @param track the track * @param tracks the tracks
* @param trackFileFormat the track file format * @param trackFileFormat the track file format
* @param onWriteListener the on write listener * @param onWriteListener the on write listener
*/ */
public TrackWriter(Context context, MyTracksProviderUtils myTracksProviderUtils, Track track, public TrackWriter(Context context, MyTracksProviderUtils myTracksProviderUtils, Track[] tracks,
TrackFileFormat trackFileFormat, OnWriteListener onWriteListener) { TrackFileFormat trackFileFormat, OnWriteListener onWriteListener) {
this(myTracksProviderUtils, track, trackFileFormat.newFormatWriter(context), onWriteListener); this(myTracksProviderUtils, tracks, trackFileFormat.newFormatWriter(context), onWriteListener);
} }
@VisibleForTesting @VisibleForTesting
public TrackWriter(MyTracksProviderUtils myTracksProviderUtils, Track track, public TrackWriter(MyTracksProviderUtils myTracksProviderUtils, Track[] tracks,
TrackFormatWriter trackFormatWriter, OnWriteListener onWriteListener) { TrackFormatWriter trackFormatWriter, OnWriteListener onWriteListener) {
this.myTracksProviderUtils = myTracksProviderUtils; this.myTracksProviderUtils = myTracksProviderUtils;
this.track = track; this.tracks = tracks;
this.trackFormatWriter = trackFormatWriter; this.trackFormatWriter = trackFormatWriter;
this.onWriteListener = onWriteListener; this.onWriteListener = onWriteListener;
} }
@@ -105,10 +105,14 @@ public class TrackWriter {
@Override @Override
public void run() { public void run() {
try { try {
trackFormatWriter.prepare(track, outputStream); trackFormatWriter.prepare(outputStream);
trackFormatWriter.writeHeader(); trackFormatWriter.writeHeader(tracks[0]);
writeWaypoints(); long startTime = tracks[0].getTripStatistics().getStartTime();
writeLocations(); for (int i = 0; i < tracks.length; i++) {
writeWaypoints(tracks[i]);
long offset = tracks[i].getTripStatistics().getStartTime() - startTime;
writeLocations(tracks[i], offset);
}
trackFormatWriter.writeFooter(); trackFormatWriter.writeFooter();
trackFormatWriter.close(); trackFormatWriter.close();
success = true; success = true;
@@ -147,7 +151,7 @@ public class TrackWriter {
/** /**
* Writes the waypoints. * Writes the waypoints.
*/ */
private void writeWaypoints() { private void writeWaypoints(Track track) {
/* /*
* TODO: Stream through the waypoints in chunks. I am leaving the number of * TODO: Stream through the waypoints in chunks. I am leaving the number of
* waypoints very high which should not be a problem because we don't try to * waypoints very high which should not be a problem because we don't try to
@@ -185,7 +189,7 @@ public class TrackWriter {
/** /**
* Writes the locations. * Writes the locations.
*/ */
private void writeLocations() throws InterruptedException { private void writeLocations(Track track, long offset) throws InterruptedException {
boolean wroteTrack = false; boolean wroteTrack = false;
boolean wroteSegment = false; boolean wroteSegment = false;
boolean isLastLocationValid = false; boolean isLastLocationValid = false;
@@ -197,6 +201,7 @@ public class TrackWriter {
int locationNumber = 0; int locationNumber = 0;
while (iterator.hasNext()) { while (iterator.hasNext()) {
Location location = iterator.next(); Location location = iterator.next();
setLocationTime(location, offset);
if (Thread.interrupted()) { if (Thread.interrupted()) {
throw new InterruptedException(); throw new InterruptedException();
} }
@@ -205,8 +210,8 @@ public class TrackWriter {
boolean isLocationValid = LocationUtils.isValidLocation(location); boolean isLocationValid = LocationUtils.isValidLocation(location);
boolean isSegmentValid = isLocationValid && isLastLocationValid; boolean isSegmentValid = isLocationValid && isLastLocationValid;
if (!wroteTrack && isSegmentValid) { if (!wroteTrack && isSegmentValid) {
// Found the first two consecutive locations that are valid // Found the first two consecutive locations that are valid
trackFormatWriter.writeBeginTrack(locationFactory.lastLocation); trackFormatWriter.writeBeginTrack(track, locationFactory.lastLocation);
wroteTrack = true; wroteTrack = true;
} }
@@ -241,17 +246,30 @@ public class TrackWriter {
} }
if (wroteTrack) { if (wroteTrack) {
Location lastValidTrackPoint = myTracksProviderUtils.getLastValidTrackPoint(track.getId()); Location lastValidTrackPoint = myTracksProviderUtils.getLastValidTrackPoint(track.getId());
trackFormatWriter.writeEndTrack(lastValidTrackPoint); setLocationTime(lastValidTrackPoint, offset);
trackFormatWriter.writeEndTrack(track, lastValidTrackPoint);
} else { } else {
// Write an empty track // Write an empty track
trackFormatWriter.writeBeginTrack(null); trackFormatWriter.writeBeginTrack(track, null);
trackFormatWriter.writeEndTrack(null); trackFormatWriter.writeEndTrack(track, null);
} }
} finally { } finally {
iterator.close(); iterator.close();
} }
} }
/**
* Sets a location time.
*
* @param location the location
* @param offset the time offset
*/
private void setLocationTime(Location location, long offset) {
if (location != null) {
location.setTime(location.getTime() - offset);
}
}
/** /**
* Track writer location factory. Keeping the last two locations. * Track writer location factory. Keeping the last two locations.
* *
@@ -427,7 +427,7 @@ public class SyncUtils {
java.io.File file = new java.io.File(directory, java.io.File file = new java.io.File(directory,
FileUtils.buildUniqueFileName(directory, track.getName(), trackFileFormat.getExtension())); FileUtils.buildUniqueFileName(directory, track.getName(), trackFileFormat.getExtension()));
TrackWriter trackWriter = new TrackWriter( TrackWriter trackWriter = new TrackWriter(
context, myTracksProviderUtils, track, trackFileFormat, null); context, myTracksProviderUtils, new Track[] {track}, trackFileFormat, null);
trackWriter.writeTrack(new FileOutputStream(file)); trackWriter.writeTrack(new FileOutputStream(file));
if (trackWriter.wasSuccess()) { if (trackWriter.wasSuccess()) {
@@ -0,0 +1,75 @@
/*
* 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.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import java.io.File;
import java.util.List;
/**
* Utilities for Google Earth.
*
* @author Jimmy Shih
*/
public class GoogleEarthUtils {
private static final String GOOGLE_EARTH_CLASS = "com.google.earth.EarthActivity";
private static final String GOOGLE_EARTH_KML_MIME_TYPE = "application/vnd.google-earth.kml+xml";
private static final String GOOGLE_EARTH_PACKAGE = "com.google.earth";
private static final String
GOOGLE_EARTH_TOUR_FEATURE_ID = "com.google.earth.EXTRA.tour_feature_id";
public static final String GOOGLE_EARTH_MARKET_URL = "market://details?id="
+ GOOGLE_EARTH_PACKAGE;
public static final String TOUR_FEATURE_ID_VALUE = "tour";
private GoogleEarthUtils() {}
/**
* Returns true if Google Earth is installed.
*
* @param context the context
*/
public static boolean isEarthInstalled(Context context) {
List<ResolveInfo> infos = context.getPackageManager().queryIntentActivities(
new Intent().setType(GOOGLE_EARTH_KML_MIME_TYPE), PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : infos) {
if (info.activityInfo != null && info.activityInfo.packageName != null
&& info.activityInfo.packageName.equals(GOOGLE_EARTH_PACKAGE)) {
return true;
}
}
return false;
}
/**
* Gets an intent to play a kml file in Google Earth.
*
* @param kmlFilePath the kml file path
*/
public static Intent getPlayInEarthIntent(String kmlFilePath) {
return new Intent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(GOOGLE_EARTH_TOUR_FEATURE_ID, TOUR_FEATURE_ID_VALUE)
.setClassName(GOOGLE_EARTH_PACKAGE, GOOGLE_EARTH_CLASS)
.setDataAndType(Uri.fromFile(new File(kmlFilePath)), GOOGLE_EARTH_KML_MIME_TYPE);
}
}
@@ -42,6 +42,8 @@ public class GoogleFeedbackUtils {
private static Boolean available = null; private static Boolean available = null;
private GoogleFeedbackUtils() {}
/** /**
* Returns true if the Google Feedback is available. * Returns true if the Google Feedback is available.
* *
@@ -23,7 +23,7 @@ import android.content.pm.ResolveInfo;
/** /**
* Utilities for Google location settings * Utilities for Google location settings
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public class GoogleLocationUtils { public class GoogleLocationUtils {
@@ -32,7 +32,9 @@ public class GoogleLocationUtils {
ACTION_GOOGLE_LOCATION_SETTINGS = "com.google.android.gsf.GOOGLE_LOCATION_SETTINGS"; ACTION_GOOGLE_LOCATION_SETTINGS = "com.google.android.gsf.GOOGLE_LOCATION_SETTINGS";
private static final String private static final String
ACTION_GOOGLE_APPS_LOCATION_SETTINGS = "com.google.android.gsf.GOOGLE_APPS_LOCATION_SETTINGS"; ACTION_GOOGLE_APPS_LOCATION_SETTINGS = "com.google.android.gsf.GOOGLE_APPS_LOCATION_SETTINGS";
private GoogleLocationUtils() {}
/** /**
* Returns true if the Google location settings is available. * Returns true if the Google location settings is available.
*/ */
@@ -98,9 +98,7 @@ public class PreferencesUtils {
public static final String SHARE_TRACK_DEFAULT = "DRIVE"; public static final String SHARE_TRACK_DEFAULT = "DRIVE";
public static final int SPLIT_FREQUENCY_DEFAULT = 0; public static final int SPLIT_FREQUENCY_DEFAULT = 0;
public static final boolean STATS_SHOW_COORDINATE_DEFAULT = false; public static final boolean STATS_SHOW_COORDINATE_DEFAULT = false;
public static final boolean STATS_SHOW_ELEVATION_DEFAULT = false; public static final boolean STATS_SHOW_GRADE_ELEVATION_DEFAULT = false;
public static final boolean STATS_SHOW_GRADE_DEFAULT = false;
public static final boolean STATS_SHOW_MOVING_TIME_DEFAULT = false;
public static final String TRACK_COLOR_MODE_DEFAULT = "SINGLE"; public static final String TRACK_COLOR_MODE_DEFAULT = "SINGLE";
public static final int TRACK_COLOR_MODE_MEDIUM_DEFAULT = 15; public static final int TRACK_COLOR_MODE_MEDIUM_DEFAULT = 15;
public static final int TRACK_COLOR_MODE_PERCENTAGE_DEFAULT = 25; public static final int TRACK_COLOR_MODE_PERCENTAGE_DEFAULT = 25;
@@ -24,78 +24,87 @@ import android.location.Location;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import java.util.Locale;
/** /**
* Utilities for updating the statistics UI labels and values. * Utilities for updating the statistics UI labels and values.
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public class StatsUtils { 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 StatsUtils() {} private StatsUtils() {}
/** /**
* Sets the location values. * Sets the location values.
* *
* @param activity the activity * @param activity the activity
* @param location the location * @param location the location
* @param showAll true to show all the fields, false to show only the * @param isRecording true if recording
* elevation field
*/ */
public static void setLocationValues(Activity activity, Location location, boolean showAll) { public static void setLocationValues(Activity activity, Location location, boolean isRecording) {
boolean metricUnits = PreferencesUtils.getBoolean( boolean metricUnits = PreferencesUtils.getBoolean(
activity, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT); activity, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
boolean reportSpeed = PreferencesUtils.getBoolean( boolean reportSpeed = PreferencesUtils.getBoolean(
activity, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT); activity, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
// Set elevation
boolean showElevation = PreferencesUtils.getBoolean(
activity, R.string.stats_show_elevation_key, PreferencesUtils.STATS_SHOW_ELEVATION_DEFAULT);
if (showElevation) {
double altitude = location == null ? Double.NaN : location.getAltitude();
setElevationValue(activity, R.id.stats_elevation_value, altitude, metricUnits);
}
if (!showAll) {
return;
}
// Set speed/pace // Set speed/pace
setSpeedLabel( double speed = !isRecording || location == null ? Double.NaN : location.getSpeed();
activity, R.id.stats_speed_label, R.string.stats_speed, R.string.stats_pace, reportSpeed); setSpeed(activity, R.id.stats_speed, R.string.stats_speed, R.string.stats_pace, speed,
double speed = location == null ? Double.NaN : location.getSpeed(); metricUnits, reportSpeed);
setSpeedValue(activity, R.id.stats_speed_value, speed, metricUnits, reportSpeed);
// Set elevation
boolean showGradeElevation = PreferencesUtils.getBoolean(activity,
R.string.stats_show_grade_elevation_key,
PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT);
View elevation = activity.findViewById(R.id.stats_elevation);
if (showGradeElevation && isRecording) {
double altitude = location == null ? Double.NaN : location.getAltitude();
elevation.setVisibility(View.VISIBLE);
setElevationValue(activity, R.id.stats_elevation, -1, altitude, metricUnits);
} else {
elevation.setVisibility(View.GONE);
}
// Set coordinate // Set coordinate
boolean showCoordinate = PreferencesUtils.getBoolean(activity, boolean showCoordinate = PreferencesUtils.getBoolean(activity,
R.string.stats_show_coordinate_key, PreferencesUtils.STATS_SHOW_COORDINATE_DEFAULT); R.string.stats_show_coordinate_key, PreferencesUtils.STATS_SHOW_COORDINATE_DEFAULT);
View coordinateLabelTableRow = activity.findViewById(R.id.stats_coordinate_label_table_row); View coordinateHorizontalLine = activity.findViewById(R.id.stats_coordinate_horizontal_line);
View coordinateValueTableRow = activity.findViewById(R.id.stats_coordinate_value_table_row); View coordinateContainer = activity.findViewById(R.id.stats_coordinate_container);
if (coordinateLabelTableRow == null || coordinateValueTableRow == null) {
return; if (showCoordinate && isRecording) {
}
coordinateLabelTableRow.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
coordinateValueTableRow.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
if (showCoordinate) {
double latitude = location == null ? Double.NaN : location.getLatitude(); double latitude = location == null ? Double.NaN : location.getLatitude();
double longitude = location == null ? Double.NaN : location.getLongitude(); double longitude = location == null ? Double.NaN : location.getLongitude();
setCoordinateValue(activity, R.id.stats_latitude_value, latitude); coordinateHorizontalLine.setVisibility(View.VISIBLE);
setCoordinateValue(activity, R.id.stats_longitude_value, longitude); coordinateContainer.setVisibility(View.VISIBLE);
setCoordinateValue(activity, R.id.stats_latitude, R.string.stats_latitude, latitude);
setCoordinateValue(activity, R.id.stats_longitude, R.string.stats_longitude, longitude);
} else {
coordinateHorizontalLine.setVisibility(View.GONE);
coordinateContainer.setVisibility(View.GONE);
} }
} }
/** /**
* Sets the total time value. * Sets the total time value.
* *
* @param activity the activity * @param activity the activity
* @param totalTime the total time * @param totalTime the total time
*/ */
public static void setTotalTimeValue(Activity activity, long totalTime) { public static void setTotalTimeValue(Activity activity, long totalTime) {
setTimeValue(activity, R.id.stats_total_time_value, totalTime); setTimeValue(activity, R.id.stats_total_time, R.string.stats_total_time, totalTime);
} }
/** /**
* Sets the trip statistics values. * Sets the trip statistics values.
* *
* @param activity the activity * @param activity the activity
* @param tripStatistics the trip statistics * @param tripStatistics the trip statistics
*/ */
@@ -104,249 +113,206 @@ public class StatsUtils {
activity, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT); activity, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
boolean reportSpeed = PreferencesUtils.getBoolean( boolean reportSpeed = PreferencesUtils.getBoolean(
activity, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT); activity, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
boolean showMovingTime = PreferencesUtils.getBoolean(activity,
R.string.stats_show_moving_time_key, PreferencesUtils.STATS_SHOW_MOVING_TIME_DEFAULT);
// Set total distance // Set total distance
double totalDistance = tripStatistics == null ? Double.NaN : tripStatistics.getTotalDistance(); double totalDistance = tripStatistics == null ? Double.NaN : tripStatistics.getTotalDistance();
setDistanceValue(activity, R.id.stats_distance_value, totalDistance, metricUnits); setDistanceValue(activity, totalDistance, metricUnits);
// Set total time // Set total time
setTimeValue(activity, R.id.stats_total_time_value, setTimeValue(activity, R.id.stats_total_time, R.string.stats_total_time,
tripStatistics != null ? tripStatistics.getTotalTime() : -1L); tripStatistics != null ? tripStatistics.getTotalTime() : -1L);
// Set moving time // Set average speed/pace
setItemVisibility(activity, R.id.stats_moving_time_label, R.id.stats_moving_time_spacer,
R.id.stats_moving_time_value, showMovingTime);
if (showMovingTime) {
setTimeValue(activity, R.id.stats_moving_time_value,
tripStatistics != null ? tripStatistics.getMovingTime() : -1L);
}
// Set average speed
setSpeedLabel(activity, R.id.stats_average_speed_label, R.string.stats_average_speed,
R.string.stats_average_pace, reportSpeed);
double averageSpeed = tripStatistics != null ? tripStatistics.getAverageSpeed() : Double.NaN; double averageSpeed = tripStatistics != null ? tripStatistics.getAverageSpeed() : Double.NaN;
setSpeedValue(activity, R.id.stats_average_speed_value, averageSpeed, metricUnits, reportSpeed); setSpeed(activity, R.id.stats_average_speed, R.string.stats_average_speed,
R.string.stats_average_pace, averageSpeed, metricUnits, reportSpeed);
// Set average moving speed
setItemVisibility(activity, R.id.stats_average_moving_speed_label,
R.id.stats_average_moving_speed_spacer, R.id.stats_average_moving_speed_value,
showMovingTime);
if (showMovingTime) {
setSpeedLabel(activity, R.id.stats_average_moving_speed_label,
R.string.stats_average_moving_speed, R.string.stats_average_moving_pace, reportSpeed);
double averageMovingSpeed = tripStatistics != null ? tripStatistics.getAverageMovingSpeed()
: Double.NaN;
setSpeedValue(activity, R.id.stats_average_moving_speed_value, averageMovingSpeed,
metricUnits, reportSpeed);
}
// Set max speed // Set moving time
setSpeedLabel(activity, R.id.stats_max_speed_label, R.string.stats_max_speed, setTimeValue(activity, R.id.stats_moving_time, R.string.stats_moving_time,
R.string.stats_fastest_pace, reportSpeed); tripStatistics != null ? tripStatistics.getMovingTime() : -1L);
// Set average moving speed/pace
double averageMovingSpeed = tripStatistics != null ? tripStatistics.getAverageMovingSpeed()
: Double.NaN;
setSpeed(activity, 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(); double maxSpeed = tripStatistics == null ? Double.NaN : tripStatistics.getMaxSpeed();
setSpeedValue(activity, R.id.stats_max_speed_value, maxSpeed, metricUnits, reportSpeed); setSpeed(activity, R.id.stats_max_speed, R.string.stats_max_speed, R.string.stats_fastest_pace,
maxSpeed, metricUnits, reportSpeed);
// Set elevation
boolean showElevation = PreferencesUtils.getBoolean( // Set grade/elevation
activity, R.string.stats_show_elevation_key, PreferencesUtils.STATS_SHOW_ELEVATION_DEFAULT); boolean showGradeElevation = PreferencesUtils.getBoolean(activity,
View elevationLabelTableRow1 = activity.findViewById(R.id.stats_elevation_label_table_row1); R.string.stats_show_grade_elevation_key,
View elevationValueTableRow1 = activity.findViewById(R.id.stats_elevation_value_table_row1); PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT);
View elevationLabelTableRow2 = activity.findViewById(R.id.stats_elevation_label_table_row2); View gradeElevationHorizontalLine = activity.findViewById(
View elevationValueTableRow2 = activity.findViewById(R.id.stats_elevation_value_table_row2); R.id.stats_grade_elevation_horizontal_line);
if (elevationLabelTableRow1 == null View gradeElevationContainer = activity.findViewById(R.id.stats_grade_elevation_container);
|| elevationValueTableRow1 == null
|| elevationLabelTableRow2 == null if (showGradeElevation) {
|| elevationValueTableRow2 == null) { gradeElevationHorizontalLine.setVisibility(View.VISIBLE);
return; gradeElevationContainer.setVisibility(View.VISIBLE);
} // Set grade
elevationLabelTableRow1.setVisibility(showElevation ? View.VISIBLE : View.GONE); double minGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMinGrade();
elevationValueTableRow1.setVisibility(showElevation ? View.VISIBLE : View.GONE); double maxGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMaxGrade();
elevationLabelTableRow2.setVisibility(showElevation ? View.VISIBLE : View.GONE); setGradeValue(activity, R.id.stats_grade_min, R.string.stats_min, minGrade);
elevationValueTableRow2.setVisibility(showElevation ? View.VISIBLE : View.GONE); setGradeValue(activity, R.id.stats_grade_max, R.string.stats_max, maxGrade);
if (showElevation) {
// Set elevation
double elevationGain = tripStatistics == null ? Double.NaN double elevationGain = tripStatistics == null ? Double.NaN
: tripStatistics.getTotalElevationGain(); : tripStatistics.getTotalElevationGain();
double minElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMinElevation(); double minElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMinElevation();
double maxElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMaxElevation(); double maxElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMaxElevation();
setElevationValue(activity, R.id.stats_elevation_gain_value, elevationGain, metricUnits); setElevationValue(
setElevationValue(activity, R.id.stats_min_elevation_value, minElevation, metricUnits); activity, R.id.stats_elevation_gain, R.string.stats_gain, elevationGain, metricUnits);
setElevationValue(activity, R.id.stats_max_elevation_value, maxElevation, metricUnits); setElevationValue(
} activity, R.id.stats_elevation_min, R.string.stats_min, minElevation, metricUnits);
setElevationValue(
// Set grade activity, R.id.stats_elevation_max, R.string.stats_max, maxElevation, metricUnits);
boolean showGrade = PreferencesUtils.getBoolean( } else {
activity, R.string.stats_show_grade_key, PreferencesUtils.STATS_SHOW_GRADE_DEFAULT); gradeElevationHorizontalLine.setVisibility(View.GONE);
View gradeLabelTableRow = activity.findViewById(R.id.stats_grade_label_table_row); gradeElevationContainer.setVisibility(View.GONE);
View gradeValueTableRow = activity.findViewById(R.id.stats_grade_value_table_row);
if (gradeLabelTableRow == null || gradeValueTableRow == null) {
return;
}
gradeLabelTableRow.setVisibility(showGrade ? View.VISIBLE : View.GONE);
gradeValueTableRow.setVisibility(showGrade ? View.VISIBLE : View.GONE);
if (showGrade) {
double minGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMinGrade();
double maxGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMaxGrade();
setGradeValue(activity, R.id.stats_min_grade_value, minGrade);
setGradeValue(activity, R.id.stats_max_grade_value, maxGrade);
}
}
private static void setItemVisibility(
Activity activity, int labelId, int spacerId, int valueId, boolean show) {
View label = activity.findViewById(labelId);
View spacer = activity.findViewById(spacerId);
View value = activity.findViewById(valueId);
if (label != null) {
label.setVisibility(show ? View.VISIBLE : View.GONE);
}
if (spacer != null) {
spacer.setVisibility(show ? View.VISIBLE : View.GONE);
}
if (value != null) {
value.setVisibility(show ? View.VISIBLE : View.GONE);
} }
} }
/** /**
* Sets a speed label. * Sets speed.
* *
* @param activity the activity * @param activity the activity
* @param id the speed label resource id * @param itemId the item id
* @param speedId the speed string id * @param speedLabelId the speed label id
* @param paceId the pace string id * @param paceLabelId the pace label id
* @param reportSpeed true to report speed
*/
private static void setSpeedLabel(
Activity activity, int id, int speedId, int paceId, boolean reportSpeed) {
TextView textView = (TextView) activity.findViewById(id);
if (textView == null) {
return;
}
textView.setText(reportSpeed ? speedId : paceId);
}
/**
* Sets a speed value.
*
* @param activity the activity
* @param id the speed value resource id
* @param speed the speed in meters per second * @param speed the speed in meters per second
* @param metricUnits true to display in metric units * @param metricUnits true if metric units
* @param reportSpeed true to report speed * @param reportSpeed true if report speed
*/ */
private static void setSpeedValue( private static void setSpeed(Activity activity, int itemId, int speedLabelId, int paceLabelId,
Activity activity, int id, double speed, boolean metricUnits, boolean reportSpeed) { double speed, boolean metricUnits, boolean reportSpeed) {
TextView textView = (TextView) activity.findViewById(id); String parts[] = StringUtils.getSpeedParts(activity, speed, metricUnits, reportSpeed);
if (textView == null) { setItem(activity, itemId, reportSpeed ? speedLabelId : paceLabelId, parts[0], parts[1]);
return;
}
textView.setText(StringUtils.formatSpeed(activity, speed, metricUnits, reportSpeed));
} }
/** /**
* Sets a distance value. * Sets distance value.
* *
* @param activity the activity * @param activity the activity
* @param id the distance value resource id
* @param distance the distance in meters * @param distance the distance in meters
* @param metricUnits true to display in metric units * @param metricUnits true if metric units
*/ */
private static void setDistanceValue( private static void setDistanceValue(Activity activity, double distance, boolean metricUnits) {
Activity activity, int id, double distance, boolean metricUnits) { String parts[] = StringUtils.getDistanceParts(activity, distance, metricUnits);
TextView textView = (TextView) activity.findViewById(id); setItem(activity, R.id.stats_distance, R.string.stats_distance, parts[0], parts[1]);
if (textView == null) {
return;
}
textView.setText(StringUtils.formatDistance(activity, distance, metricUnits));
} }
/** /**
* Sets a time value. * Sets a time value.
* *
* @param activity the activity * @param activity the activity
* @param id the time value resource id * @param itemId the item id
* @param labelId the label id
* @param time the time * @param time the time
*/ */
private static void setTimeValue(Activity activity, int id, long time) { private static void setTimeValue(Activity activity, int itemId, int labelId, long time) {
TextView textView = (TextView) activity.findViewById(id);
if (textView == null) {
return;
}
String value = time == -1L ? activity.getString(R.string.value_unknown) String value = time == -1L ? activity.getString(R.string.value_unknown)
: StringUtils.formatElapsedTime(time); : StringUtils.formatElapsedTime(time);
textView.setText(value); setItem(activity, itemId, labelId, value, null);
} }
/** /**
* Sets an elevation value. * Sets an elevation value.
* *
* @param activity the activity * @param activity the activity
* @param id the elevation value resource id * @param itemId the item id
* @param labelId the label id
* @param elevation the elevation in meters * @param elevation the elevation in meters
* @param metricUnits true to display in metric units * @param metricUnits true if metric units
*/ */
private static void setElevationValue( private static void setElevationValue(
Activity activity, int id, double elevation, boolean metricUnits) { Activity activity, int itemId, int labelId, double elevation, boolean metricUnits) {
TextView textView = (TextView) activity.findViewById(id);
if (textView == null) {
return;
}
String value; String value;
String unit;
if (Double.isNaN(elevation) || Double.isInfinite(elevation)) { if (Double.isNaN(elevation) || Double.isInfinite(elevation)) {
value = activity.getString(R.string.value_unknown); value = null;
unit = null;
} else { } else {
if (metricUnits) { if (metricUnits) {
value = activity.getString(R.string.value_float_meter, elevation); value = String.format(Locale.getDefault(), ELEVATION_FORMAT, elevation);
unit = activity.getString(R.string.unit_meter);
} else { } else {
elevation *= UnitConversions.M_TO_FT; elevation *= UnitConversions.M_TO_FT;
value = activity.getString(R.string.value_float_feet, elevation); value = String.format(Locale.getDefault(), ELEVATION_FORMAT, elevation);
unit = activity.getString(R.string.unit_feet);
} }
} }
textView.setText(value); setItem(activity, itemId, labelId, value, unit);
} }
/** /**
* Sets a grade value. * Sets a grade value.
* *
* @param activity the activity * @param activity the activity
* @param id the grade value resource id * @param itemId the item id
* @param labelId the label id
* @param grade the grade in fraction between 0 and 1 * @param grade the grade in fraction between 0 and 1
*/ */
private static void setGradeValue(Activity activity, int id, double grade) { private static void setGradeValue(Activity activity, int itemId, int labelId, double grade) {
TextView textView = (TextView) activity.findViewById(id); String value = Double.isNaN(grade) || Double.isInfinite(grade) ? null
if (textView == null) { : String.format(Locale.getDefault(), GRADE_FORMAT, Math.round(grade * 100));
return; setItem(activity, itemId, labelId, value, GRADE_PERCENTAGE);
}
String value;
if (Double.isNaN(grade) || Double.isInfinite(grade)) {
value = activity.getString(R.string.value_unknown);
} else {
value = activity.getString(R.string.value_integer_percent, Math.round(grade * 100));
}
textView.setText(value);
} }
/** /**
* Sets a coordinate value. * Sets a coordinate value.
* *
* @param activity the activity * @param activity the activity
* @param id the coordinate value resource id * @param itemId the item id
* @param labelId the label id
* @param coordinate the coordinate in degrees * @param coordinate the coordinate in degrees
*/ */
private static void setCoordinateValue(Activity activity, int id, double coordinate) { private static void setCoordinateValue(
TextView textView = (TextView) activity.findViewById(id); Activity activity, int itemId, int labelId, double coordinate) {
if (textView == null) { String value = Double.isNaN(coordinate) || Double.isInfinite(coordinate) ? null
: Location.convert(coordinate, Location.FORMAT_DEGREES);
setItem(activity, itemId, labelId, value, COORDINATE_DEGREE);
}
/**
* Sets an item.
*
* @param activity the activity
* @param itemId the item id
* @param labelId the label id. -1 to hide the label
* @param value the value, can be null
* @param unit the unit. Null to hide the unit
*/
private static void setItem(
Activity activity, int itemId, int labelId, String value, String unit) {
View view = activity.findViewById(itemId);
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);
if (labelTextView == null || valueTextView == null || unitTextView == null) {
return; return;
} }
String value; if (labelId == -1) {
if (Double.isNaN(coordinate) || Double.isInfinite(coordinate)) { labelTextView.setVisibility(View.GONE);
value = activity.getString(R.string.value_unknown);
} else { } else {
value = activity.getString( labelTextView.setVisibility(View.VISIBLE);
R.string.value_coordinate_degree, Location.convert(coordinate, Location.FORMAT_DEGREES)); labelTextView.setText(labelId);
} }
textView.setText(value);
if (value == null) {
value = activity.getString(R.string.value_unknown);
unitTextView.setVisibility(View.GONE);
} else {
if (unit == null) {
unitTextView.setVisibility(View.GONE);
} else {
unitTextView.setVisibility(View.VISIBLE);
unitTextView.setText(unit);
}
}
valueTextView.setText(value);
} }
} }
@@ -151,7 +151,8 @@ public class StringUtils {
/** /**
* Gets the distance in an array of two strings. The first string is the * Gets the distance in an array of two strings. The first string is the
* distance. The second string is the unit. * distance. The second string is the unit. The first string is null if the
* distance is invalid.
* *
* @param context the context * @param context the context
* @param distance the distance * @param distance the distance
@@ -160,7 +161,7 @@ public class StringUtils {
public static String[] getDistanceParts(Context context, double distance, boolean metricUnits) { public static String[] getDistanceParts(Context context, double distance, boolean metricUnits) {
String[] result = new String[2]; String[] result = new String[2];
if (Double.isNaN(distance) || Double.isInfinite(distance)) { if (Double.isNaN(distance) || Double.isInfinite(distance)) {
result[0] = context.getString(R.string.value_unknown); result[0] = null;
result[1] = context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet); result[1] = context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet);
return result; return result;
} }
@@ -188,42 +189,9 @@ public class StringUtils {
} }
/** /**
* Formats the speed. * Gets the speed in an array of two strings. The first string is the speed.
* * The second string is the unit. The first string is null if speed is
* @param context the context * invalid.
* @param speed the speed in meters per second
* @param metricUnits true to use metric units. False to use imperial units
* @param reportSpeed true to report as speed. False to report as pace
*/
public static String formatSpeed(
Context context, double speed, boolean metricUnits, boolean reportSpeed) {
if (Double.isNaN(speed) || Double.isInfinite(speed)) {
return context.getString(R.string.value_unknown);
}
speed *= UnitConversions.MS_TO_KMH;
if (metricUnits) {
if (reportSpeed) {
return context.getString(R.string.value_float_kilometer_hour, speed);
} else {
// convert from hours to minutes
double pace = speed == 0 ? 0.0 : 60.0 / speed;
return context.getString(R.string.value_float_minute_kilometer, pace);
}
} else {
speed *= UnitConversions.KM_TO_MI;
if (reportSpeed) {
return context.getString(R.string.value_float_mile_hour, speed);
} else {
// convert from hours to minutes
double pace = speed == 0 ? 0.0 : 60.0 / speed;
return context.getString(R.string.value_float_minute_mile, pace);
}
}
}
/**
* Gets the speed in an array of two strings. The first string is the
* speed. The second string is the unit.
* *
* @param context the context * @param context the context
* @param speed the speed * @param speed the speed
@@ -241,7 +209,7 @@ public class StringUtils {
} }
result[1] = context.getString(unitId); result[1] = context.getString(unitId);
if (Double.isNaN(speed) || Double.isInfinite(speed)) { if (Double.isNaN(speed) || Double.isInfinite(speed)) {
result[0] = context.getString(R.string.value_unknown); result[0] = null;
return result; return result;
} }
speed *= UnitConversions.MS_TO_KMH; speed *= UnitConversions.MS_TO_KMH;
@@ -314,6 +314,9 @@ public class TrackWidgetProvider extends AppWidgetProvider {
TripStatistics tripStatistics, boolean metricUnits) { TripStatistics tripStatistics, boolean metricUnits) {
double totalDistance = tripStatistics == null ? Double.NaN : tripStatistics.getTotalDistance(); double totalDistance = tripStatistics == null ? Double.NaN : tripStatistics.getTotalDistance();
String[] totalDistanceParts = StringUtils.getDistanceParts(context, totalDistance, metricUnits); String[] totalDistanceParts = StringUtils.getDistanceParts(context, totalDistance, metricUnits);
if (totalDistanceParts[0] == null) {
totalDistanceParts[0] = context.getString(R.string.value_unknown);
}
remoteViews.setTextViewText(ids[0], context.getString(R.string.stats_distance)); remoteViews.setTextViewText(ids[0], context.getString(R.string.stats_distance));
remoteViews.setTextViewText(ids[1], totalDistanceParts[0]); remoteViews.setTextViewText(ids[1], totalDistanceParts[0]);
remoteViews.setTextViewText(ids[2], totalDistanceParts[1]); remoteViews.setTextViewText(ids[2], totalDistanceParts[1]);
@@ -367,6 +370,11 @@ public class TrackWidgetProvider extends AppWidgetProvider {
Double speed = tripStatistics == null ? Double.NaN : tripStatistics.getAverageSpeed(); Double speed = tripStatistics == null ? Double.NaN : tripStatistics.getAverageSpeed();
String[] speedParts = StringUtils.getSpeedParts(context, speed, metricUnits, reportSpeed); String[] speedParts = StringUtils.getSpeedParts(context, speed, metricUnits, reportSpeed);
if (speedParts[0] == null) {
speedParts[0] = context.getString(R.string.value_unknown);
}
remoteViews.setTextViewText(ids[1], speedParts[0]); remoteViews.setTextViewText(ids[1], speedParts[0]);
remoteViews.setTextViewText(ids[2], speedParts[1]); remoteViews.setTextViewText(ids[2], speedParts[1]);
} }
@@ -406,6 +414,11 @@ public class TrackWidgetProvider extends AppWidgetProvider {
Double speed = tripStatistics == null ? Double.NaN : tripStatistics.getAverageMovingSpeed(); Double speed = tripStatistics == null ? Double.NaN : tripStatistics.getAverageMovingSpeed();
String[] speedParts = StringUtils.getSpeedParts(context, speed, metricUnits, reportSpeed); String[] speedParts = StringUtils.getSpeedParts(context, speed, metricUnits, reportSpeed);
if (speedParts[0] == null) {
speedParts[0] = context.getString(R.string.value_unknown);
}
remoteViews.setTextViewText(ids[1], speedParts[0]); remoteViews.setTextViewText(ids[1], speedParts[0]);
remoteViews.setTextViewText(ids[2], speedParts[1]); remoteViews.setTextViewText(ids[2], speedParts[1]);
} }
@@ -115,13 +115,13 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
*/ */
protected String writeTrack(TrackFormatWriter writer) throws Exception { protected String writeTrack(TrackFormatWriter writer) throws Exception {
OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE); OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE);
writer.prepare(track, output); writer.prepare(output);
writer.writeHeader(); writer.writeHeader(track);
writer.writeBeginWaypoints(); writer.writeBeginWaypoints();
writer.writeWaypoint(wp1); writer.writeWaypoint(wp1);
writer.writeWaypoint(wp2); writer.writeWaypoint(wp2);
writer.writeEndWaypoints(); writer.writeEndWaypoints();
writer.writeBeginTrack(location1); writer.writeBeginTrack(track, location1);
writer.writeOpenSegment(); writer.writeOpenSegment();
writer.writeLocation(location1); writer.writeLocation(location1);
writer.writeLocation(location2); writer.writeLocation(location2);
@@ -130,7 +130,7 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
writer.writeLocation(location3); writer.writeLocation(location3);
writer.writeLocation(location4); writer.writeLocation(location4);
writer.writeCloseSegment(); writer.writeCloseSegment();
writer.writeEndTrack(location4); writer.writeEndTrack(track, location4);
writer.writeFooter(); writer.writeFooter();
writer.close(); writer.close();
return output.toString(); return output.toString();
@@ -76,7 +76,7 @@ public class TrackWriterTest extends AndroidTestCase {
} }
}; };
trackWriter = new TrackWriter(myTracksProviderUtils, track, trackFormatWriter, onWriteListener); trackWriter = new TrackWriter(myTracksProviderUtils, new Track[] {track}, trackFormatWriter, onWriteListener);
} }
@Override @Override
@@ -91,10 +91,10 @@ public class TrackWriterTest extends AndroidTestCase {
public void testWriteTrack_emptyTrack() throws Exception { public void testWriteTrack_emptyTrack() throws Exception {
// Set expected mock behavior // Set expected mock behavior
trackFormatWriter.prepare(track, outputStream); trackFormatWriter.prepare(outputStream);
trackFormatWriter.writeHeader(); trackFormatWriter.writeHeader(track);
trackFormatWriter.writeBeginTrack(null); trackFormatWriter.writeBeginTrack(track, null);
trackFormatWriter.writeEndTrack(null); trackFormatWriter.writeEndTrack(track, null);
trackFormatWriter.writeFooter(); trackFormatWriter.writeFooter();
trackFormatWriter.close(); trackFormatWriter.close();
@@ -123,10 +123,10 @@ public class TrackWriterTest extends AndroidTestCase {
myTracksProviderUtils.bulkInsertTrackPoint(locations, locations.length, TRACK_ID)); myTracksProviderUtils.bulkInsertTrackPoint(locations, locations.length, TRACK_ID));
// Set expected mock behavior // Set expected mock behavior
trackFormatWriter.prepare(track, outputStream); trackFormatWriter.prepare(outputStream);
trackFormatWriter.writeHeader(); trackFormatWriter.writeHeader(track);
trackFormatWriter.writeBeginTrack(null); trackFormatWriter.writeBeginTrack(track, null);
trackFormatWriter.writeEndTrack(null); trackFormatWriter.writeEndTrack(track, null);
trackFormatWriter.writeFooter(); trackFormatWriter.writeFooter();
trackFormatWriter.close(); trackFormatWriter.close();
@@ -162,8 +162,8 @@ public class TrackWriterTest extends AndroidTestCase {
waypoint.setId(i + 1); waypoint.setId(i + 1);
} }
trackFormatWriter.prepare(track, outputStream); trackFormatWriter.prepare(outputStream);
trackFormatWriter.writeHeader(); trackFormatWriter.writeHeader(track);
// Expect reading/writing of the waypoints (except the first) // Expect reading/writing of the waypoints (except the first)
trackFormatWriter.writeBeginWaypoints(); trackFormatWriter.writeBeginWaypoints();
@@ -172,7 +172,7 @@ public class TrackWriterTest extends AndroidTestCase {
trackFormatWriter.writeEndWaypoints(); trackFormatWriter.writeEndWaypoints();
// Begin the track // Begin the track
trackFormatWriter.writeBeginTrack(locationEq(locations[0])); trackFormatWriter.writeBeginTrack(trackEq(track), locationEq(locations[0]));
// Write locations 1-2 // Write locations 1-2
trackFormatWriter.writeOpenSegment(); trackFormatWriter.writeOpenSegment();
@@ -190,7 +190,7 @@ public class TrackWriterTest extends AndroidTestCase {
trackFormatWriter.writeCloseSegment(); trackFormatWriter.writeCloseSegment();
// End the track // End the track
trackFormatWriter.writeEndTrack(locationEq(locations[5])); trackFormatWriter.writeEndTrack(trackEq(track), locationEq(locations[5]));
trackFormatWriter.writeFooter(); trackFormatWriter.writeFooter();
trackFormatWriter.close(); trackFormatWriter.close();
@@ -266,6 +266,33 @@ public class TrackWriterTest extends AndroidTestCase {
return null; return null;
} }
/**
* Track equals.
*
* @param track1 the track
*/
private Track trackEq(final Track track1) {
EasyMock.reportMatcher(new IArgumentMatcher() {
@Override
public boolean matches(Object object) {
if (object == null || track1 == null) {
return track1 == object;
}
Track track2 = (Track) object;
return track1.getName().equals(track2.getName());
}
@Override
public void appendTo(StringBuffer buffer) {
buffer.append("trackEq(");
buffer.append(track1);
buffer.append(")");
}
});
return null;
}
/** /**
* Fills the locations. * Fills the locations.
* *
@@ -90,28 +90,6 @@ public class StringUtilsTest extends AndroidTestCase {
assertEquals("328.08 ft", StringUtils.formatDistance(getContext(), 100, false)); assertEquals("328.08 ft", StringUtils.formatDistance(getContext(), 100, false));
} }
/**
* Tests {@link StringUtils#formatSpeed(android.content.Context, double,
* boolean, boolean)}.
*/
public void testFormatSpeed() {
// Speed in metric
assertEquals("36.00 km/h", StringUtils.formatSpeed(getContext(), 10, true, true));
// Speed in imperial
assertEquals("22.37 mi/h", StringUtils.formatSpeed(getContext(), 10, false, true));
// Pace in metric
assertEquals("1.67 min/km", StringUtils.formatSpeed(getContext(), 10, true, false));
// Pace in imperial
assertEquals("2.68 min/mi", StringUtils.formatSpeed(getContext(), 10, false, false));
// zero pace
assertEquals("0.00 min/km", StringUtils.formatSpeed(getContext(), 0, true, false));
assertEquals("0.00 min/mi", StringUtils.formatSpeed(getContext(), 0, false, false));
// speed is NaN
assertEquals("-", StringUtils.formatSpeed(getContext(), Double.NaN, true, true));
// speed is infinite
assertEquals("-", StringUtils.formatSpeed(getContext(), Double.NEGATIVE_INFINITY, true, true));
}
/** /**
* Tests {@link StringUtils#formatCData(String)}. * Tests {@link StringUtils#formatCData(String)}.
*/ */