Merge with 25704964a526fcfe09919af8f29c1e526b5409ec

This commit is contained in:
Bartlomiej Niechwiej
2010-10-04 16:34:38 -07:00
parent 256dac3a82
commit 34bb467b97
55 changed files with 942 additions and 486 deletions
@@ -1,4 +1,4 @@
#Thu Aug 12 18:54:52 BRT 2010 #Wed Sep 29 18:44:16 BRT 2010
eclipse.preferences.version=1 eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+4 -1
View File
@@ -56,8 +56,11 @@
<activity android:name="com.google.android.apps.mytracks.MyTracksSettings" <activity android:name="com.google.android.apps.mytracks.MyTracksSettings"
android:label="@string/mytracks_settings" /> android:label="@string/mytracks_settings" />
<activity android:name="com.google.android.apps.mytracks.AggregatedStatsActivity"
android:label="@string/aggregated_stats" />
<activity android:name="com.google.android.apps.mytracks.StatsActivity" <activity android:name="com.google.android.apps.mytracks.StatsActivity"
android:label="@string/statistics"/> android:label="@string/statistics" />
<activity android:name="com.google.android.apps.mytracks.ChartActivity" <activity android:name="com.google.android.apps.mytracks.ChartActivity"
android:label="@string/elevation_label"/> android:label="@string/elevation_label"/>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -17,11 +17,11 @@ limitations under the License.
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" <item android:state_window_focused="false"
android:drawable="@drawable/btn_arrow_left_default" /> android:drawable="@drawable/left_arrow_chart" />
<item android:state_pressed="true" <item android:state_pressed="true"
android:drawable="@drawable/btn_arrow_left_press" /> android:drawable="@drawable/left_arrow_chart_press" />
<item android:drawable="@drawable/btn_arrow_left_default" /> <item android:drawable="@drawable/left_arrow_chart" />
</selector> </selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -17,11 +17,11 @@ limitations under the License.
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" <item android:state_window_focused="false"
android:drawable="@drawable/btn_arrow_right_default" /> android:drawable="@drawable/left_arrow_stats" />
<item android:state_pressed="true" <item android:state_pressed="true"
android:drawable="@drawable/btn_arrow_right_press" /> android:drawable="@drawable/left_arrow_stats_press" />
<item android:drawable="@drawable/btn_arrow_right_default" /> <item android:drawable="@drawable/left_arrow_stats" />
</selector> </selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/left_arrow_track" />
<item android:state_pressed="true"
android:drawable="@drawable/left_arrow_track_press" />
<item android:drawable="@drawable/left_arrow_track" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/right_arrow_chart" />
<item android:state_pressed="true"
android:drawable="@drawable/right_arrow_chart_press" />
<item android:drawable="@drawable/right_arrow_chart" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/right_arrow_stats" />
<item android:state_pressed="true"
android:drawable="@drawable/right_arrow_stats_press" />
<item android:drawable="@drawable/right_arrow_stats" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/right_arrow_track" />
<item android:state_pressed="true"
android:drawable="@drawable/right_arrow_track_press" />
<item android:drawable="@drawable/right_arrow_track" />
</selector>
+6 -1
View File
@@ -21,9 +21,14 @@
android:orderInCategory="1001" android:orderInCategory="1001"
android:title="@string/settings"/> android:title="@string/settings"/>
<item android:id="@+id/menu_help" <item android:id="@+id/menu_aggregated_stats"
android:icon="@android:drawable/ic_menu_info_details" android:icon="@android:drawable/ic_menu_info_details"
android:orderInCategory="1002" android:orderInCategory="1002"
android:title="@string/aggregated_stats"/>
<item android:id="@+id/menu_help"
android:icon="@android:drawable/ic_menu_info_details"
android:orderInCategory="1003"
android:title="@string/help"/> android:title="@string/help"/>
</menu> </menu>
-1
View File
@@ -155,7 +155,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Dauer zwischen Ansagen:</string> <string name="settings_announcement_frequency">Dauer zwischen Ansagen:</string>
<string name="settings_announcement_frequency_summary">Kürzeste Zeit zwichen zwei Ansagen</string> <string name="settings_announcement_frequency_summary">Kürzeste Zeit zwichen zwei Ansagen</string>
<string name="settings_announcement_not_available_summary">Gesprochene Ansagen sind mit Ihrer Version von Android nicht verfügbar.</string>
<string name="settings_min_recording_distance">Kleinste Entfernung zwischen Punkten:</string> <string name="settings_min_recording_distance">Kleinste Entfernung zwischen Punkten:</string>
<string name="settings_min_recording_distance_summary">Kleinste Entfernung zwischen zwei Positionsaufzeichnungen</string> <string name="settings_min_recording_distance_summary">Kleinste Entfernung zwischen zwei Positionsaufzeichnungen</string>
-1
View File
@@ -158,7 +158,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Tiempo entre anuncios</string> <string name="settings_announcement_frequency">Tiempo entre anuncios</string>
<string name="settings_announcement_frequency_summary">Tiempo mínimo entre dos anuncios de voz</string> <string name="settings_announcement_frequency_summary">Tiempo mínimo entre dos anuncios de voz</string>
<string name="settings_announcement_not_available_summary">Conversión de anuncios de texto a voz no esta disponible en su versión de Android</string>
<string name="settings_min_recording_distance">Distancia mínima entre puntos consecutivos</string> <string name="settings_min_recording_distance">Distancia mínima entre puntos consecutivos</string>
<string name="settings_min_recording_distance_summary">Distancia mínima entre dos grabaciones de posición</string> <string name="settings_min_recording_distance_summary">Distancia mínima entre dos grabaciones de posición</string>
-1
View File
@@ -158,7 +158,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Fréquence des annonces</string> <string name="settings_announcement_frequency">Fréquence des annonces</string>
<string name="settings_announcement_frequency_summary">Temps minimum entre eux annonces vocales</string> <string name="settings_announcement_frequency_summary">Temps minimum entre eux annonces vocales</string>
<string name="settings_announcement_not_available_summary">La synthèse vocale n\'est pas disponible pour cette version d\'Android</string>
<string name="settings_min_recording_distance">Distance min. entre 2 points</string> <string name="settings_min_recording_distance">Distance min. entre 2 points</string>
<string name="settings_min_recording_distance_summary">Plus petite distance entre deux enregistrements de votre position</string> <string name="settings_min_recording_distance_summary">Plus petite distance entre deux enregistrements de votre position</string>
-1
View File
@@ -164,7 +164,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Intervallo fra due annunci:</string> <string name="settings_announcement_frequency">Intervallo fra due annunci:</string>
<string name="settings_announcement_frequency_summary">Tempo minimo fra due annunci audio</string> <string name="settings_announcement_frequency_summary">Tempo minimo fra due annunci audio</string>
<string name="settings_announcement_not_available_summary">Gli annunci audio non sono disponibili in questa varsione di Android</string>
<string name="settings_min_recording_distance">Distanza minima fra due punti successivi:</string> <string name="settings_min_recording_distance">Distanza minima fra due punti successivi:</string>
<string name="settings_min_recording_distance_summary">Distanza minima fra due successive registrazioni della posizione</string> <string name="settings_min_recording_distance_summary">Distanza minima fra due successive registrazioni della posizione</string>
+1 -1
View File
@@ -164,7 +164,7 @@ limitations under the License.
<string name="settings_announcement_frequency">定期通知</string> <string name="settings_announcement_frequency">定期通知</string>
<string name="settings_announcement_frequency_summary">音声通知間隔の最小時間</string> <string name="settings_announcement_frequency_summary">音声通知間隔の最小時間</string>
<string name="settings_announcement_not_available_summary">お使いのAndroidの版では音声読み上げ機能がありません</string>
<string name="settings_min_recording_distance">ポイント間の最短距離</string> <string name="settings_min_recording_distance">ポイント間の最短距離</string>
<string name="settings_min_recording_distance_summary">位置記録間の最短距離</string> <string name="settings_min_recording_distance_summary">位置記録間の最短距離</string>
+1 -1
View File
@@ -164,7 +164,7 @@ limitations under the License.
<string name="settings_announcement_frequency">Częstotliwość komunikatów</string> <string name="settings_announcement_frequency">Częstotliwość komunikatów</string>
<string name="settings_announcement_frequency_summary">Minimalny czas między dwoma komunikatami głosowymi</string> <string name="settings_announcement_frequency_summary">Minimalny czas między dwoma komunikatami głosowymi</string>
<string name="settings_announcement_not_available_summary">Brak wsparcia dla syntezy mowy w twojej wersji Androida</string>
<string name="settings_min_recording_distance">Min. odległość między punktami</string> <string name="settings_min_recording_distance">Min. odległość między punktami</string>
<string name="settings_min_recording_distance_summary">Najmniejsza odległość między dwoma zapisami twojej pozycji</string> <string name="settings_min_recording_distance_summary">Najmniejsza odległość między dwoma zapisami twojej pozycji</string>
-1
View File
@@ -157,7 +157,6 @@ limitations under the License.
<string name="settings_sharing">Compartilhamento</string> <string name="settings_sharing">Compartilhamento</string>
<string name="settings_announcement_frequency">Tempo entre anúncios</string> <string name="settings_announcement_frequency">Tempo entre anúncios</string>
<string name="settings_announcement_frequency_summary">Tempo mínimo entre dois anúncios de voz</string> <string name="settings_announcement_frequency_summary">Tempo mínimo entre dois anúncios de voz</string>
<string name="settings_announcement_not_available_summary">Anúncios de voz não estão disponíveis em sua versão do Android</string>
<string name="settings_min_recording_distance">Dist. mín. entre pontos</string> <string name="settings_min_recording_distance">Dist. mín. entre pontos</string>
<string name="settings_min_recording_distance_summary">Menor distância entre duas gravações da sua posição</string> <string name="settings_min_recording_distance_summary">Menor distância entre duas gravações da sua posição</string>
<string name="settings_max_recording_distance">Dist. máx. entre pontos</string> <string name="settings_max_recording_distance">Dist. máx. entre pontos</string>
+13 -13
View File
@@ -43,19 +43,19 @@ limitations under the License.
<string name="error_generic">Ett fel uppstod. Försök igen.</string> <string name="error_generic">Ett fel uppstod. Försök igen.</string>
<string name="error_track_does_not_exist">Spåret finns inte.</string> <string name="error_track_does_not_exist">Spåret finns inte.</string>
<string name="error_out_of_memory">Den här filen är för stor (måste vara mindre än 500 KB). Avbryter.</string> <string name="error_out_of_memory">Den här filen är för stor (måste vara mindre än 500 KB). Avbryter.</string>
<string name="error_unable_to_insert_marker">Kan inte skapa markör. Ingen possition tillgänglig.</string> <string name="error_unable_to_insert_marker">Kan inte skapa markör. Ingen position tillgänglig.</string>
<!-- Status messages --> <!-- Status messages -->
<string name="wait_for_fix">Väntar på bättre GPS-posstion. Se till att himlen är synlig.</string> <string name="wait_for_fix">Väntar på bättre GPS-position. Se till att himlen är synlig.</string>
<string name="status_now_recording">Spelar nu in ett nytt spår.</string> <string name="status_now_recording">Spelar nu in ett nytt spår.</string>
<string name="status_stopped_recording">Stoppade inspelning av ditt spår.</string> <string name="status_stopped_recording">Stoppade inspelning av ditt spår.</string>
<string name="status_new_mymap_has_been_created">En ny Google My Map har skapats på maps.google.com.</string> <string name="status_new_mymap_has_been_created">En ny Google My Map har skapats på maps.google.com.</string>
<string name="status_tracks_have_been_uploaded">Ditt spår har skickats till maps.google.com.</string> <string name="status_tracks_have_been_uploaded">Ditt spår har skickats till maps.google.com.</string>
<string name="status_tracks_have_been_uploaded_to_new_doc">Ditt spår har skockats till ett nytt kalkylblad på docs.google.com.</string> <string name="status_tracks_have_been_uploaded_to_new_doc">Ditt spår har skickats till ett nytt kalkylblad på docs.google.com.</string>
<string name="status_tracks_have_been_uploaded_to_docs">Ditt spår har skickats till docs.google.com.</string> <string name="status_tracks_have_been_uploaded_to_docs">Ditt spår har skickats till docs.google.com.</string>
<string name="status_mymap_info">Det kan ibland ta några minuter innan nya spår visas.</string> <string name="status_mymap_info">Det kan ibland ta några minuter innan nya spår visas.</string>
<string name="status_enable_gps">Klicka här för att gå till Säkerhet och Platsinställningar och kryssa i \'aktivera gps-satteliter\'. Tryck på tillbakaknappen för att återgå.</string> <string name="status_enable_gps">Klicka här för att gå till Säkerhet och Platsinställningar och kryssa i \'aktivera gps-satteliter\'. Tryck på tillbakaknappen för att återgå.</string>
<string name="status_approximate_location">OBS: En ungerfärlig position som avgörs från mobilmaster och WIFI-nät visas.</string> <string name="status_approximate_location">OBS: En ungefärlig position som avgörs från mobilmaster och WIFI-nät visas.</string>
<string name="status_no_location">En position kan för närvarande inte fastställas. Aktivera mer platskällor (se Säkerhet och Platsinställningar) och se till att himlen är synlig. Försök igen senare.</string> <string name="status_no_location">En position kan för närvarande inte fastställas. Aktivera mer platskällor (se Säkerhet och Platsinställningar) och se till att himlen är synlig. Försök igen senare.</string>
<string name="status_waypoint_inserted">En positionsmarkör har lagts in.</string> <string name="status_waypoint_inserted">En positionsmarkör har lagts in.</string>
<string name="status_statistics_inserted">En statistikmarkör har lagts in.</string> <string name="status_statistics_inserted">En statistikmarkör har lagts in.</string>
@@ -71,7 +71,7 @@ limitations under the License.
<string name="progress_message_sending_mymaps">Skickar spår till Google Maps...</string> <string name="progress_message_sending_mymaps">Skickar spår till Google Maps...</string>
<string name="progress_message_creating_map">Skapar en ny My Map...</string> <string name="progress_message_creating_map">Skapar en ny My Map...</string>
<string name="import_progress_message">Importerar ett GPS-spår. Detta kan ta en stund.</string> <string name="import_progress_message">Importerar ett GPS-spår. Detta kan ta en stund.</string>
<string name="write_progress_message">Skriver til till SD-kortet. Detta kan ta en stund.</string> <string name="write_progress_message">Skriver till SD-kortet. Detta kan ta en stund.</string>
<string name="progress_message_updating_track">Uppdaterar spårstatistik...</string> <string name="progress_message_updating_track">Uppdaterar spårstatistik...</string>
<!-- Used by DeleteAllDataHandler --> <!-- Used by DeleteAllDataHandler -->
@@ -125,7 +125,7 @@ limitations under the License.
<!-- Export All --> <!-- Export All -->
<string name="tracklist_btn_export_all">Exportera alla spår</string> <string name="tracklist_btn_export_all">Exportera alla spår</string>
<string name="export_done">Exporteringen slutfördes</string> <string name="export_done">Exporteringen slutfördes</string>
<string name="tracklist_pick_one">Välj ett spår. TryckPress&amp;håll för fler alternativ.</string> <string name="tracklist_pick_one">Välj ett spår. Tryck&amp;håll för fler alternativ.</string>
<string name="tracklist_all_tracks">Alla spår:</string> <string name="tracklist_all_tracks">Alla spår:</string>
<string name="tracklist_this_track">Det här spåret</string> <string name="tracklist_this_track">Det här spåret</string>
<string name="tracklist_share_track">Dela med vänner...</string> <string name="tracklist_share_track">Dela med vänner...</string>
@@ -143,13 +143,13 @@ limitations under the License.
<string name="waypointslist_show_waypoint">Visa på karta</string> <string name="waypointslist_show_waypoint">Visa på karta</string>
<string name="waypointslist_btn_insert_waypoint">Infoga positionspunkt</string> <string name="waypointslist_btn_insert_waypoint">Infoga positionspunkt</string>
<string name="waypointslist_btn_insert_statistics">Infoga statistik</string> <string name="waypointslist_btn_insert_statistics">Infoga statistik</string>
<string name="waypointslist_pick_one">Välj en markör. TryckPress&amp;håll för fler alternativ.</string> <string name="waypointslist_pick_one">Välj en markör. Tryck&amp;håll för fler alternativ.</string>
<!-- MyTracks Details --> <!-- MyTracks Details -->
<string name="trackdetails_title">Spårdetaljer</string> <string name="trackdetails_title">Spårdetaljer</string>
<string name="trackdetails_name">Spårnamn</string> <string name="trackdetails_name">Spårnamn</string>
<string name="trackdetails_description">Spårbeskrivning</string> <string name="trackdetails_description">Spårbeskrivning</string>
<string name="trackdetails_category">Aktivitetstyp (t ex vandring, cyckling, låpning, klättring...)</string> <string name="trackdetails_category">Aktivitetstyp (t ex vandring, cyckling, löpning, klättring...)</string>
<!-- MyTracks Waypoint Details --> <!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Markördetaljer</string> <string name="waypointdetails_title">Markördetaljer</string>
@@ -165,7 +165,7 @@ limitations under the License.
<string name="settings_announcement_frequency">Meddelandefrekvens</string> <string name="settings_announcement_frequency">Meddelandefrekvens</string>
<string name="settings_announcement_frequency_summary">Minimumtid mellan två röstmeddelanden</string> <string name="settings_announcement_frequency_summary">Minimumtid mellan två röstmeddelanden</string>
<string name="settings_announcement_not_available_summary">Text-till-talmeddelanden är inte tillgängligt i din version av Android</string>
<string name="settings_min_recording_distance">Min distans mellan två punkter</string> <string name="settings_min_recording_distance">Min distans mellan två punkter</string>
<string name="settings_min_recording_distance_summary">Minsta distans mellan två inspelningar av din position</string> <string name="settings_min_recording_distance_summary">Minsta distans mellan två inspelningar av din position</string>
@@ -199,11 +199,11 @@ limitations under the License.
<string name="settings_signal_sampling_frequency_summary">Tid mellan två inspelningar av telefonens signalstyrka</string> <string name="settings_signal_sampling_frequency_summary">Tid mellan två inspelningar av telefonens signalstyrka</string>
<string name="settings_split_frequency">Delningsfrekvens</string> <string name="settings_split_frequency">Delningsfrekvens</string>
<string name="settings_split_frequency_summary">Maximala tiden eller distansen varefter en ny delnginsmarkör kommer infogas</string> <string name="settings_split_frequency_summary">Maximala tiden eller distansen varefter en ny delningsmarkör kommer infogas</string>
<string name="pace_label">Tempo</string> <string name="pace_label">Tempo</string>
<!-- MyTracksSky --> <!-- MyTracksSky -->
<string name="mytracks_sky">GPS-satteliter</string> <string name="mytracks_sky">GPS-satelliter</string>
<!-- My Maps --> <!-- My Maps -->
<string name="select_map_message">Välje en karta från ditt konto:</string> <string name="select_map_message">Välje en karta från ditt konto:</string>
@@ -240,7 +240,7 @@ limitations under the License.
<!-- Visibility --> <!-- Visibility -->
<string name="public_map">Publik</string> <string name="public_map">Publik</string>
<string name="unlisted_map">Olistat</string> <string name="unlisted_map">Olistat</string>
<string name="mapslist_no_maps_available">Du har inge kartor än.</string> <string name="mapslist_no_maps_available">Du har inga kartor än.</string>
<!-- Statistics View --> <!-- Statistics View -->
<!-- Average Moving --> <!-- Average Moving -->
@@ -253,7 +253,7 @@ limitations under the License.
<string name="elevation_chart_name">Höjddiagram</string> <string name="elevation_chart_name">Höjddiagram</string>
<string name="elevation_gain_label">Höjdvinst</string> <string name="elevation_gain_label">Höjdvinst</string>
<string name="elevation_label">Höjt</string> <string name="elevation_label">Höjd</string>
<string name="latitude_label">Latitud</string> <string name="latitude_label">Latitud</string>
<string name="longitude_label">Longitud</string> <string name="longitude_label">Longitud</string>
+1 -1
View File
@@ -167,7 +167,7 @@ limitations under the License.
<string name="settings_announcement_frequency">提示频度</string> <string name="settings_announcement_frequency">提示频度</string>
<string name="settings_announcement_frequency_summary">语音提示的最小时间间隔</string> <string name="settings_announcement_frequency_summary">语音提示的最小时间间隔</string>
<string name="settings_announcement_not_available_summary">你所使用的 Android 版本不支持文本到语音转换</string>
<string name="settings_min_recording_distance">最小记录间距</string> <string name="settings_min_recording_distance">最小记录间距</string>
<string name="settings_min_recording_distance_summary">位置记录的最小间隔距离</string> <string name="settings_min_recording_distance_summary">位置记录的最小间隔距离</string>
+1
View File
@@ -38,6 +38,7 @@ limitations under the License.
<string name="share_url_only_key">shareUrlOnly</string> <string name="share_url_only_key">shareUrlOnly</string>
<string name="signal_sampling_frequency_key">signalSamplingFrequency</string> <string name="signal_sampling_frequency_key">signalSamplingFrequency</string>
<string name="split_frequency_key">splitFrequency</string> <string name="split_frequency_key">splitFrequency</string>
<string name="debug_draw_bounds_key">debugDrawBounds</string>
<!-- Preference keys which keep transient state (not user preferences) - DO NOT TRANSLATE --> <!-- Preference keys which keep transient state (not user preferences) - DO NOT TRANSLATE -->
<string name="recording_track_key">recordingTrack</string> <string name="recording_track_key">recordingTrack</string>
+1
View File
@@ -32,6 +32,7 @@ limitations under the License.
<string name="choose_track_message">Please select the track to view</string> <string name="choose_track_message">Please select the track to view</string>
<string name="current_track">Current track</string> <string name="current_track">Current track</string>
<string name="current_segment">Last segment</string> <string name="current_segment">Last segment</string>
<string name="aggregated_stats">Aggregated Statistics</string>
<!-- Error messages --> <!-- Error messages -->
<string name="error_no_location_manager">No location manager was found on this device!</string> <string name="error_no_location_manager">No location manager was found on this device!</string>
+9
View File
@@ -133,4 +133,13 @@ limitations under the License.
android:persistent="false" /> android:persistent="false" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>
<!-- Debug settings - uncomment this while debugging only -->
<!--
<PreferenceScreen android:title="Debug">
<CheckBoxPreference android:key="@string/debug_draw_bounds_key"
android:title="Draw bounding box"
android:defaultValue="false" />
</PreferenceScreen>
-->
</PreferenceScreen> </PreferenceScreen>
@@ -25,5 +25,8 @@ public class MyMapsConstants {
"http://maps.google.com/maps/ms"; "http://maps.google.com/maps/ms";
public static final String MAPSHOP_SERVICE = MapsClient.SERVICE; public static final String MAPSHOP_SERVICE = MapsClient.SERVICE;
/**
* Private constructor to prevent instantiation.
*/
private MyMapsConstants() { } private MyMapsConstants() { }
} }
@@ -0,0 +1,114 @@
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Window;
import android.widget.ScrollView;
import android.widget.TextView;
import java.util.List;
/**
* Activity for viewing the combined statistics for all the recorded tracks.
*
* Other features to add - menu items to change setings.
*
* @author Fergus Nelson
*/
public class AggregatedStatsActivity extends Activity implements
OnSharedPreferenceChangeListener {
private final StatsUtilities utils;
private MyTracksProviderUtils tracksProvider;
private boolean metricUnits = true;
public AggregatedStatsActivity() {
this.utils = new StatsUtilities(this);
this.tracksProvider = MyTracksProviderUtils.Factory.get(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Log.d(MyTracksConstants.TAG, "StatsActivity: onSharedPreferences changed "
+ key);
if (key != null) {
if (key.equals(R.string.metric_units_key)) {
metricUnits = sharedPreferences.getBoolean(
getString(R.string.metric_units_key), true);
utils.setMetricUnits(metricUnits);
utils.updateUnits();
loadAggregatedStats();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// We don't need a window title bar:
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.stats);
ScrollView sv = ((ScrollView) findViewById(R.id.scrolly));
sv.setScrollBarStyle(ScrollView.SCROLLBARS_OUTSIDE_INSET);
SharedPreferences preferences = getSharedPreferences(
MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), true);
preferences.registerOnSharedPreferenceChangeListener(this);
}
utils.setMetricUnits(metricUnits);
utils.updateUnits();
utils.setSpeedLabel(R.id.speed_label, R.string.speed, R.string.pace_label);
utils.setSpeedLabels();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
if (metrics.heightPixels > 600) {
((TextView) findViewById(R.id.speed_register)).setTextSize(80.0f);
}
loadAggregatedStats();
}
/**
* 1. Reads tracks from the db
* 2. Merges the trip stats from the tracks
* 3. Updates the view
*/
private void loadAggregatedStats() {
List<Track> tracks = retrieveTracks();
TripStatistics rollingStats = null;
if (!tracks.isEmpty()) {
rollingStats = new TripStatistics(tracks.iterator().next()
.getStatistics());
for (int i = 1; i < tracks.size(); i++) {
rollingStats.merge(tracks.get(i).getStatistics());
}
}
updateView(rollingStats);
}
private List<Track> retrieveTracks() {
return tracksProvider.getAllTracks();
}
private void updateView(TripStatistics aggStats) {
if (aggStats != null) {
utils.setAllStats(aggStats);
}
}
}
@@ -176,11 +176,15 @@ public class ChartActivity extends Activity implements
final int idColumnIdx = final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID); cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
ArrayList<double[]> data = new ArrayList<double[]>(); ArrayList<double[]> data = new ArrayList<double[]>();
// Need two locations so we can keep track of the last location.
Location location = new Location("");
do { do {
lastSeenLocationId = cursor.getLong(idColumnIdx); lastSeenLocationId = cursor.getLong(idColumnIdx);
Location location = providerUtils.createLocation(cursor); providerUtils.fillLocation(cursor, location);
if (location != null && MyTracksUtils.isValidLocation(location)) { if (MyTracksUtils.isValidLocation(location)) {
data.add(getDataPoint(location, track)); double[] point = new double[3];
location = getDataPoint(location, track, point);
data.add(point);
} }
} while (cursor.moveToPrevious()); } while (cursor.moveToPrevious());
cv.addDataPoints(data); cv.addDataPoints(data);
@@ -461,11 +465,14 @@ public class ChartActivity extends Activity implements
* data[1] = the elevation * data[1] = the elevation
* data[2] = the speed * data[2] = the speed
* *
* @param location a location * This must be called in order for each point.
* @return the data point *
* @param location the location to get data for (this method takes ownership of that location)
* @param track the track to get data from
* @param result the resulting point to fill out
* @return the previous location, now available for reuse
*/ */
public double[] getDataPoint(Location location, Track track) { private Location getDataPoint(Location location, Track track, double[] result) {
double[] result = new double[3];
switch (mode) { switch (mode) {
case BY_DISTANCE: case BY_DISTANCE:
result[0] = profileLength; result[0] = profileLength;
@@ -512,8 +519,16 @@ public class ChartActivity extends Activity implements
// Format as hours per unit // Format as hours per unit
result[2] = (60.0 / result[2]); result[2] = (60.0 / result[2]);
} }
Location oldLastLocation = lastLocation;
lastLocation = location; lastLocation = location;
return result;
if (oldLastLocation == null) {
// No previous location, but return a blank one for reuse
return new Location("");
}
return oldLastLocation;
} }
/** /**
@@ -556,6 +571,8 @@ public class ChartActivity extends Activity implements
try { try {
final ArrayList<double[]> theData = new ArrayList<double[]>(); final ArrayList<double[]> theData = new ArrayList<double[]>();
int points = 0; int points = 0;
// Need two locations so we can keep track of the last location.
Location location = new Location("");
while (lastLocationRead < track.getStopId()) { while (lastLocationRead < track.getStopId()) {
cursor = providerUtils.getLocationsCursor( cursor = providerUtils.getLocationsCursor(
selectedTrackId, lastLocationRead, bufferSize, false); selectedTrackId, lastLocationRead, bufferSize, false);
@@ -567,11 +584,12 @@ public class ChartActivity extends Activity implements
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID); cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
points++; points++;
Location location = providerUtils.createLocation(cursor); providerUtils.fillLocation(cursor, location);
if (MyTracksUtils.isValidLocation(location)) { if (MyTracksUtils.isValidLocation(location)) {
lastLocationRead = lastSeenLocationId = lastLocationRead = lastSeenLocationId =
cursor.getLong(idColumnIdx); cursor.getLong(idColumnIdx);
double[] point = getDataPoint(location, track); double[] point = new double[3];
location = getDataPoint(location, track, point);
if (points % chartSamplingFrequency == 0) { if (points % chartSamplingFrequency == 0) {
theData.add(point); theData.add(point);
} }
@@ -78,6 +78,11 @@ class MenuManager {
activity.startActivity(startIntent); activity.startActivity(startIntent);
return true; return true;
} }
case R.id.menu_aggregated_stats: {
Intent startIntent = new Intent(activity, AggregatedStatsActivity.class);
activity.startActivity(startIntent);
return true;
}
case R.id.menu_help: { case R.id.menu_help: {
Intent startIntent = new Intent(activity, WelcomeActivity.class); Intent startIntent = new Intent(activity, WelcomeActivity.class);
activity.startActivity(startIntent); activity.startActivity(startIntent);
@@ -75,7 +75,6 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Random;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@@ -217,40 +216,25 @@ public class MyTracks extends TabActivity implements OnTouchListener,
* Tabs/View navigation: * Tabs/View navigation:
*/ */
private static final int NUM_TABS = 3;
private int currentTab = 0;
private NavControls navControls; private NavControls navControls;
private final int icons[] = /** Icons shown on the left for each tab. */
{ R.drawable.arrow_grey, R.drawable.menu_by_time, private final int leftIcons[] =
R.drawable.menu_elevation }; { R.drawable.left_arrow_chart_stateful,
R.drawable.left_arrow_track_stateful,
R.drawable.left_arrow_stats_stateful };
/** Icons shown on the right for each tab. */
private final int rightIcons[] =
{ R.drawable.right_arrow_stats_stateful,
R.drawable.right_arrow_chart_stateful,
R.drawable.right_arrow_track_stateful };
private final Runnable nextActivity = new Runnable() { private final Runnable changeTab = new Runnable() {
public void run() { public void run() {
currentTab = (currentTab + 1) % NUM_TABS; getTabHost().setCurrentTab(navControls.getCurrentIcons());
navControls.setLeftIcon(icons[(currentTab + NUM_TABS - 1) % NUM_TABS]);
navControls.setRightIcon(icons[(currentTab + NUM_TABS + 1) % NUM_TABS]);
getTabHost().setCurrentTab(currentTab);
navControls.show();
} }
}; };
private final Runnable prevActivity = new Runnable() {
public void run() {
currentTab--;
if (currentTab < 0) {
currentTab = NUM_TABS - 1;
}
navControls.setLeftIcon(icons[(currentTab + NUM_TABS - 1) % NUM_TABS]);
navControls.setRightIcon(icons[(currentTab + NUM_TABS + 1) % NUM_TABS]);
getTabHost().setCurrentTab(currentTab);
navControls.show();
}
};
private final Random random = new Random();
public static MyTracks getInstance() { public static MyTracks getInstance() {
return instance; return instance;
} }
@@ -311,9 +295,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
LayoutParams params = LayoutParams params =
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
layout.setLayoutParams(params); layout.setLayoutParams(params);
navControls = new NavControls(this, layout, prevActivity, nextActivity); navControls = new NavControls(this, layout, leftIcons, rightIcons, changeTab);
navControls.setLeftIcon(icons[NUM_TABS - 1]);
navControls.setRightIcon(icons[1]);
navControls.show(); navControls.show();
tabHost.addView(layout); tabHost.addView(layout);
layout.setOnTouchListener(this); layout.setOnTouchListener(this);
@@ -376,8 +358,6 @@ public class MyTracks extends TabActivity implements OnTouchListener,
Log.d(MyTracksConstants.TAG, "MyTracks.onResume"); Log.d(MyTracksConstants.TAG, "MyTracks.onResume");
super.onResume(); super.onResume();
tryBindTrackRecordingService(); tryBindTrackRecordingService();
navControls.setLeftIcon(icons[(currentTab + NUM_TABS - 1) % NUM_TABS]);
navControls.setRightIcon(icons[(currentTab + NUM_TABS + 1) % NUM_TABS]);
} }
@Override @Override
@@ -828,33 +808,33 @@ public class MyTracks extends TabActivity implements OnTouchListener,
* This is for debugging and testing only. Useful if there is no GPS signal * This is for debugging and testing only. Useful if there is no GPS signal
* available. * available.
*/ */
public void recordRandomLocation() { // public void recordRandomLocation() {
if (trackRecordingService != null) { // if (trackRecordingService != null) {
Location loc = new Location("gps"); // Location loc = new Location("gps");
double latitude = 37.5 + random.nextDouble() / 1000; // double latitude = 37.5 + random.nextDouble() / 1000;
double longitude = -120.0 + random.nextDouble() / 1000; // double longitude = -120.0 + random.nextDouble() / 1000;
loc.setLatitude(latitude); // loc.setLatitude(latitude);
loc.setLongitude(longitude); // loc.setLongitude(longitude);
loc.setAltitude(random.nextDouble() * 100); // loc.setAltitude(random.nextDouble() * 100);
loc.setTime(System.currentTimeMillis()); // loc.setTime(System.currentTimeMillis());
loc.setSpeed(random.nextFloat()); // loc.setSpeed(random.nextFloat());
MyTracksMap map = // MyTracksMap map =
(MyTracksMap) getLocalActivityManager().getActivity("tab1"); // (MyTracksMap) getLocalActivityManager().getActivity("tab1");
if (map != null) { // if (map != null) {
map.onLocationChanged(loc); // map.onLocationChanged(loc);
} // }
StatsActivity stats = // StatsActivity stats =
(StatsActivity) getLocalActivityManager().getActivity("tab2"); // (StatsActivity) getLocalActivityManager().getActivity("tab2");
if (stats != null) { // if (stats != null) {
stats.onLocationChanged(loc); // stats.onLocationChanged(loc);
} // }
try { // try {
trackRecordingService.recordLocation(loc); // trackRecordingService.recordLocation(loc);
} catch (RemoteException e) { // } catch (RemoteException e) {
Log.e(MyTracksConstants.TAG, "MyTracks", e); // Log.e(MyTracksConstants.TAG, "MyTracks", e);
} // }
} // }
} // }
/** /**
* Resets status information for sending to MyMaps/Docs. * Resets status information for sending to MyMaps/Docs.
@@ -70,8 +70,9 @@ import android.widget.Toast;
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
*/ */
public class MyTracksMap extends MapActivity public class MyTracksMap extends MapActivity
implements LocationListener, SensorEventListener, View.OnTouchListener, implements View.OnTouchListener, View.OnClickListener,
View.OnClickListener, SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
private static final int TRACKPOINT_BUFFER_SIZE = 1024;
// Saved instance state keys: // Saved instance state keys:
// --------------------------- // ---------------------------
@@ -82,9 +83,9 @@ public class MyTracksMap extends MapActivity
public static final String KEY_HAVE_GOOD_FIX = "haveGoodFix"; public static final String KEY_HAVE_GOOD_FIX = "haveGoodFix";
/** /**
* The currently selected track (or null if nothing selected). * The ID of the currently selected track (or -1 if nothing selected).
*/ */
private Track selectedTrack; private long selectedTrackId = -1;
/** /**
* The id of the currently recording track. * The id of the currently recording track.
@@ -97,6 +98,12 @@ public class MyTracksMap extends MapActivity
*/ */
private boolean keepMyLocationVisible; private boolean keepMyLocationVisible;
/**
* Id of the first location that was seen when reading tracks from the
* provider.
*/
private long firstSeenLocationId = -1;
/** /**
* Id of the last location that was seen when reading tracks from the * Id of the last location that was seen when reading tracks from the
* provider. This is used to determine which locations are new compared to the * provider. This is used to determine which locations are new compared to the
@@ -130,19 +137,13 @@ public class MyTracksMap extends MapActivity
* A thread with a looper. Post to updateTrackHandler to execute Runnables on * A thread with a looper. Post to updateTrackHandler to execute Runnables on
* this thread. * this thread.
*/ */
private final HandlerThread updateTrackThread = private HandlerThread updateTrackThread;
new HandlerThread("updateTrackThread");
/** Handler for updateTrackThread */ /** Handler for updateTrackThread */
private Handler updateTrackHandler; private Handler updateTrackHandler;
private MyTracksProviderUtils providerUtils; private MyTracksProviderUtils providerUtils;
/**
* This value is used to decide how many points to display.
*/
private int samplingFrequency = 1;
/** /**
* A runnable that updates the track from the provider (looking for points * A runnable that updates the track from the provider (looking for points
* added after "lastSeenLocationId". * added after "lastSeenLocationId".
@@ -150,7 +151,7 @@ public class MyTracksMap extends MapActivity
private final Runnable updateTrackRunnable = new Runnable() { private final Runnable updateTrackRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
if (selectedTrack == null) { if (!isATrackSelected()) {
return; return;
} }
readAllNewTrackPoints(); readAllNewTrackPoints();
@@ -163,11 +164,12 @@ public class MyTracksMap extends MapActivity
private Runnable restoreTrackRunnable = new Runnable() { private Runnable restoreTrackRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
if (selectedTrack == null) { if (!isATrackSelected()) {
return; return;
} }
mapOverlay.clearPoints(); mapOverlay.clearPoints();
lastSeenLocationId = selectedTrack.getStartId(); firstSeenLocationId = -1;
lastSeenLocationId = -1;
readAllNewTrackPoints(); readAllNewTrackPoints();
} }
}; };
@@ -178,7 +180,7 @@ public class MyTracksMap extends MapActivity
private final Runnable restoreWaypointsRunnable = new Runnable() { private final Runnable restoreWaypointsRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
if (selectedTrack == null) { if (!isATrackSelected()) {
return; return;
} }
@@ -188,7 +190,7 @@ public class MyTracksMap extends MapActivity
// We will silently drop extra waypoints to make the app responsive. // We will silently drop extra waypoints to make the app responsive.
// TODO: Try to only load the waypoints in the view port. // TODO: Try to only load the waypoints in the view port.
cursor = providerUtils.getWaypointsCursor( cursor = providerUtils.getWaypointsCursor(
selectedTrack.getId(), 0, selectedTrackId, 0,
MyTracksConstants.MAX_DISPLAYED_WAYPOINTS_POINTS); MyTracksConstants.MAX_DISPLAYED_WAYPOINTS_POINTS);
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
@@ -219,10 +221,9 @@ public class MyTracksMap extends MapActivity
public void run() { public void run() {
uiHandler.post(new Runnable() { uiHandler.post(new Runnable() {
public void run() { public void run() {
showTrack(selectedTrack); showTrack(selectedTrackId);
mapOverlay.setSelectedTrack(selectedTrack); mapOverlay.setTrackDrawingEnabled(isATrackSelected());
mapOverlay.setShowEndMarker(selectedTrack == null || mapOverlay.setShowEndMarker(!isRecordingSelected());
selectedTrack.getId() != recordingTrackId);
mapView.invalidate(); mapView.invalidate();
busyPane.setVisibility(View.GONE); busyPane.setVisibility(View.GONE);
updateOptionsButton(); updateOptionsButton();
@@ -316,6 +317,7 @@ public class MyTracksMap extends MapActivity
locationManager = locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE); (LocationManager) getSystemService(Context.LOCATION_SERVICE);
updateTrackThread = new HandlerThread("updateTrackThread");
updateTrackThread.start(); updateTrackThread.start();
updateTrackHandler = new Handler(updateTrackThread.getLooper()); updateTrackHandler = new Handler(updateTrackThread.getLooper());
@@ -331,8 +333,7 @@ public class MyTracksMap extends MapActivity
// No track is being recorded. We should not be here. // No track is being recorded. We should not be here.
return; return;
} }
if (selectedTrack == null if (!isRecordingSelected()) {
|| selectedTrack.getId() != recordingTrackId) {
// No track, or one other than the recording track is selected, // No track, or one other than the recording track is selected,
// don't bother. // don't bother.
return; return;
@@ -348,7 +349,7 @@ public class MyTracksMap extends MapActivity
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
Log.d(MyTracksConstants.TAG, Log.d(MyTracksConstants.TAG,
"MyTracksMap: ContentObserver.onChange waypoints"); "MyTracksMap: ContentObserver.onChange waypoints");
if (selectedTrack == null) { if (!isATrackSelected()) {
return; return;
} }
updateTrackHandler.post(restoreWaypointsRunnable); updateTrackHandler.post(restoreWaypointsRunnable);
@@ -371,20 +372,29 @@ public class MyTracksMap extends MapActivity
setSelectedTrack(selectedTrackId); setSelectedTrack(selectedTrackId);
} }
updateOptionsButton(); updateOptionsButton();
mapOverlay.setDrawBounds(preferences.getBoolean(
getString(R.string.debug_draw_bounds_key), false));
preferences.registerOnSharedPreferenceChangeListener(this); preferences.registerOnSharedPreferenceChangeListener(this);
} }
} }
protected void setupZoomControls() { /**
mapView.setBuiltInZoomControls(true); * Returns whether there's a track currently selected for display.
*/
private boolean isATrackSelected() {
return selectedTrackId >= 0;
} }
@Override /**
protected void onDestroy() { * Returns whether we're currently recording the same track that's selected
// Final cleanup before activity is destroyed. * for display.
// May not be called at all in some situations. */
Log.d(MyTracksConstants.TAG, "MyTracksMap.onDestroy"); private boolean isRecordingSelected() {
super.onDestroy(); return isATrackSelected() && selectedTrackId == recordingTrackId;
}
protected void setupZoomControls() {
mapView.setBuiltInZoomControls(true);
} }
@Override @Override
@@ -449,10 +459,9 @@ public class MyTracksMap extends MapActivity
// While this activity was paused the user may have deleted the selected // While this activity was paused the user may have deleted the selected
// track. In that case the map overlay needs to be cleared: // track. In that case the map overlay needs to be cleared:
Track track = mapOverlay.getSelectedTrack(); if (isATrackSelected() && !providerUtils.trackExists(selectedTrackId)) {
if (track != null && !providerUtils.trackExists(track.getId())) {
// The recording track must have been deleted meanwhile. // The recording track must have been deleted meanwhile.
mapOverlay.setSelectedTrack(null); mapOverlay.setTrackDrawingEnabled(false);
mapView.invalidate(); mapView.invalidate();
} }
} }
@@ -514,10 +523,10 @@ public class MyTracksMap extends MapActivity
+ gpsProvider.getName()); + gpsProvider.getName());
} }
locationManager.requestLocationUpdates(gpsProvider.getName(), locationManager.requestLocationUpdates(gpsProvider.getName(),
0 /*minTime*/, 0 /*minDist*/, this); 0 /*minTime*/, 0 /*minDist*/, locationListener);
try { try {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, this); 1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, locationListener);
} catch (RuntimeException e) { } catch (RuntimeException e) {
// If anything at all goes wrong with getting a cell location do not // If anything at all goes wrong with getting a cell location do not
// abort. Cell location is not essential to this app. // abort. Cell location is not essential to this app.
@@ -535,7 +544,7 @@ public class MyTracksMap extends MapActivity
Log.d(MyTracksConstants.TAG, Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now registering sensor listeners."); "MyTracksMap: Now registering sensor listeners.");
sensorManager.registerListener( sensorManager.registerListener(
this, compass, SensorManager.SENSOR_DELAY_UI); sensorListener, compass, SensorManager.SENSOR_DELAY_UI);
} }
/** /**
@@ -545,12 +554,12 @@ public class MyTracksMap extends MapActivity
if (locationManager != null) { if (locationManager != null) {
Log.d(MyTracksConstants.TAG, Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now unregistering location listeners."); "MyTracksMap: Now unregistering location listeners.");
locationManager.removeUpdates(this); locationManager.removeUpdates(locationListener);
} }
if (sensorManager != null) { if (sensorManager != null) {
Log.d(MyTracksConstants.TAG, Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now unregistering sensor listeners."); "MyTracksMap: Now unregistering sensor listeners.");
sensorManager.unregisterListener(this); sensorManager.unregisterListener(sensorListener);
} }
} }
@@ -579,7 +588,7 @@ public class MyTracksMap extends MapActivity
*/ */
private void updateOptionsButton() { private void updateOptionsButton() {
optionsBtn.setVisibility( optionsBtn.setVisibility(
selectedTrack != null ? View.VISIBLE : View.INVISIBLE); isATrackSelected() ? View.VISIBLE : View.INVISIBLE);
} }
/** /**
@@ -632,12 +641,18 @@ public class MyTracksMap extends MapActivity
/** /**
* Zooms and pans the map so that the given track is visible. * Zooms and pans the map so that the given track is visible.
* *
* @param track a given track * @param trackId a given track ID
*/ */
public void showTrack(Track track) { public void showTrack(long trackId) {
if (track == null || mapView == null || track.getNumberOfPoints() < 2) { if (mapView == null) {
return; return;
} }
Track track = providerUtils.getTrack(trackId);
if (track == null || track.getNumberOfPoints() < 2) {
return;
}
TripStatistics stats = track.getStatistics(); TripStatistics stats = track.getStatistics();
int bottom = stats.getBottom(); int bottom = stats.getBottom();
int left = stats.getLeft(); int left = stats.getLeft();
@@ -680,24 +695,30 @@ public class MyTracksMap extends MapActivity
* @param trackId a given track id * @param trackId a given track id
*/ */
public void setSelectedTrack(final long trackId) { public void setSelectedTrack(final long trackId) {
if (selectedTrack != null && selectedTrack.getId() == trackId) { if (selectedTrackId == trackId) {
// Selected track did not change, nothing to do. // Selected track did not change, nothing to do.
mapOverlay.setSelectedTrack(selectedTrack); mapOverlay.setTrackDrawingEnabled(isATrackSelected());
mapView.invalidate(); mapView.invalidate();
updateOptionsButton(); updateOptionsButton();
return; return;
} }
if (trackId < 0) { if (trackId < 0) {
// Remove selection. // Remove selection.
selectedTrack = null; selectedTrackId = -1;
mapOverlay.setSelectedTrack(null); mapOverlay.setTrackDrawingEnabled(false);
mapOverlay.clearWaypoints(); mapOverlay.clearWaypoints();
updateOptionsButton(); updateOptionsButton();
mapView.invalidate(); mapView.invalidate();
return; return;
} }
busyPane.setVisibility(View.VISIBLE); busyPane.setVisibility(View.VISIBLE);
selectedTrack = providerUtils.getTrack(trackId); selectedTrackId = trackId;
loadSelectedTrack();
}
private void loadSelectedTrack() {
updateTrackHandler.post(restoreTrackRunnable); updateTrackHandler.post(restoreTrackRunnable);
updateTrackHandler.post(restoreWaypointsRunnable); updateTrackHandler.post(restoreWaypointsRunnable);
updateTrackHandler.post(setSelectedTrackRunnable); updateTrackHandler.post(setSelectedTrackRunnable);
@@ -753,8 +774,7 @@ public class MyTracksMap extends MapActivity
menu.setHeaderTitle(R.string.tracklist_this_track); menu.setHeaderTitle(R.string.tracklist_this_track);
menu.add(0, MyTracksConstants.MENU_EDIT, 0, menu.add(0, MyTracksConstants.MENU_EDIT, 0,
R.string.tracklist_edit_track); R.string.tracklist_edit_track);
if (!MyTracks.getInstance().isRecording() || (selectedTrack != null if (!MyTracks.getInstance().isRecording() || !isRecordingSelected()) {
&& selectedTrack.getId() != recordingTrackId)) {
menu.add(0, MyTracksConstants.MENU_SEND_TO_GOOGLE, 0, menu.add(0, MyTracksConstants.MENU_SEND_TO_GOOGLE, 0,
R.string.tracklist_send_to_google); R.string.tracklist_send_to_google);
SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0, SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0,
@@ -791,7 +811,7 @@ public class MyTracksMap extends MapActivity
@Override @Override
public boolean onMenuItemSelected(int featureId, MenuItem item) { public boolean onMenuItemSelected(int featureId, MenuItem item) {
if (!super.onMenuItemSelected(featureId, item)) { if (!super.onMenuItemSelected(featureId, item)) {
if (selectedTrack != null) { if (isATrackSelected()) {
MyTracks.getInstance().onActivityResult( MyTracks.getInstance().onActivityResult(
MyTracksConstants.getActionFromMenuId(item.getItemId()), RESULT_OK, MyTracksConstants.getActionFromMenuId(item.getItemId()), RESULT_OK,
new Intent()); new Intent());
@@ -852,8 +872,7 @@ public class MyTracksMap extends MapActivity
public void onClick(View v) { public void onClick(View v) {
if (v == messagePane) { if (v == messagePane) {
launchMyLocationSettings(); launchMyLocationSettings();
} } else if (v == optionsBtn) {
if (v == optionsBtn) {
optionsBtn.performLongClick(); optionsBtn.performLongClick();
} }
} }
@@ -895,139 +914,155 @@ public class MyTracksMap extends MapActivity
sharedPreferences.getLong( sharedPreferences.getLong(
getString(R.string.recording_track_key), getString(R.string.recording_track_key),
-1); -1);
if (selectedTrack != null) { if (isATrackSelected()) {
mapOverlay.setShowEndMarker( mapOverlay.setShowEndMarker(!isRecordingSelected());
selectedTrack.getId() != recordingTrackId);
mapView.postInvalidate(); mapView.postInvalidate();
} }
} else if (key.equals(getString(R.string.debug_draw_bounds_key))) {
mapOverlay.setDrawBounds(
sharedPreferences.getBoolean(
getString(R.string.debug_draw_bounds_key), false));
} }
} }
}); });
} }
} }
@Override private final LocationListener locationListener = new LocationListener() {
public void onProviderEnabled(String provider) { @Override
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) { public void onProviderEnabled(String provider) {
messageText.setText(R.string.wait_for_fix); if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
} messageText.setText(R.string.wait_for_fix);
}
@Override
public void onProviderDisabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.status_enable_gps);
messagePane.setVisibility(View.VISIBLE);
messagePane.setOnClickListener(this);
screen.requestLayout();
}
}
@Override
public void onLocationChanged(Location location) {
if (location.getProvider().equals(MyTracksConstants.GPS_PROVIDER)) {
// Recalculate the variation if there was a jump in location > 1km:
if (currentLocation == null
|| location.distanceTo(currentLocation) > 1000) {
setVariation(location);
} }
currentLocation = location; }
boolean haveGoodFixNow =
currentLocation.getAccuracy() < minRequiredAccuracy; @Override
if (haveGoodFixNow != haveGoodFix) { public void onProviderDisabled(String provider) {
haveGoodFix = haveGoodFixNow; if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE); messageText.setText(R.string.status_enable_gps);
messagePane.setVisibility(View.VISIBLE);
messagePane.setOnClickListener(MyTracksMap.this);
screen.requestLayout(); screen.requestLayout();
} }
showCurrentLocation();
} else {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Network location update received.");
} }
}
@Override @Override
public void onStatusChanged(String provider, int status, Bundle extras) { public void onLocationChanged(Location location) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) { if (location.getProvider().equals(MyTracksConstants.GPS_PROVIDER)) {
switch (status) { // Recalculate the variation if there was a jump in location > 1km:
case LocationProvider.OUT_OF_SERVICE: if (currentLocation == null
case LocationProvider.TEMPORARILY_UNAVAILABLE: || location.distanceTo(currentLocation) > 1000) {
haveGoodFix = false; setVariation(location);
messagePane.setVisibility(View.VISIBLE); }
currentLocation = location;
boolean haveGoodFixNow =
currentLocation.getAccuracy() < minRequiredAccuracy;
if (haveGoodFixNow != haveGoodFix) {
haveGoodFix = haveGoodFixNow;
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE);
screen.requestLayout(); screen.requestLayout();
break; }
showCurrentLocation();
} else {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Network location update received.");
} }
} }
}
@Override @Override
public void onSensorChanged(SensorEvent se) { public void onStatusChanged(String provider, int status, Bundle extras) {
synchronized (this) { if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
float magneticHeading = se.values[0]; switch (status) {
double heading = magneticHeading + variation; case LocationProvider.OUT_OF_SERVICE:
if (mapOverlay.setHeading((float) heading)) { case LocationProvider.TEMPORARILY_UNAVAILABLE:
mapView.invalidate(); haveGoodFix = false;
messagePane.setVisibility(View.VISIBLE);
screen.requestLayout();
break;
}
} }
} }
} };
@Override private final SensorEventListener sensorListener = new SensorEventListener() {
public void onAccuracyChanged(Sensor s, int accuracy) { @Override
// do nothing public void onSensorChanged(SensorEvent se) {
} synchronized (this) {
float magneticHeading = se.values[0];
double heading = magneticHeading + variation;
if (mapOverlay.setHeading((float) heading)) {
mapView.invalidate();
}
}
}
/** @Override
* Set the sampling frequency from the total number of points in the track. public void onAccuracyChanged(Sensor s, int accuracy) {
* // do nothing
* @param track The track to read the total number of points from. }
*/ };
private void setSamplingFrequency(Track track) {
long totalLocations = track.getStopId() - track.getStartId();
// Limit the number of map points readings.
samplingFrequency =
(int) (1 +
totalLocations / MyTracksConstants.TARGET_DISPLAYED_TRACK_POINTS);
Log.i(MyTracksConstants.TAG, "Sampling locations: " + samplingFrequency);
}
private void readAllNewTrackPoints() { private void readAllNewTrackPoints() {
Cursor cursor = null; int numPoints = mapOverlay.getNumLocations();
// Refetch the track to get the latest StopId if (numPoints >= MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS) {
selectedTrack = providerUtils.getTrack(selectedTrack.getId()); // We're about to exceed the maximum allowed number of points, so reload
long totalLocations = selectedTrack.getStopId() - // the whole track with fewer points (the sampling frequency will be
selectedTrack.getStartId(); // lower).
loadSelectedTrack();
return;
}
long lastStoredLocationId =
providerUtils.getLastLocationId(selectedTrackId);
int samplingFrequency = -1;
Location location = new Location("");
for (;;) {
Cursor cursor = null;
try {
cursor = providerUtils.getLocationsCursor(selectedTrackId,
lastSeenLocationId + 1, TRACKPOINT_BUFFER_SIZE, false);
if (cursor == null || !cursor.moveToFirst()) {
// No (more) data
break;
}
setSamplingFrequency(selectedTrack);
int bufferSize = 1024;
int points = 0;
while (lastSeenLocationId < selectedTrack.getStopId()) {
cursor = providerUtils.getLocationsCursor(
selectedTrack.getId(), lastSeenLocationId, bufferSize, false);
if (cursor != null && cursor.moveToFirst()) {
final int idColumnIdx = cursor.getColumnIndexOrThrow( final int idColumnIdx = cursor.getColumnIndexOrThrow(
TrackPointsColumns._ID); TrackPointsColumns._ID);
while (cursor.moveToNext()) { do {
points++; long locationId = cursor.getLong(idColumnIdx);
Location location = providerUtils.createLocation(cursor); lastSeenLocationId = locationId;
lastSeenLocationId = cursor.getLong(idColumnIdx); if (firstSeenLocationId == -1) {
// Include a point if it fits one of the following criteria: // This was our first point, keep its ID
// - Has the mod for the sampling frequency. firstSeenLocationId = locationId;
// - Is the first point.
// - Is the last point and we are not recording this track.
if (!MyTracksUtils.isValidLocation(location) ||
points % samplingFrequency == 0 ||
points == 0 ||
(recordingTrackId != selectedTrack.getId() &&
points == (totalLocations - 1))) {
mapOverlay.addLocation(location);
} }
if (samplingFrequency == -1) {
// Now we already have at least one point, calculate the sampling
// frequency
long numTotalPoints = lastStoredLocationId - firstSeenLocationId;
samplingFrequency = (int) (1 +
numTotalPoints / MyTracksConstants.TARGET_DISPLAYED_TRACK_POINTS);
}
providerUtils.fillLocation(cursor, location);
// Include a point if it fits one of the following criteria:
// - Has the mod for the sampling frequency (includes first point).
// - Is the last point and we are not recording this track.
// - The point is a segment split
if (numPoints % samplingFrequency == 0 ||
(!isRecordingSelected() && locationId == lastStoredLocationId) ||
!MyTracksUtils.isValidLocation(location)) {
// Only allocate a new location if it is going to be kept around.
mapOverlay.addLocation(new Location(location));
}
numPoints++;
} while (cursor.moveToNext());
} finally {
if (cursor != null) {
cursor.close();
} }
} else {
lastSeenLocationId += bufferSize;
} }
cursor.close();
cursor = null;
} }
mapView.postInvalidate(); mapView.postInvalidate();
} }
@@ -15,10 +15,8 @@
*/ */
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
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.util.MyTracksUtils; import com.google.android.apps.mytracks.util.MyTracksUtils;
import com.google.android.maps.GeoPoint; import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView; import com.google.android.maps.MapView;
import com.google.android.maps.Overlay; import com.google.android.maps.Overlay;
@@ -31,6 +29,7 @@ import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.location.Location; import android.location.Location;
@@ -57,10 +56,11 @@ public class MyTracksOverlay extends Overlay {
private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>(); private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
private final ArrayList<Location> points = new ArrayList<Location>(1024); private final ArrayList<Location> points = new ArrayList<Location>(1024);
private Track selectedTrack; private boolean trackDrawingEnabled;
private int lastHeading = 0; private int lastHeading = 0;
private Location myLocation; private Location myLocation;
private boolean showEndMarker = true; private boolean showEndMarker = true;
private boolean drawBounds;
public MyTracksOverlay(Context context) { public MyTracksOverlay(Context context) {
this.context = context; this.context = context;
@@ -121,14 +121,6 @@ public class MyTracksOverlay extends Overlay {
errorCirclePaint.setAntiAlias(true); errorCirclePaint.setAntiAlias(true);
} }
public void setSelectedTrack(Track track) {
selectedTrack = track;
}
public Track getSelectedTrack() {
return selectedTrack;
}
/** /**
* Add a location to the map overlay. * Add a location to the map overlay.
* *
@@ -137,19 +129,35 @@ public class MyTracksOverlay extends Overlay {
* @param l the location to add * @param l the location to add
*/ */
public void addLocation(Location l) { public void addLocation(Location l) {
points.add(l); if (l != null) {
synchronized (points) {
points.add(l);
}
}
} }
public void addWaypoint(Waypoint wpt) { public void addWaypoint(Waypoint wpt) {
waypoints.add(wpt); waypoints.add(wpt);
} }
public int getNumLocations() {
synchronized (points) {
return points.size();
}
}
public void clearWaypoints() { public void clearWaypoints() {
waypoints.clear(); waypoints.clear();
} }
public void clearPoints() { public void clearPoints() {
points.clear(); synchronized (points) {
points.clear();
}
}
public void setTrackDrawingEnabled(boolean trackDrawingEnabled) {
this.trackDrawingEnabled = trackDrawingEnabled;
} }
public void setShowEndMarker(boolean showEndMarker) { public void setShowEndMarker(boolean showEndMarker) {
@@ -158,15 +166,23 @@ public class MyTracksOverlay extends Overlay {
@Override @Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) { public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if (shadow) { if (shadow) {
return; return;
} }
// Draw the selected track: if (trackDrawingEnabled) {
drawTrack(canvas, mapView, selectedTrack, true); // Draw the selected track:
drawTrack(canvas, mapView);
// Draw the waypoints: // Draw the waypoints:
drawWaypoints(canvas, mapView);
}
// Draw the current location
drawMyLocation(canvas, mapView);
}
private void drawWaypoints(Canvas canvas, MapView mapView) {
ArrayList<Waypoint> currentWaypoints = waypoints; ArrayList<Waypoint> currentWaypoints = waypoints;
for (int i = 1; i < currentWaypoints.size(); i++) { for (int i = 1; i < currentWaypoints.size(); i++) {
Waypoint wpt = currentWaypoints.get(i); Waypoint wpt = currentWaypoints.get(i);
@@ -189,11 +205,14 @@ public class MyTracksOverlay extends Overlay {
} }
canvas.restore(); canvas.restore();
} }
}
private void drawMyLocation(Canvas canvas, MapView mapView) {
// Draw the arrow icon: // Draw the arrow icon:
if (myLocation == null) { if (myLocation == null) {
return; return;
} }
GeoPoint geoPoint = new GeoPoint( GeoPoint geoPoint = new GeoPoint(
(int) (myLocation.getLatitude() * 1E6), (int) (myLocation.getLatitude() * 1E6),
(int) (myLocation.getLongitude() * 1E6)); (int) (myLocation.getLongitude() * 1E6));
@@ -210,79 +229,119 @@ public class MyTracksOverlay extends Overlay {
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint); canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
} }
public void drawTrack(Canvas canvas, MapView mapView, Track track, private void drawTrack(Canvas canvas, MapView mapView) {
boolean selected) { Path path;
Point pt = new Point();
if (track == null) { Location lastValidLocation;
return; int locLon = 0, locLat = 0;
} GeoPoint firstGeoPoint = null;
if (points.size() < 2) { boolean lastLocValid;
return;
}
// Get the current viewing window: // Get the current viewing window:
int w = mapView.getLongitudeSpan(); int w = mapView.getLongitudeSpan();
int h = mapView.getLatitudeSpan(); int h = mapView.getLatitudeSpan();
int cx = mapView.getMapCenter().getLongitudeE6(); int cx = mapView.getMapCenter().getLongitudeE6();
int cy = mapView.getMapCenter().getLatitudeE6(); int cy = mapView.getMapCenter().getLatitudeE6();
Rect rect = new Rect(cx - w, cy - h, cx + w, cy + h); Rect viewRect = new Rect(cx - w, cy - h, cx + w, cy + h);
Point pt = new Point(); // Global bounding box, including points not visible
GeoPoint geoPoint; int allMinLat, allMinLon, allMaxLat, allMaxLon;
Location loc;
Location lastValidLocation = null;
Path path;
boolean wasInside;
// Do as much allocation and preparation outside the loop over track synchronized (points) {
// points: int numPoints = points.size();
wasInside = false; if (numPoints < 2) {
int locLon = 0, locLat = 0, minLon, maxLon, minLat, maxLat; return;
int lastLocLon = (int) (points.get(0).getLongitude() * 1E6);
int lastLocLat = (int) (points.get(0).getLatitude() * 1E6);
// Loop over track points:
path = new Path();
for (int i = 1; i < points.size(); i++) {
loc = points.get(i);
if (loc == null) {
continue;
} }
locLon = (int) (loc.getLongitude() * 1E6);
locLat = (int) (loc.getLatitude() * 1E6);
// Draw a line segment if it's inside the viewing window: GeoPoint geoPoint;
if (locLat < 90E6 && lastLocLat < 90E6) { Location loc;
lastValidLocation = loc; int minLon, maxLon, minLat, maxLat;
minLon = Math.min(locLon, lastLocLon); lastValidLocation = points.get(0);
maxLon = Math.max(locLon, lastLocLon); int lastLocLon = allMinLon = allMaxLon = (int) (lastValidLocation.getLongitude() * 1E6);
minLat = Math.min(locLat, lastLocLat); int lastLocLat = allMinLat = allMaxLat = (int) (lastValidLocation.getLatitude() * 1E6);
maxLat = Math.max(locLat, lastLocLat); lastLocValid = MyTracksUtils.isValidLocation(lastValidLocation);
if (rect.intersects(minLon, minLat, maxLon, maxLat)) { boolean lastLocVisible = false;
if (!wasInside) { path = new Path();
geoPoint = new GeoPoint(lastLocLat, lastLocLon);
mapView.getProjection().toPixels(geoPoint, pt); // Loop over track points:
path.moveTo(pt.x, pt.y); path.incReserve(numPoints);
for (int i = 1; i < numPoints; i++) {
loc = points.get(i);
boolean locValid = MyTracksUtils.isValidLocation(loc);
boolean locVisible = false;
if (locValid) {
locLon = (int) (loc.getLongitude() * 1E6);
locLat = (int) (loc.getLatitude() * 1E6);
if (firstGeoPoint == null) {
// Found the starting point
firstGeoPoint = new GeoPoint(locLat, locLon);
}
// If both the current and previous locations were valid
if (lastLocValid) {
lastValidLocation = loc;
// Get the bounding box of the segment about to be drawn
if (locLon > lastLocLon) {
minLon = lastLocLon;
maxLon = locLon;
} else {
minLon = locLon;
maxLon = lastLocLon;
}
if (locLat > lastLocLat) {
minLat = lastLocLat;
maxLat = locLat;
} else {
minLat = locLat;
maxLat = lastLocLat;
}
if (drawBounds) {
allMaxLat = Math.max(allMaxLat, maxLat);
allMinLat = Math.min(allMinLat, minLat);
allMaxLon = Math.max(allMaxLon, maxLon);
allMinLon = Math.min(allMinLon, minLon);
}
// See if that bounding box intersects the viewable bounding box
// Assume that if it does, the location is visible
locVisible = viewRect.intersects(minLon, minLat, maxLon, maxLat);
if (locVisible) {
// If the previous point wasn't drawn, start at its position
if (!lastLocVisible) {
geoPoint = new GeoPoint(lastLocLat, lastLocLon);
mapView.getProjection().toPixels(geoPoint, pt);
path.moveTo(pt.x, pt.y);
}
// Draw a line to the new point
geoPoint = new GeoPoint(locLat, locLon);
mapView.getProjection().toPixels(geoPoint, pt);
path.lineTo(pt.x, pt.y);
}
} }
geoPoint = new GeoPoint(locLat, locLon);
mapView.getProjection().toPixels(geoPoint, pt);
path.lineTo(pt.x, pt.y);
wasInside = rect.contains(locLon, locLat);
} }
} else { lastLocLon = locLon;
wasInside = false; lastLocLat = locLat;
lastLocValid = locValid;
lastLocVisible = locVisible;
} }
lastLocLon = locLon;
lastLocLat = locLat;
} }
canvas.drawPath(path, selectedTrackPaint); canvas.drawPath(path, selectedTrackPaint);
if (drawBounds) {
drawBoundingBox(canvas, mapView,
allMinLat, allMinLon, allMaxLat, allMaxLon);
}
// Draw the "End" marker: // Draw the "End" marker:
Location currentLastValidLocation = lastValidLocation; if (showEndMarker && lastValidLocation != null) {
if (showEndMarker && currentLastValidLocation != null) {
canvas.save(); canvas.save();
geoPoint = MyTracksUtils.getGeoPoint(currentLastValidLocation); GeoPoint geoPoint = MyTracksUtils.getGeoPoint(lastValidLocation);
mapView.getProjection().toPixels(geoPoint, pt); mapView.getProjection().toPixels(geoPoint, pt);
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight); canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
endMarker.draw(canvas); endMarker.draw(canvas);
@@ -290,22 +349,46 @@ public class MyTracksOverlay extends Overlay {
} }
// Draw the "Start" marker: // Draw the "Start" marker:
for (int i = 0; i < points.size(); i++) { if (firstGeoPoint != null) {
loc = points.get(i); mapView.getProjection().toPixels(firstGeoPoint, pt);
if (loc.getLatitude() < 90) { canvas.save();
locLon = (int) (loc.getLongitude() * 1E6); canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
locLat = (int) (loc.getLatitude() * 1E6); startMarker.draw(canvas);
geoPoint = new GeoPoint(locLat, locLon); canvas.restore();
mapView.getProjection().toPixels(geoPoint, pt);
canvas.save();
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
startMarker.draw(canvas);
canvas.restore();
break;
}
} }
} }
private void drawBoundingBox(Canvas canvas, MapView mapView, int allMinLat,
int allMinLon, int allMaxLat, int allMaxLon) {
// Transform coordinates
GeoPoint maxPoint = new GeoPoint(allMaxLat, allMaxLon);
GeoPoint minPoint = new GeoPoint(allMinLat, allMinLon);
Point minPt = new Point();
Point maxPt = new Point();
mapView.getProjection().toPixels(minPoint, minPt);
mapView.getProjection().toPixels(maxPoint, maxPt);
Rect allBounds = new Rect(minPt.x, minPt.y, maxPt.x, maxPt.y);
// Prepare a green paint
Paint boundingBoxPaint = new Paint();
boundingBoxPaint.setColor(context.getResources().getColor(R.color.green));
boundingBoxPaint.setStrokeWidth(2);
boundingBoxPaint.setStyle(Paint.Style.STROKE);
boundingBoxPaint.setAntiAlias(true);
// Disable clipping
canvas.save();
canvas.clipRect(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY,
Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
Region.Op.REPLACE);
// Draw the bounding box
canvas.drawRect(allBounds, boundingBoxPaint);
// Re-enable clipping
canvas.restore();
}
@Override @Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, public boolean draw(Canvas canvas, MapView mapView, boolean shadow,
long when) { long when) {
@@ -374,4 +457,8 @@ public class MyTracksOverlay extends Overlay {
} }
return super.onTap(p, mapView); return super.onTap(p, mapView);
} }
public void setDrawBounds(boolean drawBounds) {
this.drawBounds = drawBounds;
}
} }
@@ -115,7 +115,7 @@ public class MyTracksSettings extends PreferenceActivity {
announcementFrequency.setEnabled(false); announcementFrequency.setEnabled(false);
announcementFrequency.setValue("-1"); announcementFrequency.setValue("-1");
announcementFrequency.setSummary( announcementFrequency.setSummary(
R.string.settings_announcement_not_available_summary); R.string.settings_not_available_summary);
} }
} }
@@ -135,7 +135,6 @@ public class MyTracksWaypointDetails extends Activity
iconId = R.drawable.ylw_pushpin; iconId = R.drawable.ylw_pushpin;
TripStatistics waypointStats = waypoint.getStatistics(); TripStatistics waypointStats = waypoint.getStatistics();
utils.setAllStats(waypointStats); utils.setAllStats(waypointStats);
utils.setTime(R.id.total_time_register, waypointStats.getTotalTime());
utils.setAltitude( utils.setAltitude(
R.id.elevation_register, waypoint.getLocation().getAltitude()); R.id.elevation_register, waypoint.getLocation().getAltitude());
break; break;
@@ -15,8 +15,6 @@
*/ */
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Handler; import android.os.Handler;
@@ -50,24 +48,22 @@ public class NavControls {
/** /**
* A touchable image view. * A touchable image view.
* When touched it changes the navigation control icons accordingly.
*/ */
public class TouchLayout extends RelativeLayout { private class TouchLayout extends RelativeLayout implements Runnable {
private final ImageView arrow; private final boolean isLeft;
private final ImageView icon; private final ImageView icon;
public TouchLayout(Context context, boolean isLeft) { public TouchLayout(Context context, boolean isLeft) {
super(context); super(context);
arrow = new ImageView(context); this.isLeft = isLeft;
arrow.setImageDrawable(context.getResources().getDrawable( this.icon = new ImageView(context);
isLeft ? R.drawable.btn_arrow_left : R.drawable.btn_arrow_right));
icon = new ImageView(context);
icon.setVisibility(View.GONE); icon.setVisibility(View.GONE);
addView(arrow);
addView(icon); addView(icon);
icon.setPadding((isLeft ? 15 : 10), 27, 15, 0);
} }
public void setIcon(Drawable drawable) { public void setIcon(int iconId) {
Drawable drawable = getContext().getResources().getDrawable(iconId);
icon.setImageDrawable(drawable); icon.setImageDrawable(drawable);
icon.setVisibility(View.VISIBLE); icon.setVisibility(View.VISIBLE);
} }
@@ -77,18 +73,11 @@ public class NavControls {
switch (event.getAction()) { switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
setPressed(true); setPressed(true);
hide(); shiftIcons(isLeft);
if (this == prevImage) {
if (prevRunnable != null) { // Call the user back
handler.post(prevRunnable); handler.post(this);
return true;
}
} else if (this == nextImage) {
if (nextRunnable != null) {
handler.post(nextRunnable);
return true;
}
}
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
setPressed(false); setPressed(false);
@@ -96,55 +85,69 @@ public class NavControls {
} }
return super.onTouchEvent(event); return super.onTouchEvent(event);
} }
@Override
public void run() {
touchRunnable.run();
setPressed(false);
}
} }
private final Context context;
private final Runnable prevRunnable;
private final Runnable nextRunnable;
private final Handler handler = new Handler(); private final Handler handler = new Handler();
private final Runnable dismissControls = new Runnable() { private final Runnable dismissControls = new Runnable() {
public void run() { public void run() {
hide(); hide();
} }
}; };
private final TouchLayout prevImage; private final TouchLayout prevImage;
private final TouchLayout nextImage; private final TouchLayout nextImage;
private final int[] leftIcons;
private final int[] rightIcons;
private final Runnable touchRunnable;
private boolean isVisible = false; private boolean isVisible = false;
private boolean hasNext = true; private int currentIcons;
private boolean hasPrev = true;
public NavControls(Context context, ViewGroup container, public NavControls(Context context, ViewGroup container,
Runnable prevRunnable, Runnable nextRunnable) { int[] leftIcons, int[] rightIcons,
this.context = context; Runnable touchRunnable) {
this.prevRunnable = prevRunnable; this.leftIcons = leftIcons;
this.nextRunnable = nextRunnable; this.rightIcons = rightIcons;
this.touchRunnable = touchRunnable;
if (leftIcons.length != rightIcons.length || leftIcons.length < 1) {
throw new IllegalArgumentException("Invalid icons specified");
}
if (touchRunnable == null) {
throw new NullPointerException("Runnable cannot be null");
}
LayoutParams prevParams = new LayoutParams( LayoutParams prevParams = new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT); LayoutParams.WRAP_CONTENT);
prevParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
prevParams.addRule(RelativeLayout.CENTER_VERTICAL);
prevImage = new TouchLayout(context, true);
prevImage.setLayoutParams(prevParams);
prevImage.setVisibility(View.INVISIBLE);
container.addView(prevImage);
LayoutParams nextParams = new LayoutParams( LayoutParams nextParams = new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT); LayoutParams.WRAP_CONTENT);
prevParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
nextParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); nextParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
prevParams.addRule(RelativeLayout.CENTER_VERTICAL);
nextParams.addRule(RelativeLayout.CENTER_VERTICAL); nextParams.addRule(RelativeLayout.CENTER_VERTICAL);
nextImage = new TouchLayout(context, false); nextImage = new TouchLayout(context, false);
prevImage = new TouchLayout(context, true);
nextImage.setLayoutParams(nextParams); nextImage.setLayoutParams(nextParams);
prevImage.setLayoutParams(prevParams);
nextImage.setVisibility(View.INVISIBLE); nextImage.setVisibility(View.INVISIBLE);
prevImage.setVisibility(View.INVISIBLE);
container.addView(prevImage);
container.addView(nextImage); container.addView(nextImage);
}
public void setHasNext(boolean hasNext) { prevImage.setIcon(leftIcons[0]);
this.hasNext = hasNext; nextImage.setIcon(rightIcons[0]);
} this.currentIcons = 0;
public void setHasPrev(boolean hasPrev) {
this.hasPrev = hasPrev;
} }
private void keepVisible() { private void keepVisible() {
@@ -156,20 +159,18 @@ public class NavControls {
public void show() { public void show() {
if (!isVisible) { if (!isVisible) {
if (prevRunnable != null && hasPrev) { SHOW_PREV_ANIMATION.setDuration(500);
SHOW_PREV_ANIMATION.setDuration(500); SHOW_PREV_ANIMATION.startNow();
SHOW_PREV_ANIMATION.startNow(); prevImage.setPressed(false);
prevImage.setPressed(false); prevImage.setAnimation(SHOW_PREV_ANIMATION);
prevImage.setAnimation(SHOW_PREV_ANIMATION); prevImage.setVisibility(View.VISIBLE);
prevImage.setVisibility(View.VISIBLE);
} SHOW_NEXT_ANIMATION.setDuration(500);
if (nextRunnable != null && hasNext) { SHOW_NEXT_ANIMATION.startNow();
SHOW_NEXT_ANIMATION.setDuration(500); nextImage.setPressed(false);
SHOW_NEXT_ANIMATION.startNow(); nextImage.setAnimation(SHOW_NEXT_ANIMATION);
nextImage.setPressed(false); nextImage.setVisibility(View.VISIBLE);
nextImage.setAnimation(SHOW_NEXT_ANIMATION);
nextImage.setVisibility(View.VISIBLE);
}
isVisible = true; isVisible = true;
keepVisible(); keepVisible();
} else { } else {
@@ -188,33 +189,25 @@ public class NavControls {
public void hide() { public void hide() {
isVisible = false; isVisible = false;
if (prevRunnable != null) { prevImage.setAnimation(HIDE_PREV_ANIMATION);
if (hasPrev) { HIDE_PREV_ANIMATION.setDuration(500);
prevImage.setAnimation(HIDE_PREV_ANIMATION); HIDE_PREV_ANIMATION.startNow();
HIDE_PREV_ANIMATION.setDuration(500); prevImage.setVisibility(View.INVISIBLE);
HIDE_PREV_ANIMATION.startNow();
} else { nextImage.setAnimation(HIDE_NEXT_ANIMATION);
prevImage.clearAnimation(); HIDE_NEXT_ANIMATION.setDuration(500);
} HIDE_NEXT_ANIMATION.startNow();
prevImage.setVisibility(View.INVISIBLE); nextImage.setVisibility(View.INVISIBLE);
}
if (nextRunnable != null) {
if (hasNext) {
nextImage.setAnimation(HIDE_NEXT_ANIMATION);
HIDE_NEXT_ANIMATION.setDuration(500);
HIDE_NEXT_ANIMATION.startNow();
} else {
nextImage.clearAnimation();
}
nextImage.setVisibility(View.INVISIBLE);
}
} }
public void setLeftIcon(int resourceId) { public int getCurrentIcons() {
prevImage.setIcon(context.getResources().getDrawable(resourceId)); return currentIcons;
} }
public void setRightIcon(int resourceId) { private void shiftIcons(boolean isLeft) {
nextImage.setIcon(context.getResources().getDrawable(resourceId)); // Increment or decrement by one, with wrap around
currentIcons = (currentIcons + leftIcons.length + (isLeft ? -1 : 1)) % leftIcons.length;
prevImage.setIcon(leftIcons[currentIcons]);
nextImage.setIcon(rightIcons[currentIcons]);
} }
} }
@@ -49,14 +49,12 @@ import android.widget.Toast;
* @author Sandor Dornbush * @author Sandor Dornbush
*/ */
public class StatsActivity extends Activity public class StatsActivity extends Activity
implements OnSharedPreferenceChangeListener, LocationListener { implements OnSharedPreferenceChangeListener {
private final StatsUtilities utils; private final StatsUtilities utils;
private UIUpdateThread thread; private UIUpdateThread thread;
private ContentObserver observer; private ContentObserver observer;
private LocationManager locationManager;
/** /**
* The id of the currently selected track. * The id of the currently selected track.
@@ -96,7 +94,6 @@ public class StatsActivity extends Activity
private boolean showCurrentSegment = false; private boolean showCurrentSegment = false;
private final MyTracksProviderUtils providerUtils; private final MyTracksProviderUtils providerUtils;
private Track track = null;
/** /**
* A runnable for posting to the UI thread. Will update the total time field. * A runnable for posting to the UI thread. Will update the total time field.
@@ -179,11 +176,9 @@ public class StatsActivity extends Activity
preferences.getBoolean(getString(R.string.report_speed_key), true); preferences.getBoolean(getString(R.string.report_speed_key), true);
checkLiveTrack(); checkLiveTrack();
restoreStats(); restoreStats();
updateLocation(null); showUnknownLocation();
preferences.registerOnSharedPreferenceChangeListener(this); preferences.registerOnSharedPreferenceChangeListener(this);
} }
locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
utils.setMetricUnits(metricUnits); utils.setMetricUnits(metricUnits);
utils.setReportSpeed(displaySpeed); utils.setReportSpeed(displaySpeed);
utils.updateUnits(); utils.updateUnits();
@@ -214,7 +209,7 @@ public class StatsActivity extends Activity
activityOnTop = true; activityOnTop = true;
checkLiveTrack(); checkLiveTrack();
restoreStats(); restoreStats();
updateLocation(null); showUnknownLocation();
super.onResume(); super.onResume();
} }
@@ -234,7 +229,7 @@ public class StatsActivity extends Activity
-1); -1);
checkLiveTrack(); checkLiveTrack();
restoreStats(); restoreStats();
updateLocation(null); showUnknownLocation();
} else if (key.equals(getString(R.string.recording_track_key))) { } else if (key.equals(getString(R.string.recording_track_key))) {
recordingTrackId = recordingTrackId =
sharedPreferences.getLong( sharedPreferences.getLong(
@@ -242,7 +237,7 @@ public class StatsActivity extends Activity
-1); -1);
checkLiveTrack(); checkLiveTrack();
restoreStats(); restoreStats();
updateLocation(null); showUnknownLocation();
} else if (key.equals(getString(R.string.metric_units_key))) { } else if (key.equals(getString(R.string.metric_units_key))) {
metricUnits = metricUnits =
sharedPreferences.getBoolean( sharedPreferences.getBoolean(
@@ -299,29 +294,36 @@ public class StatsActivity extends Activity
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override private final LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location l) { @Override
if (selectedTrackIsRecording()) { public void onLocationChanged(Location l) {
updateLocation(l); if (selectedTrackIsRecording()) {
showLocation(l);
}
} }
}
@Override @Override
public void onProviderDisabled(String provider) { public void onProviderDisabled(String provider) {
} // Do nothing
}
@Override @Override
public void onProviderEnabled(String provider) { public void onProviderEnabled(String provider) {
} // Do nothing
}
@Override @Override
public void onStatusChanged(String provider, int status, Bundle extras) { public void onStatusChanged(String provider, int status, Bundle extras) {
} // Do nothing
}
};
/** /**
* Registers to receive location updates from the GPS location provider. * Registers to receive location updates from the GPS location provider.
*/ */
private void registerLocationListener() { private void registerLocationListener() {
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) { if (locationManager != null) {
LocationProvider gpsProvider = LocationProvider gpsProvider =
locationManager.getProvider(MyTracksConstants.GPS_PROVIDER); locationManager.getProvider(MyTracksConstants.GPS_PROVIDER);
@@ -334,7 +336,7 @@ public class StatsActivity extends Activity
+ gpsProvider.getName()); + gpsProvider.getName());
} }
locationManager.requestLocationUpdates(gpsProvider.getName(), locationManager.requestLocationUpdates(gpsProvider.getName(),
0/*minTime*/, 0/*minDist*/, this); 0/*minTime*/, 0/*minDist*/, locationListener);
} }
} }
@@ -342,8 +344,10 @@ public class StatsActivity extends Activity
* Unregisters all location listener. * Unregisters all location listener.
*/ */
private void unregisterLocationListener() { private void unregisterLocationListener() {
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) { if (locationManager != null) {
locationManager.removeUpdates(this); locationManager.removeUpdates(locationListener);
} }
} }
@@ -364,7 +368,7 @@ public class StatsActivity extends Activity
return; return;
} }
track = providerUtils.getTrack(selectedTrackId); Track track = providerUtils.getTrack(selectedTrackId);
if (track == null || track.getStatistics() == null) { if (track == null || track.getStatistics() == null) {
utils.setAllToUnknown(); utils.setAllToUnknown();
return; return;
@@ -421,17 +425,17 @@ public class StatsActivity extends Activity
* *
* @param l may be null (will set location fields to unknown) * @param l may be null (will set location fields to unknown)
*/ */
private void updateLocation(Location l) { private void showLocation(Location l) {
if (l == null) { utils.setAltitude(R.id.elevation_register, l.getAltitude());
utils.setUnknown(R.id.elevation_register); utils.setLatLong(R.id.latitude_register, l.getLatitude());
utils.setUnknown(R.id.latitude_register); utils.setLatLong(R.id.longitude_register, l.getLongitude());
utils.setUnknown(R.id.longitude_register); utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6);
utils.setUnknown(R.id.speed_register); }
} else {
utils.setAltitude(R.id.elevation_register, l.getAltitude()); private void showUnknownLocation() {
utils.setLatLong(R.id.latitude_register, l.getLatitude()); utils.setUnknown(R.id.elevation_register);
utils.setLatLong(R.id.longitude_register, l.getLongitude()); utils.setUnknown(R.id.latitude_register);
utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6); utils.setUnknown(R.id.longitude_register);
} utils.setUnknown(R.id.speed_register);
} }
} }
@@ -20,13 +20,13 @@ import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions; import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import android.app.Activity; import android.app.Activity;
import android.util.Log; import android.util.Log;
import android.widget.TextView; import android.widget.TextView;
import java.text.DecimalFormat;
import java.text.NumberFormat;
/** /**
* Various utility functions for views that display statistics information. * Various utility functions for views that display statistics information.
* *
@@ -218,7 +218,7 @@ public class StatsUtilities {
setGrade(R.id.max_grade_register, maxGrade); setGrade(R.id.max_grade_register, maxGrade);
} }
public void setAllStats(TripStatistics stats) { public void setAllStats(TripStatistics stats) {
setTime(R.id.moving_time_register, stats.getMovingTime()); setTime(R.id.moving_time_register, stats.getMovingTime());
setDistance(R.id.total_distance_register, stats.getTotalDistance() / 1000); setDistance(R.id.total_distance_register, stats.getTotalDistance() / 1000);
setSpeed(R.id.average_speed_register, stats.getAverageSpeed() * 3.6); setSpeed(R.id.average_speed_register, stats.getAverageSpeed() * 3.6);
@@ -230,6 +230,8 @@ public class StatsUtilities {
setAltitude(R.id.elevation_gain_register, stats.getTotalElevationGain()); setAltitude(R.id.elevation_gain_register, stats.getTotalElevationGain());
setGrade(R.id.min_grade_register, stats.getMinGrade()); setGrade(R.id.min_grade_register, stats.getMinGrade());
setGrade(R.id.max_grade_register, stats.getMaxGrade()); setGrade(R.id.max_grade_register, stats.getMaxGrade());
setTime(R.id.total_time_register, stats.getTotalTime());
} }
public void setSpeedLabel(int id, int speedString, int paceString) { public void setSpeedLabel(int id, int speedString, int paceString) {
@@ -21,6 +21,8 @@ import android.database.Cursor;
import android.location.Location; import android.location.Location;
import android.net.Uri; import android.net.Uri;
import java.util.List;
/** /**
* Utility to access data from the mytracks content provider. * Utility to access data from the mytracks content provider.
* *
@@ -185,6 +187,15 @@ public interface MyTracksProviderUtils {
*/ */
Track getTrack(long id); Track getTrack(long id);
/**
* Retrieves all tracks without track points. If no tracks exist an empty
* list will be returned. Use {@link #getTrackPoints(Track, int)} to load
* the track points.
*
* @return a list of all the recorded tracks
*/
List<Track> getAllTracks();
/** /**
* Loads the track points for a given track. * Loads the track points for a given track.
* *
@@ -203,9 +214,24 @@ public interface MyTracksProviderUtils {
* *
* @param track to load locations for * @param track to load locations for
* @param buffer an array of locations to fill * @param buffer an array of locations to fill
* @deprecated use {@link #fillTrackPoints} instead
*/ */
@Deprecated
void getTrackPoints(Track track, TrackBuffer buffer); void getTrackPoints(Track track, TrackBuffer buffer);
/**
* Fetches some number of locations for the given track.
*
* This is designed to be used to stream through large tracks without loading
* all points into memory.
* This method will reuse the Location objects in the buffer. If you need a
* Location object copy the object.
*
* @param track to load locations for
* @param buffer an array of locations to fill
*/
void fillTrackPoints(Track track, TrackBuffer buffer);
/** /**
* Creates a cursor over the tracks provider with a given selection. * Creates a cursor over the tracks provider with a given selection.
* Caller gets to own the returned cursor. Don't forget to close it. * Caller gets to own the returned cursor. Don't forget to close it.
@@ -294,6 +320,14 @@ public interface MyTracksProviderUtils {
*/ */
Location createLocation(Cursor cursor); Location createLocation(Cursor cursor);
/**
* Fill a location object with values from a given cursor.
*
* @param cursor a cursor pointing at a db or provider with locations
* @param location a location object to be overwritten
*/
void fillLocation(Cursor cursor, Location location);
/** /**
* Creates a waypoint object from a given cursor. * Creates a waypoint object from a given cursor.
* *
@@ -24,6 +24,9 @@ import android.location.Location;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/** /**
* Helper class providing easy access to locations and tracks in the * Helper class providing easy access to locations and tracks in the
* MyTracksProvider. All static members. * MyTracksProvider. All static members.
@@ -168,6 +171,15 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override @Override
public Location createLocation(Cursor cursor) { public Location createLocation(Cursor cursor) {
Location location = new Location("");
fillLocation(cursor, location);
return location;
}
@Override
public void fillLocation(Cursor cursor, Location location) {
location.reset();
int idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE); int idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
int idxLongitude = int idxLongitude =
cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE); cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
@@ -177,7 +189,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY); int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED); int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
Location location = new Location("");
if (!cursor.isNull(idxLatitude)) { if (!cursor.isNull(idxLatitude)) {
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6); location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
} }
@@ -199,7 +210,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
if (!cursor.isNull(idxAccuracy)) { if (!cursor.isNull(idxAccuracy)) {
location.setAccuracy(cursor.getFloat(idxAccuracy)); location.setAccuracy(cursor.getFloat(idxAccuracy));
} }
return location;
} }
@Override @Override
@@ -791,6 +801,21 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
return findTrackBy(select); return findTrackBy(select);
} }
@Override
public List<Track> getAllTracks() {
Cursor cursor = getTracksCursor(null);
if (cursor == null || !cursor.moveToFirst()) {
return new ArrayList<Track>();
}
List<Track> tracks = new ArrayList<Track>(cursor.getCount());
do {
tracks.add(createTrack(cursor));
} while(cursor.moveToNext());
return tracks;
}
@Override @Override
public long getTrackPoints(Track track, int maxPoints) { public long getTrackPoints(Track track, int maxPoints) {
long lastId = -1; long lastId = -1;
@@ -822,6 +847,16 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override @Override
public void getTrackPoints(Track track, TrackBuffer buffer) { public void getTrackPoints(Track track, TrackBuffer buffer) {
getTrackPoints(track, buffer, false);
}
@Override
public void fillTrackPoints(Track track, TrackBuffer buffer) {
getTrackPoints(track, buffer, true);
}
public void getTrackPoints(Track track, TrackBuffer buffer,
boolean reuseLocations) {
long startingPoint = buffer.getLastLocationRead() == 0 ? track.getStartId() long startingPoint = buffer.getLastLocationRead() == 0 ? track.getStartId()
: buffer.getLastLocationRead(); : buffer.getLastLocationRead();
buffer.reset(); buffer.reset();
@@ -849,11 +884,15 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
final int idColumnIdx = final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID); cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
do { do {
Location location = createLocation(cursor); if (reuseLocations) {
if (location == null) { fillLocation(cursor, buffer.add(cursor.getLong(idColumnIdx)));
continue; } else {
Location location = createLocation(cursor);
if (location == null) {
continue;
}
buffer.add(location, cursor.getLong(idColumnIdx));
} }
buffer.add(location, cursor.getLong(idColumnIdx));
} while (cursor.moveToNext()); } while (cursor.moveToNext());
if (buffer.getLocationsLoaded() == 0) { if (buffer.getLocationsLoaded() == 0) {
@@ -95,6 +95,22 @@ public class TrackBuffer {
lastLocationRead = Math.max(lastLocationRead, id); lastLocationRead = Math.max(lastLocationRead, id);
} }
/**
* Get a location from the end of the buffer.
* This may reuse Location objects already allocated to the buffer.
* @param locationId The of the location to be added.
* @return The location object associated with the give id.
*/
public Location add(long locationId) {
lastLocationRead = Math.max(lastLocationRead, locationId);
if (buffer[locationsLoaded] == null) {
buffer[locationsLoaded] = new Location("");
} else {
buffer[locationsLoaded].reset();
}
return buffer[locationsLoaded++];
}
/** /**
* @return The id of the last location loaded into the buffer * @return The id of the last location loaded into the buffer
*/ */
@@ -264,7 +264,7 @@ public class TrackWriter {
while (buffer.getLastLocationRead() < track.getStopId()) { while (buffer.getLastLocationRead() < track.getStopId()) {
Log.d(MyTracksConstants.TAG, Log.d(MyTracksConstants.TAG,
"Reading track points starting at: " + buffer.getLastLocationRead()); "Reading track points starting at: " + buffer.getLastLocationRead());
providerUtils.getTrackPoints(track, buffer); providerUtils.fillTrackPoints(track, buffer);
if (!wroteFirst) { if (!wroteFirst) {
Location first = buffer.findStartLocation(); Location first = buffer.findStartLocation();
writer.writeBeginTrack(first); writer.writeBeginTrack(first);
@@ -103,4 +103,9 @@ public class ExtremityMonitor {
public void setMax(double max) { public void setMax(double max) {
this.max = max; this.max = max;
} }
@Override
public String toString() {
return "Min: " + min + " Max: " + max;
}
} }
@@ -326,7 +326,7 @@ public class TrackWriterTest extends AndroidTestCase {
* will be fed into the track buffer on each call * will be fed into the track buffer on each call
*/ */
private void stubBufferFill(final Location[]... feeds) { private void stubBufferFill(final Location[]... feeds) {
providerUtils.getTrackPoints(same(track), isA(TrackBuffer.class)); providerUtils.fillTrackPoints(same(track), isA(TrackBuffer.class));
EasyMock.expectLastCall().andStubAnswer(new IAnswer<Void>() { EasyMock.expectLastCall().andStubAnswer(new IAnswer<Void>() {
private int lastId = 1; private int lastId = 1;
private int reads = 0; private int reads = 0;