Merge with 25704964a526fcfe09919af8f29c1e526b5409ec
@@ -1,4 +1,4 @@
|
||||
#Thu Aug 12 18:54:52 BRT 2010
|
||||
#Wed Sep 29 18:44:16 BRT 2010
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
|
||||
@@ -56,8 +56,11 @@
|
||||
<activity android:name="com.google.android.apps.mytracks.MyTracksSettings"
|
||||
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"
|
||||
android:label="@string/statistics"/>
|
||||
android:label="@string/statistics" />
|
||||
|
||||
<activity android:name="com.google.android.apps.mytracks.ChartActivity"
|
||||
android:label="@string/elevation_label"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
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">
|
||||
|
||||
<item android:state_window_focused="false"
|
||||
android:drawable="@drawable/btn_arrow_left_default" />
|
||||
android:drawable="@drawable/left_arrow_chart" />
|
||||
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
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">
|
||||
|
||||
<item android:state_window_focused="false"
|
||||
android:drawable="@drawable/btn_arrow_right_default" />
|
||||
android:drawable="@drawable/left_arrow_stats" />
|
||||
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
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>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
|
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>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
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>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
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>
|
||||
@@ -20,10 +20,15 @@
|
||||
android:icon="@android:drawable/ic_menu_preferences"
|
||||
android:orderInCategory="1001"
|
||||
android:title="@string/settings"/>
|
||||
|
||||
<item android:id="@+id/menu_aggregated_stats"
|
||||
android:icon="@android:drawable/ic_menu_info_details"
|
||||
android:orderInCategory="1002"
|
||||
android:title="@string/aggregated_stats"/>
|
||||
|
||||
<item android:id="@+id/menu_help"
|
||||
android:icon="@android:drawable/ic_menu_info_details"
|
||||
android:orderInCategory="1002"
|
||||
android:orderInCategory="1003"
|
||||
android:title="@string/help"/>
|
||||
|
||||
</menu>
|
||||
|
||||
@@ -155,7 +155,6 @@ limitations under the License.
|
||||
|
||||
<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_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_summary">Kleinste Entfernung zwischen zwei Positionsaufzeichnungen</string>
|
||||
|
||||
@@ -158,7 +158,6 @@ limitations under the License.
|
||||
|
||||
<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_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_summary">Distancia mínima entre dos grabaciones de posición</string>
|
||||
|
||||
@@ -158,7 +158,6 @@ limitations under the License.
|
||||
|
||||
<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_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_summary">Plus petite distance entre deux enregistrements de votre position</string>
|
||||
|
||||
@@ -164,7 +164,6 @@ limitations under the License.
|
||||
|
||||
<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_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_summary">Distanza minima fra due successive registrazioni della posizione</string>
|
||||
|
||||
@@ -164,7 +164,7 @@ limitations under the License.
|
||||
|
||||
<string name="settings_announcement_frequency">定期通知</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_summary">位置記録間の最短距離</string>
|
||||
|
||||
@@ -164,7 +164,7 @@ limitations under the License.
|
||||
|
||||
<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_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_summary">Najmniejsza odległość między dwoma zapisami twojej pozycji</string>
|
||||
|
||||
@@ -157,7 +157,6 @@ limitations under the License.
|
||||
<string name="settings_sharing">Compartilhamento</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_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_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>
|
||||
|
||||
@@ -43,19 +43,19 @@ limitations under the License.
|
||||
<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_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 -->
|
||||
<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_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_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_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_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_waypoint_inserted">En positionsmarkö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_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="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>
|
||||
|
||||
<!-- Used by DeleteAllDataHandler -->
|
||||
@@ -125,7 +125,7 @@ limitations under the License.
|
||||
<!-- Export All -->
|
||||
<string name="tracklist_btn_export_all">Exportera alla spår</string>
|
||||
<string name="export_done">Exporteringen slutfördes</string>
|
||||
<string name="tracklist_pick_one">Välj ett spår. TryckPress&håll för fler alternativ.</string>
|
||||
<string name="tracklist_pick_one">Välj ett spår. Tryck&håll för fler alternativ.</string>
|
||||
<string name="tracklist_all_tracks">Alla spår:</string>
|
||||
<string name="tracklist_this_track">Det här spåret</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_btn_insert_waypoint">Infoga positionspunkt</string>
|
||||
<string name="waypointslist_btn_insert_statistics">Infoga statistik</string>
|
||||
<string name="waypointslist_pick_one">Välj en markör. TryckPress&håll för fler alternativ.</string>
|
||||
<string name="waypointslist_pick_one">Välj en markör. Tryck&håll för fler alternativ.</string>
|
||||
|
||||
<!-- MyTracks Details -->
|
||||
<string name="trackdetails_title">Spårdetaljer</string>
|
||||
<string name="trackdetails_name">Spårnamn</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 -->
|
||||
<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_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_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_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>
|
||||
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">GPS-satteliter</string>
|
||||
<string name="mytracks_sky">GPS-satelliter</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Välje en karta från ditt konto:</string>
|
||||
@@ -240,7 +240,7 @@ limitations under the License.
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Publik</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 -->
|
||||
<!-- Average Moving -->
|
||||
@@ -253,7 +253,7 @@ limitations under the License.
|
||||
|
||||
<string name="elevation_chart_name">Höjddiagram</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="longitude_label">Longitud</string>
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ limitations under the License.
|
||||
|
||||
<string name="settings_announcement_frequency">提示频度</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_summary">位置记录的最小间隔距离</string>
|
||||
|
||||
@@ -38,6 +38,7 @@ limitations under the License.
|
||||
<string name="share_url_only_key">shareUrlOnly</string>
|
||||
<string name="signal_sampling_frequency_key">signalSamplingFrequency</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 -->
|
||||
<string name="recording_track_key">recordingTrack</string>
|
||||
|
||||
@@ -32,6 +32,7 @@ limitations under the License.
|
||||
<string name="choose_track_message">Please select the track to view</string>
|
||||
<string name="current_track">Current track</string>
|
||||
<string name="current_segment">Last segment</string>
|
||||
<string name="aggregated_stats">Aggregated Statistics</string>
|
||||
|
||||
<!-- Error messages -->
|
||||
<string name="error_no_location_manager">No location manager was found on this device!</string>
|
||||
|
||||
@@ -133,4 +133,13 @@ limitations under the License.
|
||||
android:persistent="false" />
|
||||
</PreferenceCategory>
|
||||
</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>
|
||||
|
||||
@@ -25,5 +25,8 @@ public class MyMapsConstants {
|
||||
"http://maps.google.com/maps/ms";
|
||||
public static final String MAPSHOP_SERVICE = MapsClient.SERVICE;
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
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 =
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
ArrayList<double[]> data = new ArrayList<double[]>();
|
||||
// Need two locations so we can keep track of the last location.
|
||||
Location location = new Location("");
|
||||
do {
|
||||
lastSeenLocationId = cursor.getLong(idColumnIdx);
|
||||
Location location = providerUtils.createLocation(cursor);
|
||||
if (location != null && MyTracksUtils.isValidLocation(location)) {
|
||||
data.add(getDataPoint(location, track));
|
||||
providerUtils.fillLocation(cursor, location);
|
||||
if (MyTracksUtils.isValidLocation(location)) {
|
||||
double[] point = new double[3];
|
||||
location = getDataPoint(location, track, point);
|
||||
data.add(point);
|
||||
}
|
||||
} while (cursor.moveToPrevious());
|
||||
cv.addDataPoints(data);
|
||||
@@ -461,11 +465,14 @@ public class ChartActivity extends Activity implements
|
||||
* data[1] = the elevation
|
||||
* data[2] = the speed
|
||||
*
|
||||
* @param location a location
|
||||
* @return the data point
|
||||
* This must be called in order for each 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) {
|
||||
double[] result = new double[3];
|
||||
private Location getDataPoint(Location location, Track track, double[] result) {
|
||||
switch (mode) {
|
||||
case BY_DISTANCE:
|
||||
result[0] = profileLength;
|
||||
@@ -512,8 +519,16 @@ public class ChartActivity extends Activity implements
|
||||
// Format as hours per unit
|
||||
result[2] = (60.0 / result[2]);
|
||||
}
|
||||
|
||||
Location oldLastLocation = lastLocation;
|
||||
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 {
|
||||
final ArrayList<double[]> theData = new ArrayList<double[]>();
|
||||
int points = 0;
|
||||
// Need two locations so we can keep track of the last location.
|
||||
Location location = new Location("");
|
||||
while (lastLocationRead < track.getStopId()) {
|
||||
cursor = providerUtils.getLocationsCursor(
|
||||
selectedTrackId, lastLocationRead, bufferSize, false);
|
||||
@@ -567,11 +584,12 @@ public class ChartActivity extends Activity implements
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
while (cursor.moveToNext()) {
|
||||
points++;
|
||||
Location location = providerUtils.createLocation(cursor);
|
||||
providerUtils.fillLocation(cursor, location);
|
||||
if (MyTracksUtils.isValidLocation(location)) {
|
||||
lastLocationRead = lastSeenLocationId =
|
||||
cursor.getLong(idColumnIdx);
|
||||
double[] point = getDataPoint(location, track);
|
||||
double[] point = new double[3];
|
||||
location = getDataPoint(location, track, point);
|
||||
if (points % chartSamplingFrequency == 0) {
|
||||
theData.add(point);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,11 @@ class MenuManager {
|
||||
activity.startActivity(startIntent);
|
||||
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: {
|
||||
Intent startIntent = new Intent(activity, WelcomeActivity.class);
|
||||
activity.startActivity(startIntent);
|
||||
|
||||
@@ -75,7 +75,6 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
@@ -217,40 +216,25 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
* Tabs/View navigation:
|
||||
*/
|
||||
|
||||
private static final int NUM_TABS = 3;
|
||||
private int currentTab = 0;
|
||||
|
||||
private NavControls navControls;
|
||||
|
||||
private final int icons[] =
|
||||
{ R.drawable.arrow_grey, R.drawable.menu_by_time,
|
||||
R.drawable.menu_elevation };
|
||||
/** Icons shown on the left for each tab. */
|
||||
private final int leftIcons[] =
|
||||
{ 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() {
|
||||
currentTab = (currentTab + 1) % NUM_TABS;
|
||||
navControls.setLeftIcon(icons[(currentTab + NUM_TABS - 1) % NUM_TABS]);
|
||||
navControls.setRightIcon(icons[(currentTab + NUM_TABS + 1) % NUM_TABS]);
|
||||
getTabHost().setCurrentTab(currentTab);
|
||||
navControls.show();
|
||||
getTabHost().setCurrentTab(navControls.getCurrentIcons());
|
||||
}
|
||||
};
|
||||
|
||||
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() {
|
||||
return instance;
|
||||
}
|
||||
@@ -311,9 +295,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
LayoutParams params =
|
||||
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
|
||||
layout.setLayoutParams(params);
|
||||
navControls = new NavControls(this, layout, prevActivity, nextActivity);
|
||||
navControls.setLeftIcon(icons[NUM_TABS - 1]);
|
||||
navControls.setRightIcon(icons[1]);
|
||||
navControls = new NavControls(this, layout, leftIcons, rightIcons, changeTab);
|
||||
navControls.show();
|
||||
tabHost.addView(layout);
|
||||
layout.setOnTouchListener(this);
|
||||
@@ -376,8 +358,6 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
Log.d(MyTracksConstants.TAG, "MyTracks.onResume");
|
||||
super.onResume();
|
||||
tryBindTrackRecordingService();
|
||||
navControls.setLeftIcon(icons[(currentTab + NUM_TABS - 1) % NUM_TABS]);
|
||||
navControls.setRightIcon(icons[(currentTab + NUM_TABS + 1) % NUM_TABS]);
|
||||
}
|
||||
|
||||
@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
|
||||
* available.
|
||||
*/
|
||||
public void recordRandomLocation() {
|
||||
if (trackRecordingService != null) {
|
||||
Location loc = new Location("gps");
|
||||
double latitude = 37.5 + random.nextDouble() / 1000;
|
||||
double longitude = -120.0 + random.nextDouble() / 1000;
|
||||
loc.setLatitude(latitude);
|
||||
loc.setLongitude(longitude);
|
||||
loc.setAltitude(random.nextDouble() * 100);
|
||||
loc.setTime(System.currentTimeMillis());
|
||||
loc.setSpeed(random.nextFloat());
|
||||
MyTracksMap map =
|
||||
(MyTracksMap) getLocalActivityManager().getActivity("tab1");
|
||||
if (map != null) {
|
||||
map.onLocationChanged(loc);
|
||||
}
|
||||
StatsActivity stats =
|
||||
(StatsActivity) getLocalActivityManager().getActivity("tab2");
|
||||
if (stats != null) {
|
||||
stats.onLocationChanged(loc);
|
||||
}
|
||||
try {
|
||||
trackRecordingService.recordLocation(loc);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(MyTracksConstants.TAG, "MyTracks", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// public void recordRandomLocation() {
|
||||
// if (trackRecordingService != null) {
|
||||
// Location loc = new Location("gps");
|
||||
// double latitude = 37.5 + random.nextDouble() / 1000;
|
||||
// double longitude = -120.0 + random.nextDouble() / 1000;
|
||||
// loc.setLatitude(latitude);
|
||||
// loc.setLongitude(longitude);
|
||||
// loc.setAltitude(random.nextDouble() * 100);
|
||||
// loc.setTime(System.currentTimeMillis());
|
||||
// loc.setSpeed(random.nextFloat());
|
||||
// MyTracksMap map =
|
||||
// (MyTracksMap) getLocalActivityManager().getActivity("tab1");
|
||||
// if (map != null) {
|
||||
// map.onLocationChanged(loc);
|
||||
// }
|
||||
// StatsActivity stats =
|
||||
// (StatsActivity) getLocalActivityManager().getActivity("tab2");
|
||||
// if (stats != null) {
|
||||
// stats.onLocationChanged(loc);
|
||||
// }
|
||||
// try {
|
||||
// trackRecordingService.recordLocation(loc);
|
||||
// } catch (RemoteException e) {
|
||||
// Log.e(MyTracksConstants.TAG, "MyTracks", e);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Resets status information for sending to MyMaps/Docs.
|
||||
|
||||
@@ -70,8 +70,9 @@ import android.widget.Toast;
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class MyTracksMap extends MapActivity
|
||||
implements LocationListener, SensorEventListener, View.OnTouchListener,
|
||||
View.OnClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
implements View.OnTouchListener, View.OnClickListener,
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final int TRACKPOINT_BUFFER_SIZE = 1024;
|
||||
|
||||
// Saved instance state keys:
|
||||
// ---------------------------
|
||||
@@ -82,9 +83,9 @@ public class MyTracksMap extends MapActivity
|
||||
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.
|
||||
@@ -97,13 +98,19 @@ public class MyTracksMap extends MapActivity
|
||||
*/
|
||||
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
|
||||
* provider. This is used to determine which locations are new compared to the
|
||||
* last time the mapOverlay was updated.
|
||||
*/
|
||||
private long lastSeenLocationId = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Magnetic variation.
|
||||
*/
|
||||
@@ -130,19 +137,13 @@ public class MyTracksMap extends MapActivity
|
||||
* A thread with a looper. Post to updateTrackHandler to execute Runnables on
|
||||
* this thread.
|
||||
*/
|
||||
private final HandlerThread updateTrackThread =
|
||||
new HandlerThread("updateTrackThread");
|
||||
private HandlerThread updateTrackThread;
|
||||
|
||||
/** Handler for updateTrackThread */
|
||||
private Handler updateTrackHandler;
|
||||
|
||||
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
|
||||
* added after "lastSeenLocationId".
|
||||
@@ -150,7 +151,7 @@ public class MyTracksMap extends MapActivity
|
||||
private final Runnable updateTrackRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (selectedTrack == null) {
|
||||
if (!isATrackSelected()) {
|
||||
return;
|
||||
}
|
||||
readAllNewTrackPoints();
|
||||
@@ -163,11 +164,12 @@ public class MyTracksMap extends MapActivity
|
||||
private Runnable restoreTrackRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (selectedTrack == null) {
|
||||
if (!isATrackSelected()) {
|
||||
return;
|
||||
}
|
||||
mapOverlay.clearPoints();
|
||||
lastSeenLocationId = selectedTrack.getStartId();
|
||||
firstSeenLocationId = -1;
|
||||
lastSeenLocationId = -1;
|
||||
readAllNewTrackPoints();
|
||||
}
|
||||
};
|
||||
@@ -178,7 +180,7 @@ public class MyTracksMap extends MapActivity
|
||||
private final Runnable restoreWaypointsRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (selectedTrack == null) {
|
||||
if (!isATrackSelected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -188,7 +190,7 @@ public class MyTracksMap extends MapActivity
|
||||
// We will silently drop extra waypoints to make the app responsive.
|
||||
// TODO: Try to only load the waypoints in the view port.
|
||||
cursor = providerUtils.getWaypointsCursor(
|
||||
selectedTrack.getId(), 0,
|
||||
selectedTrackId, 0,
|
||||
MyTracksConstants.MAX_DISPLAYED_WAYPOINTS_POINTS);
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
@@ -219,10 +221,9 @@ public class MyTracksMap extends MapActivity
|
||||
public void run() {
|
||||
uiHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
showTrack(selectedTrack);
|
||||
mapOverlay.setSelectedTrack(selectedTrack);
|
||||
mapOverlay.setShowEndMarker(selectedTrack == null ||
|
||||
selectedTrack.getId() != recordingTrackId);
|
||||
showTrack(selectedTrackId);
|
||||
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
|
||||
mapOverlay.setShowEndMarker(!isRecordingSelected());
|
||||
mapView.invalidate();
|
||||
busyPane.setVisibility(View.GONE);
|
||||
updateOptionsButton();
|
||||
@@ -316,6 +317,7 @@ public class MyTracksMap extends MapActivity
|
||||
locationManager =
|
||||
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
updateTrackThread = new HandlerThread("updateTrackThread");
|
||||
updateTrackThread.start();
|
||||
updateTrackHandler = new Handler(updateTrackThread.getLooper());
|
||||
|
||||
@@ -331,8 +333,7 @@ public class MyTracksMap extends MapActivity
|
||||
// No track is being recorded. We should not be here.
|
||||
return;
|
||||
}
|
||||
if (selectedTrack == null
|
||||
|| selectedTrack.getId() != recordingTrackId) {
|
||||
if (!isRecordingSelected()) {
|
||||
// No track, or one other than the recording track is selected,
|
||||
// don't bother.
|
||||
return;
|
||||
@@ -348,7 +349,7 @@ public class MyTracksMap extends MapActivity
|
||||
public void onChange(boolean selfChange) {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksMap: ContentObserver.onChange waypoints");
|
||||
if (selectedTrack == null) {
|
||||
if (!isATrackSelected()) {
|
||||
return;
|
||||
}
|
||||
updateTrackHandler.post(restoreWaypointsRunnable);
|
||||
@@ -371,20 +372,29 @@ public class MyTracksMap extends MapActivity
|
||||
setSelectedTrack(selectedTrackId);
|
||||
}
|
||||
updateOptionsButton();
|
||||
mapOverlay.setDrawBounds(preferences.getBoolean(
|
||||
getString(R.string.debug_draw_bounds_key), false));
|
||||
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() {
|
||||
// Final cleanup before activity is destroyed.
|
||||
// May not be called at all in some situations.
|
||||
Log.d(MyTracksConstants.TAG, "MyTracksMap.onDestroy");
|
||||
super.onDestroy();
|
||||
/**
|
||||
* Returns whether we're currently recording the same track that's selected
|
||||
* for display.
|
||||
*/
|
||||
private boolean isRecordingSelected() {
|
||||
return isATrackSelected() && selectedTrackId == recordingTrackId;
|
||||
}
|
||||
|
||||
protected void setupZoomControls() {
|
||||
mapView.setBuiltInZoomControls(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -449,10 +459,9 @@ public class MyTracksMap extends MapActivity
|
||||
|
||||
// While this activity was paused the user may have deleted the selected
|
||||
// track. In that case the map overlay needs to be cleared:
|
||||
Track track = mapOverlay.getSelectedTrack();
|
||||
if (track != null && !providerUtils.trackExists(track.getId())) {
|
||||
if (isATrackSelected() && !providerUtils.trackExists(selectedTrackId)) {
|
||||
// The recording track must have been deleted meanwhile.
|
||||
mapOverlay.setSelectedTrack(null);
|
||||
mapOverlay.setTrackDrawingEnabled(false);
|
||||
mapView.invalidate();
|
||||
}
|
||||
}
|
||||
@@ -514,10 +523,10 @@ public class MyTracksMap extends MapActivity
|
||||
+ gpsProvider.getName());
|
||||
}
|
||||
locationManager.requestLocationUpdates(gpsProvider.getName(),
|
||||
0 /*minTime*/, 0 /*minDist*/, this);
|
||||
0 /*minTime*/, 0 /*minDist*/, locationListener);
|
||||
try {
|
||||
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
|
||||
1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, this);
|
||||
1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, locationListener);
|
||||
} catch (RuntimeException e) {
|
||||
// If anything at all goes wrong with getting a cell location do not
|
||||
// abort. Cell location is not essential to this app.
|
||||
@@ -535,7 +544,7 @@ public class MyTracksMap extends MapActivity
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksMap: Now registering sensor listeners.");
|
||||
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) {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksMap: Now unregistering location listeners.");
|
||||
locationManager.removeUpdates(this);
|
||||
locationManager.removeUpdates(locationListener);
|
||||
}
|
||||
if (sensorManager != null) {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksMap: Now unregistering sensor listeners.");
|
||||
sensorManager.unregisterListener(this);
|
||||
sensorManager.unregisterListener(sensorListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,7 +588,7 @@ public class MyTracksMap extends MapActivity
|
||||
*/
|
||||
private void updateOptionsButton() {
|
||||
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.
|
||||
*
|
||||
* @param track a given track
|
||||
* @param trackId a given track ID
|
||||
*/
|
||||
public void showTrack(Track track) {
|
||||
if (track == null || mapView == null || track.getNumberOfPoints() < 2) {
|
||||
public void showTrack(long trackId) {
|
||||
if (mapView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Track track = providerUtils.getTrack(trackId);
|
||||
if (track == null || track.getNumberOfPoints() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
TripStatistics stats = track.getStatistics();
|
||||
int bottom = stats.getBottom();
|
||||
int left = stats.getLeft();
|
||||
@@ -680,24 +695,30 @@ public class MyTracksMap extends MapActivity
|
||||
* @param trackId a given track id
|
||||
*/
|
||||
public void setSelectedTrack(final long trackId) {
|
||||
if (selectedTrack != null && selectedTrack.getId() == trackId) {
|
||||
if (selectedTrackId == trackId) {
|
||||
// Selected track did not change, nothing to do.
|
||||
mapOverlay.setSelectedTrack(selectedTrack);
|
||||
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
|
||||
mapView.invalidate();
|
||||
updateOptionsButton();
|
||||
return;
|
||||
}
|
||||
|
||||
if (trackId < 0) {
|
||||
// Remove selection.
|
||||
selectedTrack = null;
|
||||
mapOverlay.setSelectedTrack(null);
|
||||
selectedTrackId = -1;
|
||||
mapOverlay.setTrackDrawingEnabled(false);
|
||||
mapOverlay.clearWaypoints();
|
||||
updateOptionsButton();
|
||||
mapView.invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
busyPane.setVisibility(View.VISIBLE);
|
||||
selectedTrack = providerUtils.getTrack(trackId);
|
||||
selectedTrackId = trackId;
|
||||
loadSelectedTrack();
|
||||
}
|
||||
|
||||
private void loadSelectedTrack() {
|
||||
updateTrackHandler.post(restoreTrackRunnable);
|
||||
updateTrackHandler.post(restoreWaypointsRunnable);
|
||||
updateTrackHandler.post(setSelectedTrackRunnable);
|
||||
@@ -753,8 +774,7 @@ public class MyTracksMap extends MapActivity
|
||||
menu.setHeaderTitle(R.string.tracklist_this_track);
|
||||
menu.add(0, MyTracksConstants.MENU_EDIT, 0,
|
||||
R.string.tracklist_edit_track);
|
||||
if (!MyTracks.getInstance().isRecording() || (selectedTrack != null
|
||||
&& selectedTrack.getId() != recordingTrackId)) {
|
||||
if (!MyTracks.getInstance().isRecording() || !isRecordingSelected()) {
|
||||
menu.add(0, MyTracksConstants.MENU_SEND_TO_GOOGLE, 0,
|
||||
R.string.tracklist_send_to_google);
|
||||
SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0,
|
||||
@@ -791,7 +811,7 @@ public class MyTracksMap extends MapActivity
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
if (!super.onMenuItemSelected(featureId, item)) {
|
||||
if (selectedTrack != null) {
|
||||
if (isATrackSelected()) {
|
||||
MyTracks.getInstance().onActivityResult(
|
||||
MyTracksConstants.getActionFromMenuId(item.getItemId()), RESULT_OK,
|
||||
new Intent());
|
||||
@@ -852,8 +872,7 @@ public class MyTracksMap extends MapActivity
|
||||
public void onClick(View v) {
|
||||
if (v == messagePane) {
|
||||
launchMyLocationSettings();
|
||||
}
|
||||
if (v == optionsBtn) {
|
||||
} else if (v == optionsBtn) {
|
||||
optionsBtn.performLongClick();
|
||||
}
|
||||
}
|
||||
@@ -895,139 +914,155 @@ public class MyTracksMap extends MapActivity
|
||||
sharedPreferences.getLong(
|
||||
getString(R.string.recording_track_key),
|
||||
-1);
|
||||
if (selectedTrack != null) {
|
||||
mapOverlay.setShowEndMarker(
|
||||
selectedTrack.getId() != recordingTrackId);
|
||||
if (isATrackSelected()) {
|
||||
mapOverlay.setShowEndMarker(!isRecordingSelected());
|
||||
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
|
||||
public void onProviderEnabled(String provider) {
|
||||
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);
|
||||
private final LocationListener locationListener = new LocationListener() {
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
|
||||
messageText.setText(R.string.wait_for_fix);
|
||||
}
|
||||
currentLocation = location;
|
||||
boolean haveGoodFixNow =
|
||||
currentLocation.getAccuracy() < minRequiredAccuracy;
|
||||
if (haveGoodFixNow != haveGoodFix) {
|
||||
haveGoodFix = haveGoodFixNow;
|
||||
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@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(MyTracksMap.this);
|
||||
screen.requestLayout();
|
||||
}
|
||||
showCurrentLocation();
|
||||
} else {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksMap: Network location update received.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
|
||||
switch (status) {
|
||||
case LocationProvider.OUT_OF_SERVICE:
|
||||
case LocationProvider.TEMPORARILY_UNAVAILABLE:
|
||||
haveGoodFix = false;
|
||||
messagePane.setVisibility(View.VISIBLE);
|
||||
@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;
|
||||
if (haveGoodFixNow != haveGoodFix) {
|
||||
haveGoodFix = haveGoodFixNow;
|
||||
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE);
|
||||
screen.requestLayout();
|
||||
break;
|
||||
}
|
||||
showCurrentLocation();
|
||||
} else {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksMap: Network location update received.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent se) {
|
||||
synchronized (this) {
|
||||
float magneticHeading = se.values[0];
|
||||
double heading = magneticHeading + variation;
|
||||
if (mapOverlay.setHeading((float) heading)) {
|
||||
mapView.invalidate();
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
|
||||
switch (status) {
|
||||
case LocationProvider.OUT_OF_SERVICE:
|
||||
case LocationProvider.TEMPORARILY_UNAVAILABLE:
|
||||
haveGoodFix = false;
|
||||
messagePane.setVisibility(View.VISIBLE);
|
||||
screen.requestLayout();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor s, int accuracy) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sampling frequency from the total number of points in the track.
|
||||
*
|
||||
* @param track The track to read the total number of points from.
|
||||
*/
|
||||
private void setSamplingFrequency(Track track) {
|
||||
long totalLocations = track.getStopId() - track.getStartId();
|
||||
private final SensorEventListener sensorListener = new SensorEventListener() {
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent se) {
|
||||
synchronized (this) {
|
||||
float magneticHeading = se.values[0];
|
||||
double heading = magneticHeading + variation;
|
||||
if (mapOverlay.setHeading((float) heading)) {
|
||||
mapView.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor s, int accuracy) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
// 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() {
|
||||
Cursor cursor = null;
|
||||
// Refetch the track to get the latest StopId
|
||||
selectedTrack = providerUtils.getTrack(selectedTrack.getId());
|
||||
long totalLocations = selectedTrack.getStopId() -
|
||||
selectedTrack.getStartId();
|
||||
int numPoints = mapOverlay.getNumLocations();
|
||||
if (numPoints >= MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS) {
|
||||
// We're about to exceed the maximum allowed number of points, so reload
|
||||
// the whole track with fewer points (the sampling frequency will be
|
||||
// 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(
|
||||
TrackPointsColumns._ID);
|
||||
while (cursor.moveToNext()) {
|
||||
points++;
|
||||
Location location = providerUtils.createLocation(cursor);
|
||||
lastSeenLocationId = cursor.getLong(idColumnIdx);
|
||||
// Include a point if it fits one of the following criteria:
|
||||
// - Has the mod for the sampling frequency.
|
||||
// - 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);
|
||||
do {
|
||||
long locationId = cursor.getLong(idColumnIdx);
|
||||
lastSeenLocationId = locationId;
|
||||
if (firstSeenLocationId == -1) {
|
||||
// This was our first point, keep its ID
|
||||
firstSeenLocationId = locationId;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
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.util.MyTracksUtils;
|
||||
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.android.maps.MapView;
|
||||
import com.google.android.maps.Overlay;
|
||||
@@ -31,6 +29,7 @@ import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.location.Location;
|
||||
|
||||
@@ -57,10 +56,11 @@ public class MyTracksOverlay extends Overlay {
|
||||
private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
|
||||
private final ArrayList<Location> points = new ArrayList<Location>(1024);
|
||||
|
||||
private Track selectedTrack;
|
||||
private boolean trackDrawingEnabled;
|
||||
private int lastHeading = 0;
|
||||
private Location myLocation;
|
||||
private boolean showEndMarker = true;
|
||||
private boolean drawBounds;
|
||||
|
||||
public MyTracksOverlay(Context context) {
|
||||
this.context = context;
|
||||
@@ -121,14 +121,6 @@ public class MyTracksOverlay extends Overlay {
|
||||
errorCirclePaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
public void setSelectedTrack(Track track) {
|
||||
selectedTrack = track;
|
||||
}
|
||||
|
||||
public Track getSelectedTrack() {
|
||||
return selectedTrack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a location to the map overlay.
|
||||
*
|
||||
@@ -137,19 +129,35 @@ public class MyTracksOverlay extends Overlay {
|
||||
* @param l the location to add
|
||||
*/
|
||||
public void addLocation(Location l) {
|
||||
points.add(l);
|
||||
if (l != null) {
|
||||
synchronized (points) {
|
||||
points.add(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addWaypoint(Waypoint wpt) {
|
||||
waypoints.add(wpt);
|
||||
}
|
||||
|
||||
public int getNumLocations() {
|
||||
synchronized (points) {
|
||||
return points.size();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearWaypoints() {
|
||||
waypoints.clear();
|
||||
}
|
||||
|
||||
public void clearPoints() {
|
||||
points.clear();
|
||||
synchronized (points) {
|
||||
points.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTrackDrawingEnabled(boolean trackDrawingEnabled) {
|
||||
this.trackDrawingEnabled = trackDrawingEnabled;
|
||||
}
|
||||
|
||||
public void setShowEndMarker(boolean showEndMarker) {
|
||||
@@ -158,15 +166,23 @@ public class MyTracksOverlay extends Overlay {
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
|
||||
|
||||
if (shadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw the selected track:
|
||||
drawTrack(canvas, mapView, selectedTrack, true);
|
||||
if (trackDrawingEnabled) {
|
||||
// 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;
|
||||
for (int i = 1; i < currentWaypoints.size(); i++) {
|
||||
Waypoint wpt = currentWaypoints.get(i);
|
||||
@@ -189,11 +205,14 @@ public class MyTracksOverlay extends Overlay {
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawMyLocation(Canvas canvas, MapView mapView) {
|
||||
// Draw the arrow icon:
|
||||
if (myLocation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GeoPoint geoPoint = new GeoPoint(
|
||||
(int) (myLocation.getLatitude() * 1E6),
|
||||
(int) (myLocation.getLongitude() * 1E6));
|
||||
@@ -210,79 +229,119 @@ public class MyTracksOverlay extends Overlay {
|
||||
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
|
||||
}
|
||||
|
||||
public void drawTrack(Canvas canvas, MapView mapView, Track track,
|
||||
boolean selected) {
|
||||
|
||||
if (track == null) {
|
||||
return;
|
||||
}
|
||||
if (points.size() < 2) {
|
||||
return;
|
||||
}
|
||||
private void drawTrack(Canvas canvas, MapView mapView) {
|
||||
Path path;
|
||||
Point pt = new Point();
|
||||
Location lastValidLocation;
|
||||
int locLon = 0, locLat = 0;
|
||||
GeoPoint firstGeoPoint = null;
|
||||
boolean lastLocValid;
|
||||
|
||||
// Get the current viewing window:
|
||||
int w = mapView.getLongitudeSpan();
|
||||
int h = mapView.getLatitudeSpan();
|
||||
int cx = mapView.getMapCenter().getLongitudeE6();
|
||||
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();
|
||||
GeoPoint geoPoint;
|
||||
Location loc;
|
||||
Location lastValidLocation = null;
|
||||
Path path;
|
||||
boolean wasInside;
|
||||
|
||||
// Do as much allocation and preparation outside the loop over track
|
||||
// points:
|
||||
wasInside = false;
|
||||
int locLon = 0, locLat = 0, minLon, maxLon, minLat, maxLat;
|
||||
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;
|
||||
// Global bounding box, including points not visible
|
||||
int allMinLat, allMinLon, allMaxLat, allMaxLon;
|
||||
|
||||
synchronized (points) {
|
||||
int numPoints = points.size();
|
||||
if (numPoints < 2) {
|
||||
return;
|
||||
}
|
||||
locLon = (int) (loc.getLongitude() * 1E6);
|
||||
locLat = (int) (loc.getLatitude() * 1E6);
|
||||
|
||||
// Draw a line segment if it's inside the viewing window:
|
||||
if (locLat < 90E6 && lastLocLat < 90E6) {
|
||||
lastValidLocation = loc;
|
||||
minLon = Math.min(locLon, lastLocLon);
|
||||
maxLon = Math.max(locLon, lastLocLon);
|
||||
minLat = Math.min(locLat, lastLocLat);
|
||||
maxLat = Math.max(locLat, lastLocLat);
|
||||
if (rect.intersects(minLon, minLat, maxLon, maxLat)) {
|
||||
if (!wasInside) {
|
||||
geoPoint = new GeoPoint(lastLocLat, lastLocLon);
|
||||
mapView.getProjection().toPixels(geoPoint, pt);
|
||||
path.moveTo(pt.x, pt.y);
|
||||
GeoPoint geoPoint;
|
||||
Location loc;
|
||||
int minLon, maxLon, minLat, maxLat;
|
||||
lastValidLocation = points.get(0);
|
||||
int lastLocLon = allMinLon = allMaxLon = (int) (lastValidLocation.getLongitude() * 1E6);
|
||||
int lastLocLat = allMinLat = allMaxLat = (int) (lastValidLocation.getLatitude() * 1E6);
|
||||
lastLocValid = MyTracksUtils.isValidLocation(lastValidLocation);
|
||||
boolean lastLocVisible = false;
|
||||
path = new Path();
|
||||
|
||||
// Loop over track points:
|
||||
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 {
|
||||
wasInside = false;
|
||||
lastLocLon = locLon;
|
||||
lastLocLat = locLat;
|
||||
lastLocValid = locValid;
|
||||
lastLocVisible = locVisible;
|
||||
}
|
||||
lastLocLon = locLon;
|
||||
lastLocLat = locLat;
|
||||
}
|
||||
|
||||
canvas.drawPath(path, selectedTrackPaint);
|
||||
|
||||
if (drawBounds) {
|
||||
drawBoundingBox(canvas, mapView,
|
||||
allMinLat, allMinLon, allMaxLat, allMaxLon);
|
||||
}
|
||||
|
||||
// Draw the "End" marker:
|
||||
Location currentLastValidLocation = lastValidLocation;
|
||||
if (showEndMarker && currentLastValidLocation != null) {
|
||||
if (showEndMarker && lastValidLocation != null) {
|
||||
canvas.save();
|
||||
geoPoint = MyTracksUtils.getGeoPoint(currentLastValidLocation);
|
||||
GeoPoint geoPoint = MyTracksUtils.getGeoPoint(lastValidLocation);
|
||||
mapView.getProjection().toPixels(geoPoint, pt);
|
||||
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
|
||||
endMarker.draw(canvas);
|
||||
@@ -290,22 +349,46 @@ public class MyTracksOverlay extends Overlay {
|
||||
}
|
||||
|
||||
// Draw the "Start" marker:
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
loc = points.get(i);
|
||||
if (loc.getLatitude() < 90) {
|
||||
locLon = (int) (loc.getLongitude() * 1E6);
|
||||
locLat = (int) (loc.getLatitude() * 1E6);
|
||||
geoPoint = new GeoPoint(locLat, locLon);
|
||||
mapView.getProjection().toPixels(geoPoint, pt);
|
||||
canvas.save();
|
||||
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
|
||||
startMarker.draw(canvas);
|
||||
canvas.restore();
|
||||
break;
|
||||
}
|
||||
if (firstGeoPoint != null) {
|
||||
mapView.getProjection().toPixels(firstGeoPoint, pt);
|
||||
canvas.save();
|
||||
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
|
||||
startMarker.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
public boolean draw(Canvas canvas, MapView mapView, boolean shadow,
|
||||
long when) {
|
||||
@@ -374,4 +457,8 @@ public class MyTracksOverlay extends Overlay {
|
||||
}
|
||||
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.setValue("-1");
|
||||
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;
|
||||
TripStatistics waypointStats = waypoint.getStatistics();
|
||||
utils.setAllStats(waypointStats);
|
||||
utils.setTime(R.id.total_time_register, waypointStats.getTotalTime());
|
||||
utils.setAltitude(
|
||||
R.id.elevation_register, waypoint.getLocation().getAltitude());
|
||||
break;
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
@@ -50,24 +48,22 @@ public class NavControls {
|
||||
|
||||
/**
|
||||
* A touchable image view.
|
||||
* When touched it changes the navigation control icons accordingly.
|
||||
*/
|
||||
public class TouchLayout extends RelativeLayout {
|
||||
private final ImageView arrow;
|
||||
private class TouchLayout extends RelativeLayout implements Runnable {
|
||||
private final boolean isLeft;
|
||||
private final ImageView icon;
|
||||
|
||||
public TouchLayout(Context context, boolean isLeft) {
|
||||
super(context);
|
||||
arrow = new ImageView(context);
|
||||
arrow.setImageDrawable(context.getResources().getDrawable(
|
||||
isLeft ? R.drawable.btn_arrow_left : R.drawable.btn_arrow_right));
|
||||
icon = new ImageView(context);
|
||||
this.isLeft = isLeft;
|
||||
this.icon = new ImageView(context);
|
||||
icon.setVisibility(View.GONE);
|
||||
addView(arrow);
|
||||
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.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@@ -77,18 +73,11 @@ public class NavControls {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
setPressed(true);
|
||||
hide();
|
||||
if (this == prevImage) {
|
||||
if (prevRunnable != null) {
|
||||
handler.post(prevRunnable);
|
||||
return true;
|
||||
}
|
||||
} else if (this == nextImage) {
|
||||
if (nextRunnable != null) {
|
||||
handler.post(nextRunnable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
shiftIcons(isLeft);
|
||||
|
||||
// Call the user back
|
||||
handler.post(this);
|
||||
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
setPressed(false);
|
||||
@@ -96,55 +85,69 @@ public class NavControls {
|
||||
}
|
||||
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 Runnable dismissControls = new Runnable() {
|
||||
public void run() {
|
||||
hide();
|
||||
}
|
||||
};
|
||||
|
||||
private final TouchLayout prevImage;
|
||||
private final TouchLayout nextImage;
|
||||
private final int[] leftIcons;
|
||||
private final int[] rightIcons;
|
||||
private final Runnable touchRunnable;
|
||||
|
||||
private boolean isVisible = false;
|
||||
private boolean hasNext = true;
|
||||
private boolean hasPrev = true;
|
||||
private int currentIcons;
|
||||
|
||||
public NavControls(Context context, ViewGroup container,
|
||||
Runnable prevRunnable, Runnable nextRunnable) {
|
||||
this.context = context;
|
||||
this.prevRunnable = prevRunnable;
|
||||
this.nextRunnable = nextRunnable;
|
||||
int[] leftIcons, int[] rightIcons,
|
||||
Runnable touchRunnable) {
|
||||
this.leftIcons = leftIcons;
|
||||
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.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.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
|
||||
prevParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
|
||||
nextParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
prevParams.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
nextParams.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
|
||||
nextImage = new TouchLayout(context, false);
|
||||
prevImage = new TouchLayout(context, true);
|
||||
nextImage.setLayoutParams(nextParams);
|
||||
prevImage.setLayoutParams(prevParams);
|
||||
nextImage.setVisibility(View.INVISIBLE);
|
||||
prevImage.setVisibility(View.INVISIBLE);
|
||||
|
||||
container.addView(prevImage);
|
||||
container.addView(nextImage);
|
||||
}
|
||||
|
||||
public void setHasNext(boolean hasNext) {
|
||||
this.hasNext = hasNext;
|
||||
}
|
||||
|
||||
public void setHasPrev(boolean hasPrev) {
|
||||
this.hasPrev = hasPrev;
|
||||
prevImage.setIcon(leftIcons[0]);
|
||||
nextImage.setIcon(rightIcons[0]);
|
||||
this.currentIcons = 0;
|
||||
}
|
||||
|
||||
private void keepVisible() {
|
||||
@@ -156,20 +159,18 @@ public class NavControls {
|
||||
|
||||
public void show() {
|
||||
if (!isVisible) {
|
||||
if (prevRunnable != null && hasPrev) {
|
||||
SHOW_PREV_ANIMATION.setDuration(500);
|
||||
SHOW_PREV_ANIMATION.startNow();
|
||||
prevImage.setPressed(false);
|
||||
prevImage.setAnimation(SHOW_PREV_ANIMATION);
|
||||
prevImage.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (nextRunnable != null && hasNext) {
|
||||
SHOW_NEXT_ANIMATION.setDuration(500);
|
||||
SHOW_NEXT_ANIMATION.startNow();
|
||||
nextImage.setPressed(false);
|
||||
nextImage.setAnimation(SHOW_NEXT_ANIMATION);
|
||||
nextImage.setVisibility(View.VISIBLE);
|
||||
}
|
||||
SHOW_PREV_ANIMATION.setDuration(500);
|
||||
SHOW_PREV_ANIMATION.startNow();
|
||||
prevImage.setPressed(false);
|
||||
prevImage.setAnimation(SHOW_PREV_ANIMATION);
|
||||
prevImage.setVisibility(View.VISIBLE);
|
||||
|
||||
SHOW_NEXT_ANIMATION.setDuration(500);
|
||||
SHOW_NEXT_ANIMATION.startNow();
|
||||
nextImage.setPressed(false);
|
||||
nextImage.setAnimation(SHOW_NEXT_ANIMATION);
|
||||
nextImage.setVisibility(View.VISIBLE);
|
||||
|
||||
isVisible = true;
|
||||
keepVisible();
|
||||
} else {
|
||||
@@ -188,33 +189,25 @@ public class NavControls {
|
||||
|
||||
public void hide() {
|
||||
isVisible = false;
|
||||
if (prevRunnable != null) {
|
||||
if (hasPrev) {
|
||||
prevImage.setAnimation(HIDE_PREV_ANIMATION);
|
||||
HIDE_PREV_ANIMATION.setDuration(500);
|
||||
HIDE_PREV_ANIMATION.startNow();
|
||||
} else {
|
||||
prevImage.clearAnimation();
|
||||
}
|
||||
prevImage.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);
|
||||
}
|
||||
prevImage.setAnimation(HIDE_PREV_ANIMATION);
|
||||
HIDE_PREV_ANIMATION.setDuration(500);
|
||||
HIDE_PREV_ANIMATION.startNow();
|
||||
prevImage.setVisibility(View.INVISIBLE);
|
||||
|
||||
nextImage.setAnimation(HIDE_NEXT_ANIMATION);
|
||||
HIDE_NEXT_ANIMATION.setDuration(500);
|
||||
HIDE_NEXT_ANIMATION.startNow();
|
||||
nextImage.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
public void setLeftIcon(int resourceId) {
|
||||
prevImage.setIcon(context.getResources().getDrawable(resourceId));
|
||||
public int getCurrentIcons() {
|
||||
return currentIcons;
|
||||
}
|
||||
|
||||
public void setRightIcon(int resourceId) {
|
||||
nextImage.setIcon(context.getResources().getDrawable(resourceId));
|
||||
private void shiftIcons(boolean isLeft) {
|
||||
// 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
|
||||
*/
|
||||
public class StatsActivity extends Activity
|
||||
implements OnSharedPreferenceChangeListener, LocationListener {
|
||||
|
||||
implements OnSharedPreferenceChangeListener {
|
||||
|
||||
private final StatsUtilities utils;
|
||||
private UIUpdateThread thread;
|
||||
|
||||
private ContentObserver observer;
|
||||
private LocationManager locationManager;
|
||||
|
||||
/**
|
||||
* The id of the currently selected track.
|
||||
@@ -96,7 +94,6 @@ public class StatsActivity extends Activity
|
||||
private boolean showCurrentSegment = false;
|
||||
|
||||
private final MyTracksProviderUtils providerUtils;
|
||||
private Track track = null;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
checkLiveTrack();
|
||||
restoreStats();
|
||||
updateLocation(null);
|
||||
showUnknownLocation();
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
locationManager =
|
||||
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
utils.setMetricUnits(metricUnits);
|
||||
utils.setReportSpeed(displaySpeed);
|
||||
utils.updateUnits();
|
||||
@@ -214,7 +209,7 @@ public class StatsActivity extends Activity
|
||||
activityOnTop = true;
|
||||
checkLiveTrack();
|
||||
restoreStats();
|
||||
updateLocation(null);
|
||||
showUnknownLocation();
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@@ -234,7 +229,7 @@ public class StatsActivity extends Activity
|
||||
-1);
|
||||
checkLiveTrack();
|
||||
restoreStats();
|
||||
updateLocation(null);
|
||||
showUnknownLocation();
|
||||
} else if (key.equals(getString(R.string.recording_track_key))) {
|
||||
recordingTrackId =
|
||||
sharedPreferences.getLong(
|
||||
@@ -242,7 +237,7 @@ public class StatsActivity extends Activity
|
||||
-1);
|
||||
checkLiveTrack();
|
||||
restoreStats();
|
||||
updateLocation(null);
|
||||
showUnknownLocation();
|
||||
} else if (key.equals(getString(R.string.metric_units_key))) {
|
||||
metricUnits =
|
||||
sharedPreferences.getBoolean(
|
||||
@@ -299,29 +294,36 @@ public class StatsActivity extends Activity
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location l) {
|
||||
if (selectedTrackIsRecording()) {
|
||||
updateLocation(l);
|
||||
private final LocationListener locationListener = new LocationListener() {
|
||||
@Override
|
||||
public void onLocationChanged(Location l) {
|
||||
if (selectedTrackIsRecording()) {
|
||||
showLocation(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers to receive location updates from the GPS location provider.
|
||||
*/
|
||||
private void registerLocationListener() {
|
||||
LocationManager locationManager =
|
||||
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null) {
|
||||
LocationProvider gpsProvider =
|
||||
locationManager.getProvider(MyTracksConstants.GPS_PROVIDER);
|
||||
@@ -334,7 +336,7 @@ public class StatsActivity extends Activity
|
||||
+ 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.
|
||||
*/
|
||||
private void unregisterLocationListener() {
|
||||
LocationManager locationManager =
|
||||
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null) {
|
||||
locationManager.removeUpdates(this);
|
||||
locationManager.removeUpdates(locationListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +368,7 @@ public class StatsActivity extends Activity
|
||||
return;
|
||||
}
|
||||
|
||||
track = providerUtils.getTrack(selectedTrackId);
|
||||
Track track = providerUtils.getTrack(selectedTrackId);
|
||||
if (track == null || track.getStatistics() == null) {
|
||||
utils.setAllToUnknown();
|
||||
return;
|
||||
@@ -421,17 +425,17 @@ public class StatsActivity extends Activity
|
||||
*
|
||||
* @param l may be null (will set location fields to unknown)
|
||||
*/
|
||||
private void updateLocation(Location l) {
|
||||
if (l == null) {
|
||||
utils.setUnknown(R.id.elevation_register);
|
||||
utils.setUnknown(R.id.latitude_register);
|
||||
utils.setUnknown(R.id.longitude_register);
|
||||
utils.setUnknown(R.id.speed_register);
|
||||
} else {
|
||||
utils.setAltitude(R.id.elevation_register, l.getAltitude());
|
||||
utils.setLatLong(R.id.latitude_register, l.getLatitude());
|
||||
utils.setLatLong(R.id.longitude_register, l.getLongitude());
|
||||
utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6);
|
||||
}
|
||||
private void showLocation(Location l) {
|
||||
utils.setAltitude(R.id.elevation_register, l.getAltitude());
|
||||
utils.setLatLong(R.id.latitude_register, l.getLatitude());
|
||||
utils.setLatLong(R.id.longitude_register, l.getLongitude());
|
||||
utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6);
|
||||
}
|
||||
|
||||
private void showUnknownLocation() {
|
||||
utils.setUnknown(R.id.elevation_register);
|
||||
utils.setUnknown(R.id.latitude_register);
|
||||
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.maps.mytracks.R;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
/**
|
||||
* Various utility functions for views that display statistics information.
|
||||
*
|
||||
@@ -218,7 +218,7 @@ public class StatsUtilities {
|
||||
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());
|
||||
setDistance(R.id.total_distance_register, stats.getTotalDistance() / 1000);
|
||||
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());
|
||||
setGrade(R.id.min_grade_register, stats.getMinGrade());
|
||||
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) {
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility to access data from the mytracks content provider.
|
||||
*
|
||||
@@ -184,6 +186,15 @@ public interface MyTracksProviderUtils {
|
||||
* @return a Track object, or null if not found
|
||||
*/
|
||||
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.
|
||||
@@ -203,9 +214,24 @@ public interface MyTracksProviderUtils {
|
||||
*
|
||||
* @param track to load locations for
|
||||
* @param buffer an array of locations to fill
|
||||
* @deprecated use {@link #fillTrackPoints} instead
|
||||
*/
|
||||
@Deprecated
|
||||
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.
|
||||
* Caller gets to own the returned cursor. Don't forget to close it.
|
||||
@@ -293,7 +319,15 @@ public interface MyTracksProviderUtils {
|
||||
* @return a new location object
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -24,6 +24,9 @@ import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class providing easy access to locations and tracks in the
|
||||
* MyTracksProvider. All static members.
|
||||
@@ -168,6 +171,15 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
|
||||
@Override
|
||||
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 idxLongitude =
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
|
||||
@@ -177,7 +189,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
||||
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
||||
|
||||
Location location = new Location("");
|
||||
if (!cursor.isNull(idxLatitude)) {
|
||||
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
|
||||
}
|
||||
@@ -199,7 +210,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
if (!cursor.isNull(idxAccuracy)) {
|
||||
location.setAccuracy(cursor.getFloat(idxAccuracy));
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -790,6 +800,21 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
String select = TracksColumns._ID + "=" + id;
|
||||
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
|
||||
public long getTrackPoints(Track track, int maxPoints) {
|
||||
@@ -822,6 +847,16 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
|
||||
@Override
|
||||
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()
|
||||
: buffer.getLastLocationRead();
|
||||
buffer.reset();
|
||||
@@ -849,13 +884,17 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
final int idColumnIdx =
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
do {
|
||||
Location location = createLocation(cursor);
|
||||
if (location == null) {
|
||||
continue;
|
||||
if (reuseLocations) {
|
||||
fillLocation(cursor, buffer.add(cursor.getLong(idColumnIdx)));
|
||||
} else {
|
||||
Location location = createLocation(cursor);
|
||||
if (location == null) {
|
||||
continue;
|
||||
}
|
||||
buffer.add(location, cursor.getLong(idColumnIdx));
|
||||
}
|
||||
buffer.add(location, cursor.getLong(idColumnIdx));
|
||||
} while (cursor.moveToNext());
|
||||
|
||||
|
||||
if (buffer.getLocationsLoaded() == 0) {
|
||||
Log.w(MyTracksProvider.TAG, "No locations read.");
|
||||
buffer.resetAt(startingPoint + buffer.getSize());
|
||||
|
||||
@@ -94,6 +94,22 @@ public class TrackBuffer {
|
||||
buffer[locationsLoaded++] = location;
|
||||
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
|
||||
|
||||
@@ -264,7 +264,7 @@ public class TrackWriter {
|
||||
while (buffer.getLastLocationRead() < track.getStopId()) {
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"Reading track points starting at: " + buffer.getLastLocationRead());
|
||||
providerUtils.getTrackPoints(track, buffer);
|
||||
providerUtils.fillTrackPoints(track, buffer);
|
||||
if (!wroteFirst) {
|
||||
Location first = buffer.findStartLocation();
|
||||
writer.writeBeginTrack(first);
|
||||
|
||||
@@ -103,4 +103,9 @@ public class ExtremityMonitor {
|
||||
public void setMax(double 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
|
||||
*/
|
||||
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>() {
|
||||
private int lastId = 1;
|
||||
private int reads = 0;
|
||||
|
||||