Merge from head.
@@ -7,6 +7,7 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry exported="true" kind="lib" path="/MyTracksLib/libs/protobuf-java-2.3.0-lite.jar"/>
|
||||
<classpathentry kind="lib" path="libs/google-api-client-1.2.1-alpha.jar"/>
|
||||
<classpathentry kind="src" path="MyTracksLib_src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@@ -56,11 +56,6 @@ limitations under the License.
|
||||
android:label="MyTracks"
|
||||
android:exported="false" />
|
||||
|
||||
<activity android:name="com.google.android.apps.mymaps.MyMapsList"
|
||||
android:label="@string/map_list_title"
|
||||
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|fontScale"
|
||||
android:exported="false" />
|
||||
|
||||
<activity android:name="com.google.android.apps.mytracks.MyTracksList"
|
||||
android:label="Recorded Tracks" />
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<launchConfiguration type="com.android.ide.eclipse.adt.debug.LaunchConfigType">
|
||||
<intAttribute key="com.android.ide.eclipse.adt.action" value="0"/>
|
||||
<stringAttribute key="com.android.ide.eclipse.adt.commandline" value=""/>
|
||||
@@ -16,8 +16,8 @@
|
||||
<listEntry value="1"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ALLOW_TERMINATE" value="true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="MyTracks"/>
|
||||
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 343 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
@@ -1,103 +0,0 @@
|
||||
<?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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apl/res/com.google.android.maps"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/list_bar"
|
||||
android:layout_weight="0"
|
||||
android:padding="3dip">
|
||||
<TextView android:id="@+id/message"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16dip"
|
||||
android:textColor="#fff"
|
||||
android:text="@string/select_map_message"
|
||||
android:layout_weight="0"/>
|
||||
</LinearLayout>
|
||||
<ListView android:id="@+id/maplist"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
<TextView android:id="@+id/mapslist_empty"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:padding="3dip"
|
||||
android:textSize="16dip"
|
||||
android:textColor="#c00"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/mapslist_no_maps_available"
|
||||
android:visibility="gone"/>
|
||||
<TextView android:id="@+id/failed"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:padding="3dip"
|
||||
android:textSize="16dip"
|
||||
android:textColor="#c00"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/maps_list_failed"
|
||||
android:visibility="gone"/>
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/list_bar"
|
||||
android:layout_weight="0"
|
||||
android:padding="3dip">
|
||||
<LinearLayout android:id="@+id/loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="3dip"
|
||||
android:layout_gravity="center">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
<ProgressBar
|
||||
android:layout_height="20dip"
|
||||
android:layout_width="20dip"
|
||||
android:layout_gravity="center"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingLeft="3dip"
|
||||
android:textSize="16dip"
|
||||
android:textColor="#fff"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/maps_list_loading"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -42,7 +42,7 @@ limitations under the License.
|
||||
android:layout_marginTop="5dip"
|
||||
android:layout_marginLeft="5dip">
|
||||
<TableRow>
|
||||
<ImageView android:src="@drawable/mymaps_icon"
|
||||
<ImageView android:src="@drawable/googledocs_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<LinearLayout
|
||||
@@ -50,24 +50,12 @@ limitations under the License.
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginLeft="10dip">
|
||||
<CheckBox android:id="@+id/sendtogoogle_google_mymaps"
|
||||
<CheckBox android:id="@+id/sendtogoogle_google_fusiontables"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sendtogoogle_google_mymaps"
|
||||
android:text="@string/sendtogoogle_google_fusiontables"
|
||||
android:textSize="20dip"
|
||||
android:checked="true"/>
|
||||
<RadioGroup android:id="@+id/sendtogoogle_group_mymaps"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<RadioButton android:id="@+id/sendtogoogle_create_new_map"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sendtogoogle_create_new_map"/>
|
||||
<RadioButton android:id="@+id/sendtogoogle_pick_existing_map"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sendtogoogle_pick_existing_map"/>
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/menu_start_recording"
|
||||
android:icon="@drawable/menu_record"
|
||||
android:icon="@drawable/ic_menu_recordtrack"
|
||||
android:title="@string/start_recording"/>
|
||||
|
||||
<item android:id="@+id/menu_stop_recording"
|
||||
android:icon="@drawable/menu_stop"
|
||||
android:icon="@drawable/ic_menu_stoprecording"
|
||||
android:title="@string/stop_recording"/>
|
||||
|
||||
<item android:id="@+id/menu_list_tracks"
|
||||
android:icon="@drawable/menu_list_tracks"
|
||||
android:icon="@drawable/ic_menu_tracklist"
|
||||
android:title="@string/list_tracks"/>
|
||||
|
||||
<item android:id="@+id/menu_list_markers"
|
||||
android:icon="@drawable/menu_marker"
|
||||
android:icon="@drawable/ic_menu_waypoints"
|
||||
android:title="@string/list_markers"/>
|
||||
|
||||
<item android:id="@+id/menu_settings"
|
||||
@@ -26,7 +26,7 @@
|
||||
android:title="@string/aggregated_stats"/>
|
||||
|
||||
<item android:id="@+id/menu_sensor_state"
|
||||
android:icon="@drawable/menu_sensor_status"
|
||||
android:icon="@drawable/ic_menu_sensorstatus"
|
||||
android:orderInCategory="1000"
|
||||
android:title="@string/sensor_state"/>
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">S\'ha enviat la ruta a maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">S\'ha enviat la ruta a un full de càlcul a docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">S\'ha enviat la ruta a docs.google.com.</string>
|
||||
<string name="status_mymap_info">Poden passar uns minuts abans que les rutes noves apareguin.</string>
|
||||
<string name="status_enable_gps">Piqueu aquí per anar als paràmetres de seguretat i ubicació i activeu \'habilita els satèl·lits GPS\'. Premeu el botó d\'anar enrere per tornar.</string>
|
||||
<string name="status_approximate_location">Nota: Es mostra una ubicació determinada a partir de les xarxes sense fils i antenes telefòniques.</string>
|
||||
<string name="status_no_location">No es pot determinar la ubicació. Habiliteu més fonts d\'ubicació (vegeu els paràmetres de seguretat i ubicació) i assegureu-vos que el cel és visible. Proveu de nou més tard.</string>
|
||||
@@ -104,6 +103,10 @@ limitations under the License.
|
||||
<string name="error">Error</string>
|
||||
<string name="success">Fet</string>
|
||||
<string name="waypoint">Punt de referència</string>
|
||||
<string name="cancel">Cancel·la</string>
|
||||
<string name="close">Tanca</string>
|
||||
<string name="edit">Edita</string>
|
||||
<string name="save">Desa</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Inici)</string>
|
||||
@@ -241,28 +244,6 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">Satèl·lits GPS</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Seleccioneu un mapa del vostre compte:</string>
|
||||
<string name="create_new_map">Crea un mapa nou...</string>
|
||||
<string name="map_list_title">Envia a Google My Maps</string>
|
||||
<string name="maps_list_loading">S\'estan carregant els mapes...</string>
|
||||
<string name="maps_list_failed">No s\'han pogut carregar els vostres mapes en aquest moment</string>
|
||||
<string name="open_map">Selecciona aquest mapa</string>
|
||||
<string name="edit_map_details">Edita els detalls del mapa</string>
|
||||
<string name="share_map">Comparteix el mapa amb amics</string>
|
||||
<string name="share_track">Comparteix la ruta amb amics</string>
|
||||
<string name="delete_map">Elimina el mapa del vostre compte</string>
|
||||
<string name="map_title">Títol del mapa:</string>
|
||||
<string name="map_description">Descripció del mapa:</string>
|
||||
<string name="map_searchable_edit">Fes que aquest mapa i els seus continguts apareguen als resultats de cerca</string>
|
||||
<string name="map_not_searchable_edit">No permetis que aquest mapa ni els seus continguts es mostrin als resultats de cerca, tot i que sí es podrà visualitzar per altres amb l\'adreça URL directa</string>
|
||||
<string name="map_searchable">Aquest mapa i els seus continguts es mostraran als resultats de cerca</string>
|
||||
<string name="map_not_searchable">Ni els mapes ni els seus continguts es mostraran als resultats de cerca, però podran visualitzar-se per altres a través d\'un enllaç directe.</string>
|
||||
<string name="close">Tanca</string>
|
||||
<string name="edit">Edita</string>
|
||||
<string name="cancel">Cancel·la</string>
|
||||
<string name="save">Desa</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">M\'agradaria compartir un mapa amb tu</string>
|
||||
<string name="share_map_body_format">Crec que estaràs interessat en aquest mapa:
|
||||
@@ -272,6 +253,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">Crec que estaràs interessat en aquesta ruta.
|
||||
|
||||
Per verue aquesta ruta fes servir Google Earth o altre programari de mapes.</string>
|
||||
<string name="share_track">Comparteix la ruta amb amics</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Públic</string>
|
||||
|
||||
@@ -1,398 +1,378 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2010 Google Inc.
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Used by the Menu -->
|
||||
<string name="start_recording">Track aufzeichnen</string>
|
||||
<string name="stop_recording">Aufzeichnung anhalten</string>
|
||||
<string name="switch_to_sat">Satellitenansicht</string>
|
||||
<string name="switch_to_map">Kartenansicht</string>
|
||||
<string name="mylocation">Mein Aufenthaltsort</string>
|
||||
<string name="list_markers">Wegpunkte</string>
|
||||
<string name="delete_all_tracks">Alle Tracks löschen</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="elevation">Höhe</string>
|
||||
<string name="choose_track_message">Bitte wähle einen Track zur Ansicht aus</string>
|
||||
<string name="current_track">Aktueller Track</string>
|
||||
<string name="current_segment">Letztes Segment</string>
|
||||
<string name="sensor_state">Sensorstatus</string>
|
||||
<string name="aggregated_stats">Aggregierte Statistiken</string>
|
||||
|
||||
<!-- Error messages -->
|
||||
<string name="error_no_location_manager">Es wurde kein Dienst zur Positionsermittlung auf diesem Gerät gefunden.</string>
|
||||
<string name="error_no_gps_location_provider">Es wurde kein GPS-Empfänger auf diesem Gerät gefunden.</string>
|
||||
<string name="error_sending_to_mymap">Beim Senden des Tracks an Google MyMaps ist ein Fehler aufgetreten. Bitte versuche es später noch einmal.</string>
|
||||
<string name="error_sending_to_docs">Beim Senden des Tracks an Google Docs ist ein Fehler aufgetreten. Bitte versuche es später noch einmal.</string>
|
||||
<string name="error_unable_to_start_recording">Start der Aufzeichnung fehlgeschlagen. Bitte versuche es noch einmal.</string>
|
||||
<string name="error_unable_to_stop_recording">Anhalten unmöglich. Bitte versuche es noch einmal.</string>
|
||||
<string name="error_unable_to_read_file">Konnte Datei nicht lesen.</string>
|
||||
<string name="error_invalid_gpx_format">Datei ist nicht im GPX-Format.</string>
|
||||
<string name="error_generic">Ein Fehler ist aufgetreten. Bitte versuche es noch einmal.</string>
|
||||
<string name="error_track_does_not_exist">Track existiert nicht.</string>
|
||||
<string name="error_out_of_memory">Die Datei ist zu groß (maximal 500 KB). Operation abgebrochen.</string>
|
||||
<string name="error_unable_to_insert_marker">Der Wegpunkt kann nicht einfügt werden. Keine Positionsbestimmung möglich.</string>
|
||||
|
||||
<!-- Status messages -->
|
||||
<string name="wait_for_fix">Warte auf ein besseres GPS-Signal. Bitte stelle sicher, dass der Himmel sichtbar ist.</string>
|
||||
<string name="status_now_recording">Ein neuer Track wird aufgezeichnet.</string>
|
||||
<string name="status_stopped_recording">Die Aufzeichnung wurde angehalten.</string>
|
||||
<string name="status_new_mymap_has_been_created">Eine neue Karte wurde auf maps.google.com erstellt.</string>
|
||||
<string name="status_tracks_have_been_uploaded">Dein Track wurde auf maps.google.com gespeichert.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Dein Track wurde in einer neuen Tabelle bei docs.google.com gespeichert.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Dein Track wurde an docs.google.com gesendet.</string>
|
||||
<string name="status_mymap_info">Manchmal dauert es ein paar Minuten, bis neue Tracks bei maps.google.com erscheinen.</string>
|
||||
<string name="status_enable_gps">Hier berühren, um zu den Sicherheits- und Positionseinstellungen zu gelangen. Aktiviere \'GPS-Satelliten\'. Drücke dann den Zurück-Knopf, um hierher zurückzukehren.</string>
|
||||
<string name="status_approximate_location">Anmerkung: Eine ungefähre Position wird angezeigt, bestimmt mittels Mobilfunkstationen und WiFi-Netzwerken.</string>
|
||||
<string name="status_no_location">Die Position kann derzeit nicht bestimmt werden. Aktiviere mehr Positionsquellen (siehe Sicherheits- und Positionseinstellungen) und stelle sicher, dass der Himmel sichtbar ist.</string>
|
||||
<string name="status_waypoint_inserted">Ein Wegpunkt wurde eingefügt.</string>
|
||||
<string name="status_statistics_inserted">Ein Statistikwegpunkt wurde eingefügt.</string>
|
||||
|
||||
<!-- Progress dialog -->
|
||||
<string name="progress_title">Bitte warten…</string>
|
||||
<string name="progress_message">Dein Track wird an Google gesendet...</string>
|
||||
<string name="progress_message_reading_track">Lese Track...</string>
|
||||
<string name="progress_message_preparing_track">Bereite Track vor...</string>
|
||||
<string name="progress_message_authenticating_mymaps">Anmeldung bei Google Maps...</string>
|
||||
<string name="progress_message_authenticating_docs">Anmeldung bei Google Docs...</string>
|
||||
<string name="progress_message_sending_docs">Track wird an Google Docs gesendet...</string>
|
||||
<string name="progress_message_sending_mymaps">Track wird an Google Maps gesendet...</string>
|
||||
<string name="progress_message_creating_map">Neue \'Meine Karte\' wird erstellt...</string>
|
||||
<string name="import_progress_message">Importiere GPS-Track. Das kann ein wenig dauern.</string>
|
||||
<string name="write_progress_message">Schreibe Datei auf SD-Karte. Das kann ein wenig dauern.</string>
|
||||
<string name="progress_message_updating_track">Aktualisiere Trackstatistik...</string>
|
||||
|
||||
<!-- Used by DeleteAllDataHandler -->
|
||||
<string name="are_you_sure_question">Bist Du Dir wirklich sicher?</string>
|
||||
<string name="all_data_will_be_permanently_deleted">Alle Tracks und Wegpunkte werden endgültig von diesem Gerät gelöscht.</string>
|
||||
<string name="track_will_be_permanently_deleted">Dieser Track und seine Wegpunkte werden endgültig von diesem Gerät gelöscht.</string>
|
||||
<string name="marker_will_be_permanently_deleted">Dieser Wegpunkt wird endgültig von diesem Gerät gelöscht.</string>
|
||||
|
||||
<!-- Text and links when uploading to Google MyMaps -->
|
||||
<string name="new_map_description">Erstellt von My Tracks für Android.</string>
|
||||
<string name="my_map_link">Erstellt von <a href=\'http://mytracks.appspot.com\'>My Tracks</a> für Android.</string>
|
||||
<string name="new_map_title">Neue Karte</string>
|
||||
|
||||
<!-- Formatting of track and marker descriptions -->
|
||||
<string name="points">Punkte</string>
|
||||
<string name="recorded_date">Aufgezeichnet am</string>
|
||||
<string name="category">Aktivität</string>
|
||||
<string name="waypoint_category">Art des Wegpunktes</string>
|
||||
<string name="part">(Teil %d)</string>
|
||||
|
||||
<!-- Used by the track recording service -->
|
||||
<string name="recording_your_track">Dein Track wird aufgezeichnet…</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="no">Nein</string>
|
||||
<string name="yes">Ja</string>
|
||||
<string name="error">Fehler</string>
|
||||
<string name="success">Erfolgreich</string>
|
||||
<string name="waypoint">Wegpunkt</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Beginn)</string>
|
||||
<string name="end">(Ende)</string>
|
||||
|
||||
<!-- TrackList -->
|
||||
<string name="tracklist_show_track">Anzeigen</string>
|
||||
<string name="tracklist_send_to_mymaps">An eine Google MyMaps-Karte senden</string>
|
||||
<string name="tracklist_send_to_new_mymap">An eine neue Google MyMaps-Karte senden</string>
|
||||
<string name="tracklist_send_to_docs">An Google Docs senden</string>
|
||||
<string name="tracklist_send_to_google">An Google senden…</string>
|
||||
<string name="tracklist_delete_track">Vom Gerät löschen…</string>
|
||||
<string name="tracklist_edit_track">Bearbeiten</string>
|
||||
<string name="tracklist_write_to_sd">Auf SD-Karte speichern…</string>
|
||||
<string name="tracklist_save_as_gpx">GPX-Datei exportieren</string>
|
||||
<string name="tracklist_save_as_kml">KML-Datei exportieren</string>
|
||||
<string name="tracklist_save_as_csv">CSV-Datei exportieren</string>
|
||||
<string name="tracklist_save_as_tcx">TCX-Datei exportieren</string>
|
||||
<string name="tracklist_btn_send_all_to_mymaps">An Google MyMaps senden</string>
|
||||
<string name="tracklist_btn_delete_all">Alle Tracks löschen</string>
|
||||
<!-- Export All -->
|
||||
<string name="tracklist_btn_export_all">Alle Tracks exportieren</string>
|
||||
<string name="export_done">Exportieren beendet</string>
|
||||
<string name="tracklist_pick_one">Einen Track auswählen. Lange berühren für mehr Optionen.</string>
|
||||
<string name="tracklist_all_tracks">Alle Tracks</string>
|
||||
<string name="tracklist_this_track">Dieser Track</string>
|
||||
<string name="tracklist_share_track">Mit Freunden teilen…</string>
|
||||
<string name="tracklist_share_link">Ein Link zur Google MyMaps-Karte</string>
|
||||
<string name="tracklist_share_gpx_file">Eine GPX-Datei</string>
|
||||
<string name="tracklist_share_kml_file">Eine KML-Datei</string>
|
||||
<string name="tracklist_share_csv_file">Eine CSV-Datei</string>
|
||||
<string name="tracklist_share_tcx_file">Eine TCX-Datei</string>
|
||||
<string name="tracklist_clear_map">Karte zurücksetzen.</string>
|
||||
|
||||
<!-- Waypoints list -->
|
||||
<string name="waypointslist_btn_delete_all">Alle Wegpunkte löschen</string>
|
||||
<string name="waypointslist_this_waypoint">Dieser Wegpunkt</string>
|
||||
<string name="waypointslist_delete_waypoint">Löschen</string>
|
||||
<string name="waypointslist_edit_waypoint">Details bearbeiten</string>
|
||||
<string name="waypointslist_show_waypoint">Auf der Karte zeigen</string>
|
||||
<string name="waypointslist_btn_insert_waypoint">Wegpunkt einfügen</string>
|
||||
<string name="waypointslist_btn_insert_statistics">Statistik einfügen</string>
|
||||
<string name="waypointslist_pick_one">Wegpunkt auswählen. Lange berühren für mehr Optionen.</string>
|
||||
|
||||
<!-- MyTracks Details -->
|
||||
<string name="trackdetails_title">Trackdetails</string>
|
||||
<string name="trackdetails_name">Name</string>
|
||||
<string name="trackdetails_description">Beschreibung</string>
|
||||
<string name="trackdetails_category">Aktivität (z. B. Wandern, Fahrradfahren, Laufen, Klettern…)</string>
|
||||
|
||||
<!-- MyTracks Waypoint Details -->
|
||||
<string name="waypointdetails_title">Wegpunktdetails</string>
|
||||
<string name="waypointdetails_name">Wegpunktname</string>
|
||||
<string name="waypointdetails_description">Wegpunktbeschreibung</string>
|
||||
<string name="waypointdetails_category">Art des Wegpunktes (z. B. Gebäude, Schlagloch, Sehenswürdigkeit…)</string>
|
||||
|
||||
<!-- MyTracks Settings -->
|
||||
<string name="mytracks_settings">Einstellungen für My Tracks</string>
|
||||
<string name="settings_display">Anzeige</string>
|
||||
<string name="settings_recording">Aufzeichnung</string>
|
||||
<string name="settings_sharing">Mit Freunden teilen</string>
|
||||
|
||||
<string name="settings_not_available_summary">In dieser Android-Version nicht verfügbar</string>
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<string name="settings_announcement_frequency">Dauer zwischen Ansagen</string>
|
||||
<string name="settings_announcement_frequency_summary">Kürzeste Zeit zwischen zwei Ansagen</string>
|
||||
|
||||
<string name="settings_min_recording_distance">Kleinste Entfernung zwischen Punkten</string>
|
||||
<string name="settings_min_recording_distance_summary">Kleinste Entfernung zwischen zwei Positionsaufzeichnungen</string>
|
||||
|
||||
<string name="settings_max_recording_distance">Neues Segment, wenn Punkte weiter entfernt als</string>
|
||||
<string name="settings_max_recording_distance_summary">Der Track wird in zwei geteilt, wenn zwei folgende Punkte weiter entfernt sind als hier angegeben.</string>
|
||||
|
||||
<string name="settings_min_recording_interval">Aufzeichnungsfrequenz</string>
|
||||
<string name="settings_min_recording_interval_summary">Kürzeste Zeit zwischen zwei Positionsaufzeichnungen</string>
|
||||
|
||||
<string name="settings_min_required_accuracy">Aufzeichnung pausieren, wenn Genauigkeit kleiner als</string>
|
||||
<string name="settings_min_required_accuracy_summary">Die Aufzeichnung wird kurzfristig angehalten, wenn Deine Position nicht genauer als hier angegeben ermittelt werden kann.</string>
|
||||
|
||||
<string name="settings_metric_units">Entfernungen in metrischen Einheiten anzeigen</string>
|
||||
<string name="settings_metric_units_summary_on">Alle Entfernungen werden in metrischen Einheiten angezeigt.</string>
|
||||
<string name="settings_metric_units_summary_off">Alle Entfernungen werden in imperialen Längeneinheiten angezeigt.</string>
|
||||
|
||||
<string name="settings_map_public">Karten auf maps.google.com veröffentlichen</string>
|
||||
<string name="settings_map_public_summary_on">Hochgeladene Karten auf maps.google.com sind für alle sichtbar.</string>
|
||||
<string name="settings_map_public_summary_off">Hochgeladene Karten auf maps.google.com werden nicht öffentlich sichtbar aufgelistet.</string>
|
||||
|
||||
<string name="settings_report_speed_or_pace">Geschwindigkeitsanzeige</string>
|
||||
<string name="settings_report_speed_or_pace_summary_on">Geschwindigkeit wird angezeigt.</string>
|
||||
<string name="settings_report_speed_or_pace_summary_off">Pace wird angezeigt.</string>
|
||||
|
||||
<string name="settings_share_url_only">Nur URL als Einladung zum Teilen senden</string>
|
||||
<string name="settings_share_url_only_summary_on">Beim Teilen mit Freunden wird nur die Karten-URL gesendet.</string>
|
||||
<string name="settings_share_url_only_summary_off">Beim Teilen mit Freunden wird der GPS-Track selbst gesendet.</string>
|
||||
|
||||
<string name="settings_split_frequency">Zerteilungsfrequenz</string>
|
||||
<string name="settings_split_frequency_summary">Längste Dauer oder Entfernung, nach der automatisch ein Marker eingefügt wird.</string>
|
||||
<!-- Used by the Menu -->
|
||||
<string name="start_recording">Track aufzeichnen</string>
|
||||
<string name="stop_recording">Aufzeichnung anhalten</string>
|
||||
<string name="switch_to_sat">Satellitenansicht</string>
|
||||
<string name="switch_to_map">Kartenansicht</string>
|
||||
<string name="mylocation">Mein Aufenthaltsort</string>
|
||||
<string name="list_markers">Wegpunkte</string>
|
||||
<string name="delete_all_tracks">Alle Tracks löschen</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="elevation">Höhe</string>
|
||||
<string name="choose_track_message">Bitte wähle einen Track zur Ansicht aus</string>
|
||||
<string name="current_track">Aktueller Track</string>
|
||||
<string name="current_segment">Letztes Segment</string>
|
||||
<string name="sensor_state">Sensorstatus</string>
|
||||
<string name="aggregated_stats">Aggregierte Statistiken</string>
|
||||
|
||||
<string name="settings_auto_resume_track_timeout">Zeitbegrenzung der Auto-Wiederaufnahme</string>
|
||||
<string name="settings_auto_resume_track_timeout_summary">Maximale Zeit in Minuten, nach der eine abgebrochene Aufzeichnung nach einem Telefonneustart wiederaufgenommen wird.</string>
|
||||
|
||||
<string name="settings_timestamp_track_name">Zeitstempel als Trackname</string>
|
||||
<string name="settings_timestamp_track_name_summary_on">Der Trackname wird aus Datum und Zeit zum Aufzeichnungsbeginn zusammengesetzt.</string>
|
||||
<string name="settings_timestamp_track_name_summary_off">Tracknamen erhalten eine fortlaufende Nummer.</string>
|
||||
|
||||
<string name="pace_label">Pace</string>
|
||||
|
||||
<string name="eclair_required">Wird nur unter Android 2.0 oder höher unterstützt</string>
|
||||
<string name="settings_sensor_type">Sensortyp</string>
|
||||
<string name="settings_sensor_type_summary">Der Typ des zu verbindenden Sensors</string>
|
||||
<string name="settings_sensor_id">Sensor-Id</string>
|
||||
<string name="settings_sensor_id_summary">Die zum Sensor zu sendende Id</string>
|
||||
<string name="sensor_id_malformed">Die Id muss eine Ganzzahl sein.</string>
|
||||
<string name="bluetooth_sensor_title">Bluetooth-Sensor</string>
|
||||
<string name="bluetooth_sensor_summary">Der zu verbindende Bluetoothsensor</string>
|
||||
<string name="settings_modular_stats_display_title">Art der Datenanzeige</string>
|
||||
|
||||
<string name="settings_backup">Datensicherung (BETA)</string>
|
||||
<string name="settings_sd_backup">SD-Karten-Sicherung</string>
|
||||
<string name="settings_backup_to_sd">Jetzt sichern</string>
|
||||
<string name="settings_backup_to_sd_summary">Eine Kopie aller Deiner Tracks und Einstellungen auf die SD-Karte schreiben</string>
|
||||
<string name="settings_restore_from_sd">Jetzt wiederherstellen</string>
|
||||
<string name="settings_restore_from_sd_summary">Alle Tracks und Einstellungen von der SD-Karte holen.</string>
|
||||
<string name="settings_no_backup_while_recording">Während ein Track aufgezeichnet wird, kann nicht mit der Datensicherung gearbeitet werden.</string>
|
||||
|
||||
<!-- Backups -->
|
||||
<string name="restore_overwrites_warning">Eine Wiederherstellung von der SD-Karte ÜBERSCHREIBT alle vorhandenen Tracks und Einstellungen. Willst Du das wirklich?</string>
|
||||
<string name="select_backup_to_restore">Wähle die wiederherzustellende Datensicherung aus</string>
|
||||
<string name="no_backups">Keine Datensicherungen gefunden.</string>
|
||||
<string name="backup_write_progress_message">Schreibe alle Tracks und Einstellungen auf die SD-Karte. Dies kann eine Weile dauern...</string>
|
||||
<string name="backup_import_progress_message">Lese alle Tracks und Einstellungen von der SD-Karte. Dies kann eine Weile dauern...</string>
|
||||
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">GPS-Satelliten</string>
|
||||
|
||||
<!-- MyMaps -->
|
||||
<string name="select_map_message">Wähle eine Karte von Deinem Konto aus</string>
|
||||
<string name="create_new_map">Neue Karte erstellen…</string>
|
||||
<string name="map_list_title">An Google My Maps senden</string>
|
||||
<string name="maps_list_loading">Karten werden geladen…</string>
|
||||
<string name="maps_list_failed">Deine Karten konnten im Augenblick nicht geladen werden!</string>
|
||||
<string name="open_map">Diese Karte auswählen</string>
|
||||
<string name="edit_map_details">Kartendetails bearbeiten</string>
|
||||
<string name="share_map">Karte mit Freunden teilen</string>
|
||||
<string name="share_track">Track mit Freunden teilen</string>
|
||||
<string name="delete_map">Karte vom Konto löschen</string>
|
||||
<string name="map_title">Kartentitel</string>
|
||||
<string name="map_description">Trackbeschreibung</string>
|
||||
<string name="map_searchable_edit">Diese Karte und ihren Inhalt in Suchergebnissen sichtbar machen.</string>
|
||||
<string name="map_not_searchable_edit">Diese Karte und ihren Inhalt nicht in Suchergebnissen sichtbar machen.</string>
|
||||
<string name="map_searchable">Diese Karte und ihr Inhalt sind in Suchergebnissen sichtbar.</string>
|
||||
<string name="map_not_searchable">Weder diese Karte noch ihr Inhalt sind in Suchergebnissen sichtbar, allerdings kann sie trotzdem von anderen durch einen direkten Link angezeigt werden.</string>
|
||||
<string name="close">Schließen</string>
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<!-- Error messages -->
|
||||
<string name="error_no_location_manager">Es wurde kein Dienst zur Positionsermittlung auf diesem Gerät gefunden.</string>
|
||||
<string name="error_no_gps_location_provider">Es wurde kein GPS-Empfänger auf diesem Gerät gefunden.</string>
|
||||
<string name="error_sending_to_fusiontables">Beim Senden des Tracks an Google Fusion Tables ist ein Fehler aufgetreten. Bitte versuche es später noch einmal.</string>
|
||||
<string name="error_sending_to_docs">Beim Senden des Tracks an Google Docs ist ein Fehler aufgetreten. Bitte versuche es später noch einmal.</string>
|
||||
<string name="error_unable_to_start_recording">Start der Aufzeichnung fehlgeschlagen. Bitte versuche es noch einmal.</string>
|
||||
<string name="error_unable_to_stop_recording">Anhalten unmöglich. Bitte versuche es noch einmal.</string>
|
||||
<string name="error_unable_to_read_file">Konnte Datei nicht lesen.</string>
|
||||
<string name="error_invalid_gpx_format">Datei ist nicht im GPX-Format.</string>
|
||||
<string name="error_generic">Ein Fehler ist aufgetreten. Bitte versuche es noch einmal.</string>
|
||||
<string name="error_track_does_not_exist">Track existiert nicht.</string>
|
||||
<string name="error_out_of_memory">Die Datei ist zu groß (maximal 500 KB). Operation abgebrochen.</string>
|
||||
<string name="error_unable_to_insert_marker">Der Wegpunkt kann nicht einfügt werden. Keine Positionsbestimmung möglich.</string>
|
||||
|
||||
<!-- Status messages -->
|
||||
<string name="wait_for_fix">Warte auf ein besseres GPS-Signal. Bitte stelle sicher, dass der Himmel sichtbar ist.</string>
|
||||
<string name="status_now_recording">Ein neuer Track wird aufgezeichnet.</string>
|
||||
<string name="status_stopped_recording">Die Aufzeichnung wurde angehalten.</string>
|
||||
<string name="status_new_fusiontable_has_been_created">Eine neue Tabelle wurde auf google.com/fusiontables erstellt.</string>
|
||||
<string name="status_tracks_have_been_uploaded">Dein Track wurde auf maps.google.com gespeichert.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Dein Track wurde in einer neuen Tabelle bei docs.google.com gespeichert.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Dein Track wurde an docs.google.com gesendet.</string>
|
||||
<string name="status_enable_gps">Hier berühren, um zu den Sicherheits- und Positionseinstellungen zu gelangen. Aktiviere \'GPS-Satelliten\'. Drücke dann den Zurück-Knopf, um hierher zurückzukehren.</string>
|
||||
<string name="status_approximate_location">Anmerkung: Eine ungefähre Position wird angezeigt, bestimmt mittels Mobilfunkstationen und WiFi-Netzwerken.</string>
|
||||
<string name="status_no_location">Die Position kann derzeit nicht bestimmt werden. Aktiviere mehr Positionsquellen (siehe Sicherheits- und Positionseinstellungen) und stelle sicher, dass der Himmel sichtbar ist.</string>
|
||||
<string name="status_waypoint_inserted">Ein Wegpunkt wurde eingefügt.</string>
|
||||
<string name="status_statistics_inserted">Ein Statistikwegpunkt wurde eingefügt.</string>
|
||||
|
||||
<!-- Progress dialog -->
|
||||
<string name="progress_title">Bitte warten…</string>
|
||||
<string name="progress_message">Dein Track wird an Google gesendet...</string>
|
||||
<string name="progress_message_reading_track">Lese Track...</string>
|
||||
<string name="progress_message_preparing_track">Bereite Track vor...</string>
|
||||
<string name="progress_message_authenticating_fusiontables">Anmeldung bei Google Fusion Tables...</string>
|
||||
<string name="progress_message_authenticating_docs">Anmeldung bei Google Docs...</string>
|
||||
<string name="progress_message_sending_docs">Track wird an Google Docs gesendet...</string>
|
||||
<string name="progress_message_sending_fusiontables">Track wird an Google Fusion Tables gesendet...</string>
|
||||
<string name="progress_message_creating_fusiontable">Neue \'Fusion Tabelle\' wird erstellt...</string>
|
||||
<string name="import_progress_message">Importiere GPS-Track. Das kann ein wenig dauern.</string>
|
||||
<string name="write_progress_message">Schreibe Datei auf SD-Karte. Das kann ein wenig dauern.</string>
|
||||
<string name="progress_message_updating_track">Aktualisiere Trackstatistik...</string>
|
||||
|
||||
<!-- Used by DeleteAllDataHandler -->
|
||||
<string name="are_you_sure_question">Bist Du Dir wirklich sicher?</string>
|
||||
<string name="all_data_will_be_permanently_deleted">Alle Tracks und Wegpunkte werden endgültig von diesem Gerät gelöscht.</string>
|
||||
<string name="track_will_be_permanently_deleted">Dieser Track und seine Wegpunkte werden endgültig von diesem Gerät gelöscht.</string>
|
||||
<string name="marker_will_be_permanently_deleted">Dieser Wegpunkt wird endgültig von diesem Gerät gelöscht.</string>
|
||||
|
||||
<!-- Text and links when uploading to Google Fusion Tables -->
|
||||
<string name="new_map_description">Erstellt von My Tracks für Android.</string>
|
||||
<string name="my_map_link">Erstellt von <a href=\'http://mytracks.appspot.com\'>My Tracks</a> für Android.</string>
|
||||
<string name="new_map_title">Neue Karte</string>
|
||||
|
||||
<!-- Formatting of track and marker descriptions -->
|
||||
<string name="points">Punkte</string>
|
||||
<string name="recorded_date">Aufgezeichnet am</string>
|
||||
<string name="category">Aktivität</string>
|
||||
<string name="waypoint_category">Art des Wegpunktes</string>
|
||||
<string name="part">(Teil %d)</string>
|
||||
|
||||
<!-- Used by the track recording service -->
|
||||
<string name="recording_your_track">Dein Track wird aufgezeichnet…</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="no">Nein</string>
|
||||
<string name="yes">Ja</string>
|
||||
<string name="error">Fehler</string>
|
||||
<string name="success">Erfolgreich</string>
|
||||
<string name="waypoint">Wegpunkt</string>
|
||||
<string name="cancel">Abbrechen</string>
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<string name="save">Sichern</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">Ich möchte gerne eine Karte mit Dir teilen.</string>
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Beginn)</string>
|
||||
<string name="end">(Ende)</string>
|
||||
|
||||
<!-- TrackList -->
|
||||
<string name="tracklist_show_track">Anzeigen</string>
|
||||
<string name="tracklist_send_to_mymaps">An eine Google MyMaps-Karte senden</string>
|
||||
<string name="tracklist_send_to_new_mymap">An eine neue Google MyMaps-Karte senden</string>
|
||||
<string name="tracklist_send_to_docs">An Google Docs senden</string>
|
||||
<string name="tracklist_send_to_google">An Google senden…</string>
|
||||
<string name="tracklist_delete_track">Vom Gerät löschen…</string>
|
||||
<string name="tracklist_edit_track">Bearbeiten</string>
|
||||
<string name="tracklist_write_to_sd">Auf SD-Karte speichern…</string>
|
||||
<string name="tracklist_save_as_gpx">GPX-Datei exportieren</string>
|
||||
<string name="tracklist_save_as_kml">KML-Datei exportieren</string>
|
||||
<string name="tracklist_save_as_csv">CSV-Datei exportieren</string>
|
||||
<string name="tracklist_save_as_tcx">TCX-Datei exportieren</string>
|
||||
<string name="tracklist_btn_send_all_to_mymaps">An Google MyMaps senden</string>
|
||||
<string name="tracklist_btn_delete_all">Alle Tracks löschen</string>
|
||||
<!-- Export All -->
|
||||
<string name="tracklist_btn_export_all">Alle Tracks exportieren</string>
|
||||
<string name="export_done">Exportieren beendet</string>
|
||||
<string name="tracklist_pick_one">Einen Track auswählen. Lange berühren für mehr Optionen.</string>
|
||||
<string name="tracklist_all_tracks">Alle Tracks</string>
|
||||
<string name="tracklist_this_track">Dieser Track</string>
|
||||
<string name="tracklist_share_track">Mit Freunden teilen…</string>
|
||||
<string name="tracklist_share_linkfusion">Ein Link zur Google Fusion Tables-Karte</string>
|
||||
<string name="tracklist_share_gpx_file">Eine GPX-Datei</string>
|
||||
<string name="tracklist_share_kml_file">Eine KML-Datei</string>
|
||||
<string name="tracklist_share_csv_file">Eine CSV-Datei</string>
|
||||
<string name="tracklist_share_tcx_file">Eine TCX-Datei</string>
|
||||
<string name="tracklist_clear_map">Karte zurücksetzen.</string>
|
||||
|
||||
<!-- Waypoints list -->
|
||||
<string name="waypointslist_btn_delete_all">Alle Wegpunkte löschen</string>
|
||||
<string name="waypointslist_this_waypoint">Dieser Wegpunkt</string>
|
||||
<string name="waypointslist_delete_waypoint">Löschen</string>
|
||||
<string name="waypointslist_edit_waypoint">Details bearbeiten</string>
|
||||
<string name="waypointslist_show_waypoint">Auf der Karte zeigen</string>
|
||||
<string name="waypointslist_btn_insert_waypoint">Wegpunkt einfügen</string>
|
||||
<string name="waypointslist_btn_insert_statistics">Statistik einfügen</string>
|
||||
<string name="waypointslist_pick_one">Wegpunkt auswählen. Lange berühren für mehr Optionen.</string>
|
||||
|
||||
<!-- MyTracks Details -->
|
||||
<string name="trackdetails_title">Trackdetails</string>
|
||||
<string name="trackdetails_name">Name</string>
|
||||
<string name="trackdetails_description">Beschreibung</string>
|
||||
<string name="trackdetails_category">Aktivität (z. B. Wandern, Fahrradfahren, Laufen, Klettern…)</string>
|
||||
|
||||
<!-- MyTracks Waypoint Details -->
|
||||
<string name="waypointdetails_title">Wegpunktdetails</string>
|
||||
<string name="waypointdetails_name">Wegpunktname</string>
|
||||
<string name="waypointdetails_description">Wegpunktbeschreibung</string>
|
||||
<string name="waypointdetails_category">Art des Wegpunktes (z. B. Gebäude, Schlagloch, Sehenswürdigkeit…)</string>
|
||||
|
||||
<!-- MyTracks Settings -->
|
||||
<string name="mytracks_settings">Einstellungen für My Tracks</string>
|
||||
<string name="settings_display">Anzeige</string>
|
||||
<string name="settings_recording">Aufzeichnung</string>
|
||||
<string name="settings_sharing">Mit Freunden teilen</string>
|
||||
|
||||
<string name="settings_not_available_summary">In dieser Android-Version nicht verfügbar</string>
|
||||
|
||||
<string name="settings_announcement_frequency">Dauer zwischen Ansagen</string>
|
||||
<string name="settings_announcement_frequency_summary">Kürzeste Zeit zwischen zwei Ansagen</string>
|
||||
|
||||
<string name="settings_min_recording_distance">Kleinste Entfernung zwischen Punkten</string>
|
||||
<string name="settings_min_recording_distance_summary">Kleinste Entfernung zwischen zwei Positionsaufzeichnungen</string>
|
||||
|
||||
<string name="settings_max_recording_distance">Neues Segment, wenn Punkte weiter entfernt als</string>
|
||||
<string name="settings_max_recording_distance_summary">Der Track wird in zwei geteilt, wenn zwei folgende Punkte weiter entfernt sind als hier angegeben.</string>
|
||||
|
||||
<string name="settings_min_recording_interval">Aufzeichnungsfrequenz</string>
|
||||
<string name="settings_min_recording_interval_summary">Kürzeste Zeit zwischen zwei Positionsaufzeichnungen</string>
|
||||
|
||||
<string name="settings_min_required_accuracy">Aufzeichnung pausieren, wenn Genauigkeit kleiner als</string>
|
||||
<string name="settings_min_required_accuracy_summary">Die Aufzeichnung wird kurzfristig angehalten, wenn Deine Position nicht genauer als hier angegeben ermittelt werden kann.</string>
|
||||
|
||||
<string name="settings_metric_units">Entfernungen in metrischen Einheiten anzeigen</string>
|
||||
<string name="settings_metric_units_summary_on">Alle Entfernungen werden in metrischen Einheiten angezeigt.</string>
|
||||
<string name="settings_metric_units_summary_off">Alle Entfernungen werden in imperialen Längeneinheiten angezeigt.</string>
|
||||
|
||||
<string name="settings_map_public">Karten auf maps.google.com veröffentlichen</string>
|
||||
<string name="settings_map_public_summary_on">Hochgeladene Karten auf maps.google.com sind für alle sichtbar.</string>
|
||||
<string name="settings_map_public_summary_off">Hochgeladene Karten auf maps.google.com werden nicht öffentlich sichtbar aufgelistet.</string>
|
||||
|
||||
<string name="settings_report_speed_or_pace">Geschwindigkeitsanzeige</string>
|
||||
<string name="settings_report_speed_or_pace_summary_on">Geschwindigkeit wird angezeigt.</string>
|
||||
<string name="settings_report_speed_or_pace_summary_off">Pace wird angezeigt.</string>
|
||||
|
||||
<string name="settings_share_url_only">Nur URL als Einladung zum Teilen senden</string>
|
||||
<string name="settings_share_url_only_summary_on">Beim Teilen mit Freunden wird nur die Karten-URL gesendet.</string>
|
||||
<string name="settings_share_url_only_summary_off">Beim Teilen mit Freunden wird der GPS-Track selbst gesendet.</string>
|
||||
|
||||
<string name="settings_split_frequency">Zerteilungsfrequenz</string>
|
||||
<string name="settings_split_frequency_summary">Längste Dauer oder Entfernung, nach der automatisch ein Marker eingefügt wird.</string>
|
||||
|
||||
<string name="settings_auto_resume_track_timeout">Zeitbegrenzung der Auto-Wiederaufnahme</string>
|
||||
<string name="settings_auto_resume_track_timeout_summary">Maximale Zeit in Minuten, nach der eine abgebrochene Aufzeichnung nach einem Telefonneustart wiederaufgenommen wird.</string>
|
||||
|
||||
<string name="settings_timestamp_track_name">Zeitstempel als Trackname</string>
|
||||
<string name="settings_timestamp_track_name_summary_on">Der Trackname wird aus Datum und Zeit zum Aufzeichnungsbeginn zusammengesetzt.</string>
|
||||
<string name="settings_timestamp_track_name_summary_off">Tracknamen erhalten eine fortlaufende Nummer.</string>
|
||||
|
||||
<string name="pace_label">Pace</string>
|
||||
|
||||
<string name="eclair_required">Wird nur unter Android 2.0 oder höher unterstützt</string>
|
||||
<string name="settings_sensor_type">Sensortyp</string>
|
||||
<string name="settings_sensor_type_summary">Der Typ des zu verbindenden Sensors</string>
|
||||
<string name="settings_sensor_id">Sensor-Id</string>
|
||||
<string name="settings_sensor_id_summary">Die zum Sensor zu sendende Id</string>
|
||||
<string name="sensor_id_malformed">Die Id muss eine Ganzzahl sein.</string>
|
||||
<string name="bluetooth_sensor_title">Bluetooth-Sensor</string>
|
||||
<string name="bluetooth_sensor_summary">Der zu verbindende Bluetoothsensor</string>
|
||||
<string name="settings_modular_stats_display_title">Art der Datenanzeige</string>
|
||||
|
||||
<string name="settings_backup">Datensicherung (BETA)</string>
|
||||
<string name="settings_sd_backup">SD-Karten-Sicherung</string>
|
||||
<string name="settings_backup_to_sd">Jetzt sichern</string>
|
||||
<string name="settings_backup_to_sd_summary">Eine Kopie aller Deiner Tracks und Einstellungen auf die SD-Karte schreiben</string>
|
||||
<string name="settings_restore_from_sd">Jetzt wiederherstellen</string>
|
||||
<string name="settings_restore_from_sd_summary">Alle Tracks und Einstellungen von der SD-Karte holen.</string>
|
||||
<string name="settings_no_backup_while_recording">Während ein Track aufgezeichnet wird, kann nicht mit der Datensicherung gearbeitet werden.</string>
|
||||
|
||||
<!-- Backups -->
|
||||
<string name="restore_overwrites_warning">Eine Wiederherstellung von der SD-Karte ÜBERSCHREIBT alle vorhandenen Tracks und Einstellungen. Willst Du das wirklich?</string>
|
||||
<string name="select_backup_to_restore">Wähle die wiederherzustellende Datensicherung aus</string>
|
||||
<string name="no_backups">Keine Datensicherungen gefunden.</string>
|
||||
<string name="backup_write_progress_message">Schreibe alle Tracks und Einstellungen auf die SD-Karte. Dies kann eine Weile dauern...</string>
|
||||
<string name="backup_import_progress_message">Lese alle Tracks und Einstellungen von der SD-Karte. Dies kann eine Weile dauern...</string>
|
||||
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">GPS-Satelliten</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map">Karte mit Freunden teilen</string>
|
||||
<string name="share_map_subject">Ich möchte gerne eine Karte mit Dir teilen.</string>
|
||||
<string name="share_map_body_format">Vielleicht interessierst Du Dich für diese Karte:
|
||||
|
||||
%s</string>
|
||||
<string name="send_track_subject">Ich möchte einen GPS-Track mit Dir teilen</string>
|
||||
|
||||
%s</string>
|
||||
<string name="send_track_subject">Ich möchte einen GPS-Track mit Dir teilen</string>
|
||||
<string name="send_track_body_format">Vielleicht interessierst Du Dich für diese Track:
|
||||
|
||||
|
||||
%s.</string>
|
||||
|
||||
<string name="share_track">Track mit Freunden teilen</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Öffentlich</string>
|
||||
<string name="unlisted_map">Nicht in Suchergebnissen enthalten.</string>
|
||||
<string name="mapslist_no_maps_available">Du hast noch keine Karten.</string>
|
||||
|
||||
<!-- Statistics View -->
|
||||
<!-- Average Moving -->
|
||||
<string name="average_moving_speed_label">Durchschnitt in Bew.</string>
|
||||
<string name="average_moving_pace_label">Pace in Bew.</string>
|
||||
|
||||
<!-- Average -->
|
||||
<string name="average_speed_label">Durchschnittsgeschw.</string>
|
||||
<string name="average_pace_label">Durchschnitts-Pace</string>
|
||||
|
||||
<string name="elevation_chart_name">Höhenprofil</string>
|
||||
<string name="elevation_gain_label">Höhengewinn</string>
|
||||
<string name="elevation_label">Höhe</string>
|
||||
<string name="latitude_label">Breitengrad</string>
|
||||
<string name="longitude_label">Längengrad</string>
|
||||
|
||||
<!-- Min -->
|
||||
<string name="min_elevation_label">Niedrigste Höhenlage</string>
|
||||
<string name="min_grade_label">Minimale Steigung</string>
|
||||
<string name="min_pace_label">Minimaler Pace</string>
|
||||
|
||||
<!-- Max -->
|
||||
<string name="max_elevation_label">Höchste Höhenlage</string>
|
||||
<string name="max_grade_label">Maximale Steigung</string>
|
||||
<string name="max_speed_label">Max. Geschwindigkeit</string>
|
||||
|
||||
<string name="loading_track">Lade Track…</string>
|
||||
|
||||
<!-- Chart Settings -->
|
||||
<string name="chart_settings">Diagramm-Einstellungen</string>
|
||||
<!-- X Axis -->
|
||||
<string name="x_axis">X-Achse</string>
|
||||
<string name="by_distance">Nach Entfernung</string>
|
||||
<string name="by_time">Nach Zeit</string>
|
||||
|
||||
<!-- Y Axis -->
|
||||
<string name="y_axis">Y-Achse</string>
|
||||
|
||||
<!-- Units -->
|
||||
<!-- Time -->
|
||||
<string name="hour">Stunde</string>
|
||||
<string name="hours">Stunden</string>
|
||||
<string name="hr">Std</string>
|
||||
<string name="minute">Minute</string>
|
||||
<string name="minutes">Minuten</string>
|
||||
<string name="second">Sekunde</string>
|
||||
<string name="seconds">Sekunden</string>
|
||||
|
||||
<!-- Distance -->
|
||||
<string name="feet">Fuß</string>
|
||||
<string name="kilometers_long">Kilometer</string>
|
||||
<string name="miles_long">Meilen</string>
|
||||
|
||||
<!-- Speed and Pace -->
|
||||
<string name="kilometer_per_hour_long">Kilometer pro Stunde</string>
|
||||
<string name="per_kilometer">pro Kilometer</string>
|
||||
<string name="mile_per_hour_long">Meilen pro Stunde</string>
|
||||
<string name="per_mile">pro Meile</string>
|
||||
|
||||
<!-- Stats Labels -->
|
||||
<string name="moving_time_label">Zeit in Bewegung</string>
|
||||
<string name="total_time_label">Gesamtzeit</string>
|
||||
<string name="time_label">Zeit</string>
|
||||
<string name="sd_unavail">SD-Karte nicht verfügbar</string>
|
||||
<string name="speed">Geschwindigkeit</string>
|
||||
<string name="start_trip">Du musst erst eine Aufzeichnung beginnen.</string>
|
||||
<string name="statistics">Statistik</string>
|
||||
<string name="total_distance_label">Gesamtentfernung</string>
|
||||
<string name="distance_label">Entfernung</string>
|
||||
<string name="power">Energie</string>
|
||||
<string name="cadence">Kadenz</string>
|
||||
<string name="heart_rate">Herzfrequenz</string>
|
||||
<string name="max_heart_rate">Höchste Herzfrequenz</string>
|
||||
<string name="waiting">Bitte warten…</string>
|
||||
|
||||
<!-- End User License Agreement -->
|
||||
<string name="eula_title">Lizenzvereinbarung für Endverbraucher</string>
|
||||
<string name="accept">Akzeptieren</string>
|
||||
<string name="decline">Ablehnen</string>
|
||||
|
||||
<!-- About -->
|
||||
<string name="about">Über</string>
|
||||
<string name="help">Hilfe</string>
|
||||
<string name="about_email">Unterstützung</string>
|
||||
|
||||
<!-- Send to Google -->
|
||||
<string name="sendtogoogle_title">Senden an…</string>
|
||||
<string name="sendtogoogle_send_now">Jetzt senden</string>
|
||||
<string name="sendtogoogle_create_new_map">Neue Karte erstellen</string>
|
||||
<string name="sendtogoogle_pick_existing_map">Eine Karte auswählen</string>
|
||||
<string name="sendtogoogle_send_stats">Trackstatistik senden</string>
|
||||
<string name="sendtogoogle_send_stats_and_points">Statistik und Punkte senden</string>
|
||||
|
||||
<!-- Getting the account to upload to. -->
|
||||
<string name="no_account_found_title">Kein Konto vorhanden</string>
|
||||
<string name="no_account_found">Kein Konto gefunden, um Tracks zu versenden.</string>
|
||||
<string name="choose_account_title">Wähle ein Konto</string>
|
||||
|
||||
<!-- SD card -->
|
||||
<string name="io_no_external_storage_found">Keine externes Speichermedium verfügbar. Stelle sicher, dass eine SD-Karte vorhanden ist und nicht bereits von einem Computer darauf zugegriffen wird.</string>
|
||||
<string name="io_write_failed">Die Datei kann nicht geschrieben werden.</string>
|
||||
<string name="io_read_failed">Die Datei konnte nicht gelesen werden.</string>
|
||||
<string name="io_create_dir_failed">Das Verzeichnis auf der SD-Karte kann nicht erstellt werden.</string>
|
||||
<string name="io_write_finished">Der Track wurde auf die SD-Karte geschrieben.</string>
|
||||
<string name="io_read_finished">Lesen von der SD-Karte beendet.</string>
|
||||
|
||||
<!-- VersionChecker -->
|
||||
<string name="version_check_title">Suche nach neuer Version</string>
|
||||
<string name="version_check_upgrade_button">Jetzt aktualisieren</string>
|
||||
<string name="version_check_continue_button">Fortfahren, ohne zu aktualisieren</string>
|
||||
<string name="upgrade_required">Deine Version von %1$s funktioniert möglicherweise nicht mehr richtig und wird nicht mehr unterstützt. Bitte aktualisiere %1$s.</string>
|
||||
<string name="upgrade_recommended">Deine Version von %1$s funktioniert möglicherweise nicht mehr richtig. Du kannst sie jetzt aktualisieren, oder auf sie auf eigene Gefahr weiterverwenden. Einige Funktionen wie Datenaustausch mit Google-Diensten werden möglicherweise nicht mehr funktionieren, solange Du die Software nicht aktualisierst.</string>
|
||||
<string name="upgrade_available">Eine neue Version von %1$s ist jetzt verfügbar. Du kannst Deine installierte Version ohne Probleme weiterverwenden, aber um die neuesten Funktionen in Anspruch nehmen zu können, musst Du die Software aktualisieren.</string>
|
||||
|
||||
<!-- Welcome -->
|
||||
<string name="welcome">Willkommen</string>
|
||||
<string name="welcome_about">Über My Tracks</string>
|
||||
|
||||
<!-- Announcement -->
|
||||
<string name="announce_no_distance">My Tracks keine Entfernung</string>
|
||||
<!-- Statistics View -->
|
||||
<!-- Average Moving -->
|
||||
<string name="average_moving_speed_label">Durchschnitt in Bew.</string>
|
||||
<string name="average_moving_pace_label">Pace in Bew.</string>
|
||||
|
||||
<!-- Average -->
|
||||
<string name="average_speed_label">Durchschnittsgeschw.</string>
|
||||
<string name="average_pace_label">Durchschnitts-Pace</string>
|
||||
|
||||
<string name="elevation_chart_name">Höhenprofil</string>
|
||||
<string name="elevation_gain_label">Höhengewinn</string>
|
||||
<string name="elevation_label">Höhe</string>
|
||||
<string name="latitude_label">Breitengrad</string>
|
||||
<string name="longitude_label">Längengrad</string>
|
||||
|
||||
<!-- Min -->
|
||||
<string name="min_elevation_label">Niedrigste Höhenlage</string>
|
||||
<string name="min_grade_label">Minimale Steigung</string>
|
||||
<string name="min_pace_label">Minimaler Pace</string>
|
||||
|
||||
<!-- Max -->
|
||||
<string name="max_elevation_label">Höchste Höhenlage</string>
|
||||
<string name="max_grade_label">Maximale Steigung</string>
|
||||
<string name="max_speed_label">Max. Geschwindigkeit</string>
|
||||
|
||||
<string name="loading_track">Lade Track…</string>
|
||||
|
||||
<!-- Chart Settings -->
|
||||
<string name="chart_settings">Diagramm-Einstellungen</string>
|
||||
<!-- X Axis -->
|
||||
<string name="x_axis">X-Achse</string>
|
||||
<string name="by_distance">Nach Entfernung</string>
|
||||
<string name="by_time">Nach Zeit</string>
|
||||
|
||||
<!-- Y Axis -->
|
||||
<string name="y_axis">Y-Achse</string>
|
||||
|
||||
<!-- Units -->
|
||||
<!-- Time -->
|
||||
<string name="hour">Stunde</string>
|
||||
<string name="hours">Stunden</string>
|
||||
<string name="hr">Std</string>
|
||||
<string name="minute">Minute</string>
|
||||
<string name="minutes">Minuten</string>
|
||||
<string name="second">Sekunde</string>
|
||||
<string name="seconds">Sekunden</string>
|
||||
|
||||
<!-- Distance -->
|
||||
<string name="feet">Fuß</string>
|
||||
<string name="kilometers_long">Kilometer</string>
|
||||
<string name="miles_long">Meilen</string>
|
||||
|
||||
<!-- Speed and Pace -->
|
||||
<string name="kilometer_per_hour_long">Kilometer pro Stunde</string>
|
||||
<string name="per_kilometer">pro Kilometer</string>
|
||||
<string name="mile_per_hour_long">Meilen pro Stunde</string>
|
||||
<string name="per_mile">pro Meile</string>
|
||||
|
||||
<!-- Stats Labels -->
|
||||
<string name="moving_time_label">Zeit in Bewegung</string>
|
||||
<string name="total_time_label">Gesamtzeit</string>
|
||||
<string name="time_label">Zeit</string>
|
||||
<string name="sd_unavail">SD-Karte nicht verfügbar</string>
|
||||
<string name="speed">Geschwindigkeit</string>
|
||||
<string name="start_trip">Du musst erst eine Aufzeichnung beginnen.</string>
|
||||
<string name="statistics">Statistik</string>
|
||||
<string name="total_distance_label">Gesamtentfernung</string>
|
||||
<string name="distance_label">Entfernung</string>
|
||||
<string name="power">Energie</string>
|
||||
<string name="cadence">Kadenz</string>
|
||||
<string name="heart_rate">Herzfrequenz</string>
|
||||
<string name="max_heart_rate">Höchste Herzfrequenz</string>
|
||||
<string name="waiting">Bitte warten…</string>
|
||||
|
||||
<!-- End User License Agreement -->
|
||||
<string name="eula_title">Lizenzvereinbarung für Endverbraucher</string>
|
||||
<string name="accept">Akzeptieren</string>
|
||||
<string name="decline">Ablehnen</string>
|
||||
|
||||
<!-- About -->
|
||||
<string name="about">Über</string>
|
||||
<string name="help">Hilfe</string>
|
||||
<string name="about_email">Unterstützung</string>
|
||||
|
||||
<!-- Send to Google -->
|
||||
<string name="sendtogoogle_title">Senden an…</string>
|
||||
<string name="sendtogoogle_send_now">Jetzt senden</string>
|
||||
<string name="sendtogoogle_send_stats">Trackstatistik senden</string>
|
||||
<string name="sendtogoogle_send_stats_and_points">Statistik und Punkte senden</string>
|
||||
|
||||
<!-- Getting the account to upload to. -->
|
||||
<string name="no_account_found_title">Kein Konto vorhanden</string>
|
||||
<string name="no_account_found">Kein Konto gefunden, um Tracks zu versenden.</string>
|
||||
<string name="choose_account_title">Wähle ein Konto</string>
|
||||
|
||||
<!-- SD card -->
|
||||
<string name="io_no_external_storage_found">Keine externes Speichermedium verfügbar. Stelle sicher, dass eine SD-Karte vorhanden ist und nicht bereits von einem Computer darauf zugegriffen wird.</string>
|
||||
<string name="io_write_failed">Die Datei kann nicht geschrieben werden.</string>
|
||||
<string name="io_read_failed">Die Datei konnte nicht gelesen werden.</string>
|
||||
<string name="io_create_dir_failed">Das Verzeichnis auf der SD-Karte kann nicht erstellt werden.</string>
|
||||
<string name="io_write_finished">Der Track wurde auf die SD-Karte geschrieben.</string>
|
||||
<string name="io_read_finished">Lesen von der SD-Karte beendet.</string>
|
||||
|
||||
<!-- VersionChecker -->
|
||||
<string name="version_check_title">Suche nach neuer Version</string>
|
||||
<string name="version_check_upgrade_button">Jetzt aktualisieren</string>
|
||||
<string name="version_check_continue_button">Fortfahren, ohne zu aktualisieren</string>
|
||||
<string name="upgrade_required">Deine Version von %1$s funktioniert möglicherweise nicht mehr richtig und wird nicht mehr unterstützt. Bitte aktualisiere %1$s.</string>
|
||||
<string name="upgrade_recommended">Deine Version von %1$s funktioniert möglicherweise nicht mehr richtig. Du kannst sie jetzt aktualisieren, oder auf sie auf eigene Gefahr weiterverwenden. Einige Funktionen wie Datenaustausch mit Google-Diensten werden möglicherweise nicht mehr funktionieren, solange Du die Software nicht aktualisierst.</string>
|
||||
<string name="upgrade_available">Eine neue Version von %1$s ist jetzt verfügbar. Du kannst Deine installierte Version ohne Probleme weiterverwenden, aber um die neuesten Funktionen in Anspruch nehmen zu können, musst Du die Software aktualisieren.</string>
|
||||
|
||||
<!-- Welcome -->
|
||||
<string name="welcome">Willkommen</string>
|
||||
<string name="welcome_about">Über My Tracks</string>
|
||||
|
||||
<!-- Announcement -->
|
||||
<string name="announce_no_distance">My Tracks keine Entfernung</string>
|
||||
<!-- Format:
|
||||
$1 : Distance label
|
||||
$2 : Distance value (5)
|
||||
@@ -400,42 +380,42 @@ limitations under the License.
|
||||
$4 : Time description (10 minutes 5 seconds)
|
||||
$5 : Speed value (5.6) : Pace value (5 minutes 6 seconds)
|
||||
$6 : Speed units (mph) : Pace units (per mile)
|
||||
-->
|
||||
<string name="announce_template">My Tracks %1$s %2$.2f %3$s in %4$s mit %5$s %6$s</string>
|
||||
|
||||
<!-- Check Units -->
|
||||
<string name="check_units_title">Einheitenbenachrichtigung</string>
|
||||
<string name="check_units_message">Voreingestellt sind metrische Einheiten. Willst Du diese Einstellung ändern?</string>
|
||||
|
||||
<!-- Fake preference keys - never saved, but show options in the preferences screen -->
|
||||
<string name="bluetooth_options">Bluetootheinstellungen</string>
|
||||
<string name="bluetooth_pairing">Geräteverbindung</string>
|
||||
<string name="bluetooth_pairing_summary">Öffnet Androids Bluetootheinstellungen, um Geräte zu verbinden.</string>
|
||||
<string name="sensor_options">Sensoreinstellungen</string>
|
||||
|
||||
<!-- Bluetooth Sensor -->
|
||||
<string name="bluetooth_connected">Verbunden mit %s</string>
|
||||
<string name="bluetooth_connecting">Verbinde mit Bluetoothsensor ...</string>
|
||||
<string name="bluetooth_not_connected">Bluetoothsensor nicht verbunden.</string>
|
||||
|
||||
<!-- Sensor State -->
|
||||
<string name="sensor_data_time">Sensor Data Time</string>
|
||||
<string name="power_meter_enabled">Energiemessung aktiviert</string>
|
||||
<string name="heart_rate_monitor_enabled">Herzfrequenzmessung aktiviert</string>
|
||||
<string name="no_sensor_manager">Keine Sensoren verfügbar</string>
|
||||
<string name="enabled">Aktiviert</string>
|
||||
<string name="connecting">Verbinde...</string>
|
||||
<string name="connected">Verbunden</string>
|
||||
<string name="disconnected">Getrennt</string>
|
||||
<string name="no_sensor_service">Kein Sensor-Dienst</string>
|
||||
<string name="searching">Suche...</string>
|
||||
<string name="sending">Sende...</string>
|
||||
|
||||
<!-- Manifest description (not seen inside the app) -->
|
||||
<string name="notification_permission_label">Track-Aufzeichnungs-Benachrichtigungen empfangen</string>
|
||||
<string name="read_tracks_permission_label">Aufgezeichnete Trackdaten lesen</string>
|
||||
<string name="write_tracks_permission_label">Aufgezeichnete Trackdaten schreiben, Starten/Anhalten des Aufzeichnens von Tracks</string>
|
||||
<string name="notification_permission_description">Werde benachrichtigt, wenn die Aufzeichnung eines Tracks beginnt oder anhält.</string>
|
||||
<string name="read_tracks_permission_description">Ermöglicht das Lesen von Trackdaten wie Messpunkten oder Wegpunkten.</string>
|
||||
<string name="write_tracks_permission_description">Ermöglicht das Schreiben von Trackdaten wie beim Starten der Aufzeichnung oder beim Hinzufügen von Wegpunkten.</string>
|
||||
</resources>
|
||||
-->
|
||||
<string name="announce_template">My Tracks %1$s %2$.2f %3$s in %4$s mit %5$s %6$s</string>
|
||||
|
||||
<!-- Check Units -->
|
||||
<string name="check_units_title">Einheitenbenachrichtigung</string>
|
||||
<string name="check_units_message">Voreingestellt sind metrische Einheiten. Willst Du diese Einstellung ändern?</string>
|
||||
|
||||
<!-- Fake preference keys - never saved, but show options in the preferences screen -->
|
||||
<string name="bluetooth_options">Bluetootheinstellungen</string>
|
||||
<string name="bluetooth_pairing">Geräteverbindung</string>
|
||||
<string name="bluetooth_pairing_summary">Öffnet Androids Bluetootheinstellungen, um Geräte zu verbinden.</string>
|
||||
<string name="sensor_options">Sensoreinstellungen</string>
|
||||
|
||||
<!-- Bluetooth Sensor -->
|
||||
<string name="bluetooth_connected">Verbunden mit %s</string>
|
||||
<string name="bluetooth_connecting">Verbinde mit Bluetoothsensor ...</string>
|
||||
<string name="bluetooth_not_connected">Bluetoothsensor nicht verbunden.</string>
|
||||
|
||||
<!-- Sensor State -->
|
||||
<string name="sensor_data_time">Sensor Data Time</string>
|
||||
<string name="power_meter_enabled">Energiemessung aktiviert</string>
|
||||
<string name="heart_rate_monitor_enabled">Herzfrequenzmessung aktiviert</string>
|
||||
<string name="no_sensor_manager">Keine Sensoren verfügbar</string>
|
||||
<string name="enabled">Aktiviert</string>
|
||||
<string name="connecting">Verbinde...</string>
|
||||
<string name="connected">Verbunden</string>
|
||||
<string name="disconnected">Getrennt</string>
|
||||
<string name="no_sensor_service">Kein Sensor-Dienst</string>
|
||||
<string name="searching">Suche...</string>
|
||||
<string name="sending">Sende...</string>
|
||||
|
||||
<!-- Manifest description (not seen inside the app) -->
|
||||
<string name="notification_permission_label">Track-Aufzeichnungs-Benachrichtigungen empfangen</string>
|
||||
<string name="read_tracks_permission_label">Aufgezeichnete Trackdaten lesen</string>
|
||||
<string name="write_tracks_permission_label">Aufgezeichnete Trackdaten schreiben, Starten/Anhalten des Aufzeichnens von Tracks</string>
|
||||
<string name="notification_permission_description">Werde benachrichtigt, wenn die Aufzeichnung eines Tracks beginnt oder anhält.</string>
|
||||
<string name="read_tracks_permission_description">Ermöglicht das Lesen von Trackdaten wie Messpunkten oder Wegpunkten.</string>
|
||||
<string name="write_tracks_permission_description">Ermöglicht das Schreiben von Trackdaten wie beim Starten der Aufzeichnung oder beim Hinzufügen von Wegpunkten.</string>
|
||||
</resources>
|
||||
|
||||
@@ -51,7 +51,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">Su recorrido ha sido enviado a maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Su recorrido ha sido enviado a una nueva hoja de cálculo en docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Su recorrido ha sido enviado a docs.google.com.</string>
|
||||
<string name="status_mymap_info">A veces nuevos recorridos pueden tardar algunos minutos en aparecer.</string>
|
||||
<string name="status_enable_gps">Por favor presione aquí para ir al menú de Seguridad y Ubicación y asegúrese que el GPS esté habilitado. Presione el botón de regreso para regresar.</string>
|
||||
<string name="status_approximate_location">Nota: Una ubicación aproximada es mostrada, basada en informacion de torres de telefonía celular y red WIFI.</string>
|
||||
<string name="status_no_location">La ubicación actual no puede ser determinada. Habilite más fuentes de datos (vea configuraciones de Seguridad y Ubicación) y asegúrese que el cielo sea visible. Inténtelo mas tarde.</string>
|
||||
@@ -98,6 +97,7 @@ limitations under the License.
|
||||
<string name="yes">Sí</string>
|
||||
<string name="success">Éxito</string>
|
||||
<string name="waypoint">Señal de recorrido</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Inicio)</string>
|
||||
@@ -193,30 +193,10 @@ limitations under the License.
|
||||
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">Satélites GPS</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Elija un mapa de su cuenta:</string>
|
||||
<string name="create_new_map">Crear mapa nuevo...</string>
|
||||
<string name="map_list_title">Enviar a Google My Maps</string>
|
||||
<string name="maps_list_loading">Cargando mapas...</string>
|
||||
<string name="maps_list_failed">Sus mapas no pudieron ser cargados en este momento!</string>
|
||||
<string name="open_map">Seleccione este mapa</string>
|
||||
<string name="edit_map_details">Editar detalles del mapa</string>
|
||||
<string name="share_map">Compartir mapa con amigos</string>
|
||||
<string name="share_track">Compartir recorrido con amigos</string>
|
||||
<string name="delete_map">Borrar mapa de su cuenta</string>
|
||||
<string name="map_title">Título del mapa:</string>
|
||||
<string name="map_description">Descripción del mapa:</string>
|
||||
<string name="map_searchable_edit">Permitir que este mapa y su contenido sean mostrados en resultados de búsqueda</string>
|
||||
<string name="map_not_searchable_edit">No permitir que este mapa y su contenido sean mostrados en resultados de búsqueda, pero permitir acceso directo mediante su URL</string>
|
||||
<string name="map_searchable">Este mapa y su contenido serán mostrados en resultados de búsqueda</string>
|
||||
<string name="map_not_searchable">No mostrar ni este mapa ni sus contenidos en resultados de búsqueda, pero permitir acceso directo mediante su URL</string>
|
||||
<string name="close">Cerrar</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="save">Guardar</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map">Compartir mapa con amigos</string>
|
||||
<string name="share_map_subject">Me gustaría compartir un mapa contigo</string>
|
||||
<string name="share_map_body_format">Creo que este mapa pudiera interesarte:
|
||||
|
||||
@@ -225,6 +205,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">Creo que este recorrido pudiera interesarte.
|
||||
|
||||
Para ver este recorrido usa Google Earth o algún otro software de mapeo.</string>
|
||||
<string name="share_track">Compartir recorrido con amigos</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Público</string>
|
||||
@@ -315,8 +296,6 @@ limitations under the License.
|
||||
<!-- Send to Google -->
|
||||
<string name="sendtogoogle_title">Enviar a...</string>
|
||||
<string name="sendtogoogle_send_now">Enviar ahora</string>
|
||||
<string name="sendtogoogle_create_new_map">Crear un nuevo mapa</string>
|
||||
<string name="sendtogoogle_pick_existing_map">Escoja un mapa existente</string>
|
||||
<string name="sendtogoogle_send_stats">Enviar estadísticas de recorrido</string>
|
||||
<string name="sendtogoogle_send_stats_and_points">Enviar estadísticas y puntos</string>
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">Votre parcours a été envoyé à maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Votre parcours a été envoyé dans un tableur à docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Votre parcours a été envoyé à docs.google.com.</string>
|
||||
<string name="status_mymap_info">Votre parcours peut prendre quelques minutes avant d\'être visible.</string>
|
||||
<string name="status_enable_gps">Cliquez ici et allez dans \'Paramètres\', \'Sécurité et Localisation\' et selectionnez \'Activer les satellites GPS\'. Appuyez sur le bouton de retour pour revenir au menu.</string>
|
||||
<string name="status_approximate_location">Note : Votre position est approximative, elle a été déterminée par les antennes de téléphonie ou le réseau WiFi.</string>
|
||||
<string name="status_no_location">Votre position ne peut être déterminée. Vérifiez votre configuration dans \'Paramètres\', \'Sécurité et Localisation\'. Assurez-vous également que le ciel soit bien visible.</string>
|
||||
@@ -100,6 +99,10 @@ limitations under the License.
|
||||
<string name="yes">Oui</string>
|
||||
<string name="error">Erreur</string>
|
||||
<string name="waypoint">Point de passage</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="close">Fermer</string>
|
||||
<string name="edit">Modifier</string>
|
||||
<string name="save">Enregistrer</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Début)</string>
|
||||
@@ -194,29 +197,8 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">Satellites GPS</string>
|
||||
|
||||
<!-- MyMaps -->
|
||||
<string name="select_map_message">Choisissez une carte de votre compte :</string>
|
||||
<string name="create_new_map">Création d\'une nouvelle carte en cours...</string>
|
||||
<string name="map_list_title">Envoyer à Google Mes cartes</string>
|
||||
<string name="maps_list_loading">Chargements de vos cartes...</string>
|
||||
<string name="maps_list_failed">Vos cartes n\'ont pu être chargées</string>
|
||||
<string name="open_map">Sélectionnez la carte</string>
|
||||
<string name="edit_map_details">Consulter les détails de la carte</string>
|
||||
<string name="share_map">Partager.</string>
|
||||
<string name="share_track">Partager des parcours.</string>
|
||||
<string name="delete_map">Supprimer la carte de votre compte.</string>
|
||||
<string name="map_title">Titre de la carte :</string>
|
||||
<string name="map_description">Description de la carte :</string>
|
||||
<string name="map_searchable_edit">Rendre cette carte et son contenu visibles dans les résultats de recherche.</string>
|
||||
<string name="map_not_searchable_edit">Ne pas inclure cette carte et sont contenu dans les résultats de recherche. Ils seront cependant consultables par d\'autres utilisateurs en utilisant l\'adresse URL exacte</string>
|
||||
<string name="map_searchable">Cette carte et son contenu seront visibles dans les résultats de recherche</string>
|
||||
<string name="map_not_searchable">Ni cette carte, ni son contenu seront visible dans les résultats de recherche, mais ils seront consultables en utilisant l\'adresse URL exacte</string>
|
||||
<string name="close">Fermer</string>
|
||||
<string name="edit">Modifier</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="save">Enregistrer</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map">Partager.</string>
|
||||
<string name="share_map_subject">Je voudrais partager ma carte avec vous</string>
|
||||
<string name="share_map_body_format">Je pense que cette carte vous intéressera :
|
||||
|
||||
@@ -225,6 +207,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">Je pense que ce parcours vous intéressera.
|
||||
|
||||
Pour voir ce parcours, utilisez Google Earth ou tout autre logiciel de cartographie.</string>
|
||||
<string name="share_track">Partager des parcours.</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="unlisted_map">Carte non listée</string>
|
||||
|
||||
@@ -54,7 +54,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">Il tuo percorso è stato inviato a maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Il tuo percorso è stato inviato ad un nuovo foglio di calcolo su docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Il tuo percorso è stato inviato a docs.google.com.</string>
|
||||
<string name="status_mymap_info">Potrebbe essere necessario aspettare qualche minuto prima che i nuovi percorsi siano visibili.</string>
|
||||
<string name="status_enable_gps">Premi qui per andare a «Impostazioni di posizione e protezione» e attiva «Utilizza satelliti GPS». Poi premi il tasto di ritorno per tornare qui.</string>
|
||||
<string name="status_approximate_location">Nota: la posizione indicata è un\'approssimazione determinata in base alle antenne cellulari e reti WIFI vicine.</string>
|
||||
<string name="status_no_location">La posizione non può essere determinata. Attiva più fonti di posizione (vedi «Impostazioni di posizione e protezione») e eccertati che il cielo sia visibile. Riprova più tardi.</string>
|
||||
@@ -104,6 +103,10 @@ limitations under the License.
|
||||
<string name="error">Errore</string>
|
||||
<string name="success">Successo</string>
|
||||
<string name="waypoint">Punto di passaggio</string>
|
||||
<string name="cancel">Annulla</string>
|
||||
<string name="close">Chiudi</string>
|
||||
<string name="edit">Modifica</string>
|
||||
<string name="save">Salva</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Inizio)</string>
|
||||
@@ -200,28 +203,6 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">Satelliti GPS</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Scegli una mappa dal tuo account:</string>
|
||||
<string name="create_new_map">Crea una nuova mappa...</string>
|
||||
<string name="map_list_title">Invia a «Le mie mappe» di Google</string>
|
||||
<string name="maps_list_loading">Caricamento mappe in corso...</string>
|
||||
<string name="maps_list_failed">Le tue mappe non possono momentaneamente essere caricate!</string>
|
||||
<string name="open_map">Scegli questa mappa</string>
|
||||
<string name="edit_map_details">Modifica dettagli della mappa</string>
|
||||
<string name="share_map">Condividi la mappa con amici</string>
|
||||
<string name="share_track">Condividi il percorso con amici</string>
|
||||
<string name="delete_map">Elimina la mappa dal tuo account</string>
|
||||
<string name="map_title">Titolo della mappa:</string>
|
||||
<string name="map_description">Descrizione della mappa:</string>
|
||||
<string name="map_searchable_edit">Rendi questa mappa e il suo contenuto visibili fra i risultati di ricerche</string>
|
||||
<string name="map_not_searchable_edit">Rendi questa mappa e il suo contenuto invisibili fra i risultati di ricerche. Resteranno comunque visibili ad altri utilizzando l\'indirizzo URL esatto</string>
|
||||
<string name="map_searchable">Questa mappa e il suo contenuto saranno visibili fra i risultati di ricerche</string>
|
||||
<string name="map_not_searchable">Né questa mappa né il suo contenuto saranno visibili in risultati di ricerche, ma saranno comunque visibili ad altri tramite un link diretto.</string>
|
||||
<string name="close">Chiudi</string>
|
||||
<string name="edit">Modifica</string>
|
||||
<string name="cancel">Annulla</string>
|
||||
<string name="save">Salva</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">Vorrei condividere una mappa con te</string>
|
||||
<string name="share_map_body_format">Credo che questa mappa potrebbe interessarti:
|
||||
@@ -231,6 +212,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">Credo che questo percorso potrebbe interessarti.
|
||||
|
||||
Per vedere questo percorso, usa Google Earth o un altro software di cartografia.</string>
|
||||
<string name="share_track">Condividi il percorso con amici</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Pubblica</string>
|
||||
|
||||
@@ -52,7 +52,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">トラックを maps.google.com に送信しました.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">トラックを docs.google.com の新しいスプレッドシートに送信しました.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">トラックを docs.google.com に送信しました.</string>
|
||||
<string name="status_mymap_info">新しいトラックが見えるようになるまで数分程度かかります.</string>
|
||||
<string name="status_enable_gps">ここをタップして,セキュリティ &位置情報 を開いて,「GPS機能をONにする」にチェックして下さい.バックボタンで戻ってください.</string>
|
||||
<string name="status_approximate_location">ご注意: 携帯電話中継局とWi-Fiネットワークから大まかな位置を特定します.</string>
|
||||
<string name="status_no_location">現在位置決定ができません.位置取得源を増やし(セキュリティ&位置情報をご覧ください),空が見えるようにして下さい.もう一度試して下さい.</string>
|
||||
@@ -102,6 +101,10 @@ limitations under the License.
|
||||
<string name="error">エラー</string>
|
||||
<string name="success">成功</string>
|
||||
<string name="waypoint">ウェイポイント</string>
|
||||
<string name="cancel">キャンセル</string>
|
||||
<string name="close">閉じる</string>
|
||||
<string name="edit">編集</string>
|
||||
<string name="save">保存</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(開始)</string>
|
||||
@@ -201,30 +204,6 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">GPS衛星</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">ご使用のアカウントからのマップ選択:</string>
|
||||
<string name="create_new_map">新規マップ...</string>
|
||||
<string name="map_list_title">Googleマイマップへの送信</string>
|
||||
<string name="maps_list_loading">マップの読み込み中...</string>
|
||||
<string name="maps_list_failed">マップを読み込めませんでした!</string>
|
||||
<string name="open_map">このマップを選択</string>
|
||||
<string name="edit_map_details">マップ詳細の編集</string>
|
||||
<string name="share_map">マップの共有</string>
|
||||
<string name="share_track">トラックの共有</string>
|
||||
<string name="delete_map">アカウントからマップの削除</string>
|
||||
<string name="map_title">マップタイトル:</string>
|
||||
<string name="map_description">マップ説明:</string>
|
||||
|
||||
|
||||
<string name="map_searchable_edit">このマップとコンテンツを検索結果に表示させる</string>
|
||||
<string name="map_not_searchable_edit">このマップとコンテンツはどちらも検索結果に表示させない(直接URLで他の方が見ることは可能)</string>
|
||||
<string name="map_searchable">このマップとコンテンツは検索結果に表示されます</string>
|
||||
<string name="map_not_searchable">このマップとコンテンツはどちらも検索結果に表示されませんが,直接リンクで他の方が見ることができます.</string>
|
||||
<string name="close">閉じる</string>
|
||||
<string name="edit">編集</string>
|
||||
<string name="cancel">キャンセル</string>
|
||||
<string name="save">保存</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">マップを共有したいと思います</string>
|
||||
<string name="share_map_body_format">このマップに興味を持って頂けると思います:
|
||||
@@ -234,6 +213,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">このトラックに興味を持って頂けると思います.
|
||||
|
||||
このトラックを閲覧するには,Google Earthまたは他のマッピングソフトウェアが必要です.</string>
|
||||
<string name="share_track">トラックの共有</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">公開</string>
|
||||
|
||||
@@ -53,7 +53,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">Twoja trasa został wysłana do maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Twoja trasa została wysłana do nowego arkusza na docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Twoja trasa została wysłana do docs.google.com.</string>
|
||||
<string name="status_mymap_info">Trasa będzie widoczna za kilka minut.</string>
|
||||
<string name="status_enable_gps">Tapnij tutaj, aby przejść do ustawień Lokalizacji i zabezpieczeń, a następnie zaznacz Użyj satelitów GPS. Naciśnij klawisz Wstecz, aby powrócić.</string>
|
||||
<string name="status_approximate_location">Uwaga: używana jest przybliżona lokalizacja ustalona za pomocą stacji komórkowych i sieci WIFI.</string>
|
||||
<string name="status_no_location">Lokalizacja nie może być obecnie ustalona. Włącz wiecej źródeł (zobacz ustawienia Lokalizacji i zabezpieczeń) i upewnij się, że niebo jest widoczne. Spróbuj później.</string>
|
||||
@@ -103,6 +102,10 @@ limitations under the License.
|
||||
<string name="error">Błąd</string>
|
||||
<string name="success">Powodzenie</string>
|
||||
<string name="waypoint">Punkt orientacyjny</string>
|
||||
<string name="cancel">Anuluj</string>
|
||||
<string name="close">Zamknij</string>
|
||||
<string name="edit">Edytuj</string>
|
||||
<string name="save">Zapisz</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Początek)</string>
|
||||
@@ -233,28 +236,6 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">Satelity GPS</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Wybierz mapę z twojego konta:</string>
|
||||
<string name="create_new_map">Utwórz nową mapę...</string>
|
||||
<string name="map_list_title">Wyślij do Moich Map Google</string>
|
||||
<string name="maps_list_loading">Wczytywanie map...</string>
|
||||
<string name="maps_list_failed">Nie można wczytać twoich map!</string>
|
||||
<string name="open_map">Wybierz tę mapę</string>
|
||||
<string name="edit_map_details">Edytuj szczegóły mapy</string>
|
||||
<string name="share_map">Podziel się mapą z przyjaciółmi</string>
|
||||
<string name="share_track">Podziel się trasą z przyjaciółmi</string>
|
||||
<string name="delete_map">Usuń mapę z twojego konta</string>
|
||||
<string name="map_title">Tytuł mapy:</string>
|
||||
<string name="map_description">Opis mapy:</string>
|
||||
<string name="map_searchable_edit">Pokazuj tę pamę i jej zawartość w wynikach wyszukiwania</string>
|
||||
<string name="map_not_searchable_edit">Nie pokazuj tej mapy i jej zawartości w wynikach wyszukiwania. Dostęp do mapy będzie ciągle możliwy poprzez odnośnik</string>
|
||||
<string name="map_searchable">Ta mapa i jej zawartość będzie dostępna w wynikach wyszukiwania</string>
|
||||
<string name="map_not_searchable">Mapa i jej zawartość nie będzie dostępna w wynikach wyszukiwania, ale będzie dostępna przez bezpośredni odnośnik</string>
|
||||
<string name="close">Zamknij</string>
|
||||
<string name="edit">Edytuj</string>
|
||||
<string name="cancel">Anuluj</string>
|
||||
<string name="save">Zapisz</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">Chciałbym się z Tobą podzielić mapą</string>
|
||||
<string name="share_map_body_format">Myślę, że może Cię zainteresować ta mapa:
|
||||
@@ -264,6 +245,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">Myślę, że może Cię zainteresować ta trasa.
|
||||
|
||||
Trasę możesz obejrzeć w Google Earth lub innym programie do obsługi map.</string>
|
||||
<string name="share_track">Podziel się trasą z przyjaciółmi</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Publiczna</string>
|
||||
|
||||
@@ -52,7 +52,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">Seu caminho foi enviado para maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Seu caminho foi enviado para uma nova planilha em docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Seu caminho foi enviado para docs.google.com.</string>
|
||||
<string name="status_mymap_info">"Pode demorar alguns minutor para que novos caminhos apareçam.</string>
|
||||
<string name="status_enable_gps">"Por favor pressione aqui para ir aos ajustes de segurança e localização e verifique que o GPS esteja habilitado. Pressione o botão de voltar para retornar."</string>
|
||||
<string name="status_approximate_location">Nota: Uma localização aproximada, determinada por torres de telefonia celular, está sendo mostrada.</string>
|
||||
<string name="status_no_location">Sua localização não pôde ser determinada. Habilite fontes adicionais de localização (veja configurações de Segurança e Localização) e assegure-se de que o céu está visível.</string>
|
||||
@@ -101,6 +100,10 @@ limitations under the License.
|
||||
<string name="error">Erro</string>
|
||||
<string name="success">Successo</string>
|
||||
<string name="waypoint">Ponto de passagem</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="close">Fechar</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="save">Salvar</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Início)</string>
|
||||
@@ -184,28 +187,6 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">Satélites de GPS</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Escolha um mapa da sua conta:</string>
|
||||
<string name="create_new_map">Criar novo mapa...</string>
|
||||
<string name="map_list_title">Enviar para o Google Meus Mapas</string>
|
||||
<string name="maps_list_loading">Carregando mapas...</string>
|
||||
<string name="maps_list_failed">Seus mapas não puderam ser carregados neste momento!</string>
|
||||
<string name="open_map">Selecionar este Mapa</string>
|
||||
<string name="edit_map_details">Editar detalhes do mapa</string>
|
||||
<string name="share_map">Compartilhar mapa com amigos</string>
|
||||
<string name="share_track">Compartilhar caminho com amigos</string>
|
||||
<string name="delete_map">Apagar mapa da sua conta</string>
|
||||
<string name="map_title">Título do Mapa:</string>
|
||||
<string name="map_description">Descrição do Mapa:</string>
|
||||
<string name="map_searchable_edit">Fazer com que o conteúdo deste mapa apareça em resultados de buscas</string>
|
||||
<string name="map_not_searchable_edit">Não permitir que este mapa ou seu conteúdo apareçam em resultados de buscas, porém ele ainda pode ser visto por sua URL direta</string>
|
||||
<string name="map_searchable">Este mapa e seu conteúdo vão aparecer em resultados de buscas</string>
|
||||
<string name="map_not_searchable">Nem este mapa nem seu conteúdo vão aparecer em resultados de buscas, porém outras pessoas ainda podem vê-los se obtiverem sua URL direta</string>
|
||||
<string name="close">Fechar</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="save">Salvar</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">Eu gostaria de compartilhar um mapa com você</string>
|
||||
<string name="share_map_body_format">Eu acredito que você pode se interessar por este mapa:
|
||||
@@ -215,6 +196,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">Eu acredito que você pode se interesasr por este caminho.
|
||||
|
||||
Para visualizar este caminho, use o Google Earth ou outro software de mapas.</string>
|
||||
<string name="share_track">Compartilhar caminho com amigos</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Público</string>
|
||||
|
||||
@@ -53,7 +53,6 @@ limitations under the License.
|
||||
<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 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 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>
|
||||
@@ -103,6 +102,10 @@ limitations under the License.
|
||||
<string name="error">Fel</string>
|
||||
<string name="success">Lyckades</string>
|
||||
<string name="waypoint">Positionsmärke</string>
|
||||
<string name="cancel">Avbryt</string>
|
||||
<string name="close">Stäng</string>
|
||||
<string name="edit">Ändra</string>
|
||||
<string name="save">Spara</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Start)</string>
|
||||
@@ -202,28 +205,6 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">GPS-satelliter</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Välj en karta från ditt konto:</string>
|
||||
<string name="create_new_map">Skapa en ny karta...</string>
|
||||
<string name="map_list_title">Skicka till Google Mina Kartor</string>
|
||||
<string name="maps_list_loading">Laddar kartor...</string>
|
||||
<string name="maps_list_failed">Dina kartor kunde inte laddas just nu!</string>
|
||||
<string name="open_map">Välj den här kartan</string>
|
||||
<string name="edit_map_details">Ändra kartdetaljer</string>
|
||||
<string name="share_map">Dela kartan med vänner</string>
|
||||
<string name="share_track">Dela spår med vänner</string>
|
||||
<string name="delete_map">Ta bort kartan från ditt konto</string>
|
||||
<string name="map_title">Karttitel:</string>
|
||||
<string name="map_description">Kartbeskrivning:</string>
|
||||
<string name="map_searchable_edit">Gör så att kartan och dess innehåll visas i sökresultat</string>
|
||||
<string name="map_not_searchable_edit">Låt inte kartan och dess innehåll visas i sökresultat, men de kan ändå ses av andra genom en direktlänk</string>
|
||||
<string name="map_searchable">Kartan och dess innehåll kommer att visas i sökresultat</string>
|
||||
<string name="map_not_searchable">Varken kartan eller dess innehåll visas i sökresultat, men de kan ändå ses av andra genom en direktlänk</string>
|
||||
<string name="close">Stäng</string>
|
||||
<string name="edit">Ändra</string>
|
||||
<string name="cancel">Avbryt</string>
|
||||
<string name="save">Spara</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">Jag vill dela en karta med dig</string>
|
||||
<string name="share_map_body_format">Jag tror att du skulle vara intresserad av den här kartan:
|
||||
@@ -233,6 +214,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">Jag tror att du skulle vara intresserad av det här spåret.
|
||||
|
||||
För att se det här spåret använd Google Earth eller något annat kartprogram.</string>
|
||||
<string name="share_track">Dela spår med vänner</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Publik</string>
|
||||
|
||||
@@ -56,7 +56,6 @@ limitations under the License.
|
||||
<string name="status_tracks_have_been_uploaded">你的轨迹已经发送到 maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">你的轨迹已经作为新的电子表格发送到 docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">你的轨迹已经发送到 docs.google.com.</string>
|
||||
<string name="status_mymap_info">新增的轨迹可能需要等待一段时间才会显示到页面上.</string>
|
||||
<string name="status_enable_gps">请点击这里进入\'安全和位置设置\'并勾选上\'启用 GPS 卫星\'. 然后用后退键返回.</string>
|
||||
<string name="status_approximate_location">注意: 当前显示的位置是根据无线信号和无线网络计算的近似位置.</string>
|
||||
<string name="status_no_location">目前无法定位. 请启用更多的位置来源 (在\'安全和位置设置\'中) 并确认天空是可见的. 再重试.</string>
|
||||
@@ -106,6 +105,10 @@ limitations under the License.
|
||||
<string name="error">错误</string>
|
||||
<string name="success">成功</string>
|
||||
<string name="waypoint">路点</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="edit">编辑</string>
|
||||
<string name="save">保存</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(开始)</string>
|
||||
@@ -239,28 +242,6 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">GPS 卫星</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">从你的帐户里选择地图:</string>
|
||||
<string name="create_new_map">创建新地图...</string>
|
||||
<string name="map_list_title">发送到 Google My Maps</string>
|
||||
<string name="maps_list_loading">载入地图中...</string>
|
||||
<string name="maps_list_failed">现在无法载入你的地图!</string>
|
||||
<string name="open_map">选择此地图</string>
|
||||
<string name="edit_map_details">编辑地图详细信息</string>
|
||||
<string name="share_map">与朋友分享地图</string>
|
||||
<string name="share_track">与朋友分享轨迹</string>
|
||||
<string name="delete_map">删除你帐号下的此地图</string>
|
||||
<string name="map_title">地图标题:</string>
|
||||
<string name="map_description">地图描述:</string>
|
||||
<string name="map_searchable_edit">允许此地图及其内容出现在搜索结果中</string>
|
||||
<string name="map_not_searchable_edit">不允许地图及其内容出现在搜索结果中, 但它的内容依然可以通过网址直接访问</string>
|
||||
<string name="map_searchable">此地图及其内容会出现在搜索结果中</string>
|
||||
<string name="map_not_searchable">此地图及其内容不会出现在搜索结果中, 但它的内容依然可以通过网址直接访问</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="edit">编辑</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="save">保存</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">我想与你分享一张地图</string>
|
||||
<string name="share_map_body_format">我想你可能会对这个地图感兴趣:
|
||||
@@ -270,6 +251,7 @@ limitations under the License.
|
||||
<string name="send_track_body_format">我想你可能会对这条轨迹感兴趣.
|
||||
|
||||
请用 Google Earth 或其它地图软件查看此轨迹.</string>
|
||||
<string name="share_track">与朋友分享轨迹</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">公开</string>
|
||||
|
||||
@@ -35,7 +35,7 @@ limitations under the License.
|
||||
<!-- Error messages -->
|
||||
<string name="error_no_location_manager">No location manager was found on this device!</string>
|
||||
<string name="error_no_gps_location_provider">No GPS location provider was found on this device.</string>
|
||||
<string name="error_sending_to_mymap">There was an error while sending your track to Google My Map. Please try again later.</string>
|
||||
<string name="error_sending_to_fusiontables">There was an error while sending your track to Google Fusion Tables. Please try again later.</string>
|
||||
<string name="error_sending_to_docs">There was an error while sending your track to Google Docs. Please try again later.</string>
|
||||
<string name="error_unable_to_start_recording">Unable to start recording. Try again.</string>
|
||||
<string name="error_unable_to_stop_recording">Unable to stop recording. Try again.</string>
|
||||
@@ -50,11 +50,10 @@ limitations under the License.
|
||||
<string name="wait_for_fix">Waiting for a better GPS fix. Make sure the sky is visible.</string>
|
||||
<string name="status_now_recording">Now recording a new track.</string>
|
||||
<string name="status_stopped_recording">Stopped recording your track.</string>
|
||||
<string name="status_new_mymap_has_been_created">A new Google My Map has been created at maps.google.com.</string>
|
||||
<string name="status_new_fusiontable_has_been_created">A new Google Fusion table has been created at google.com/fusiontables.</string>
|
||||
<string name="status_tracks_have_been_uploaded">Your track has been sent to maps.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_new_doc">Your track has been sent to a new spreadsheet at docs.google.com.</string>
|
||||
<string name="status_tracks_have_been_uploaded_to_docs">Your track has been sent to docs.google.com.</string>
|
||||
<string name="status_mymap_info">It may sometimes take a few minutes before new tracks appear.</string>
|
||||
<string name="status_enable_gps">Please tap here to go to Security and Location Settings and check \'enable GPS satellites\'. Press back button to return.</string>
|
||||
<string name="status_approximate_location">Note: An approximate location as determined from cell towers and WIFI networks is shown.</string>
|
||||
<string name="status_no_location">A location can currently not be determined. Enable more location sources (see Security and Location Settings) and make sure the sky is visible. Try again later.</string>
|
||||
@@ -66,11 +65,11 @@ limitations under the License.
|
||||
<string name="progress_message">Sending your track to Google.</string>
|
||||
<string name="progress_message_reading_track">Reading track...</string>
|
||||
<string name="progress_message_preparing_track">Preparing track...</string>
|
||||
<string name="progress_message_authenticating_mymaps">Authenticating to Google Maps...</string>
|
||||
<string name="progress_message_authenticating_fusiontables">Authenticating to Google Fusion Tables...</string>
|
||||
<string name="progress_message_authenticating_docs">Authenticating to Google Docs...</string>
|
||||
<string name="progress_message_sending_docs">Sending track to Google Docs...</string>
|
||||
<string name="progress_message_sending_mymaps">Sending track to Google Maps...</string>
|
||||
<string name="progress_message_creating_map">Creating new My Map...</string>
|
||||
<string name="progress_message_sending_fusiontables">Sending track to Fusion Tables...</string>
|
||||
<string name="progress_message_creating_fusiontable">Creating new Fusion table...</string>
|
||||
<string name="import_progress_message">Importing a GPS track. This may take a while.</string>
|
||||
<string name="write_progress_message">Writing file to SD card. This may take a while.</string>
|
||||
<string name="progress_message_updating_track">Updating track statistics...</string>
|
||||
@@ -81,10 +80,9 @@ limitations under the License.
|
||||
<string name="track_will_be_permanently_deleted">The track and its markers will be permanently deleted from this phone.</string>
|
||||
<string name="marker_will_be_permanently_deleted">The marker will be permanently deleted from this phone.</string>
|
||||
|
||||
<!-- Text and links when uploading to Google My Maps -->
|
||||
<!-- Text and links when uploading to Google Fusion Tables -->
|
||||
<string name="new_map_description">Created by My Tracks on Android.</string>
|
||||
<string name="my_map_link">Created by <a href=\'http://mytracks.appspot.com\'>My Tracks</a> on Android.</string>
|
||||
<string name="new_map_title">New map</string>
|
||||
<string name="fusiontable_link">Created by <a href=\'http://mytracks.appspot.com\'>My Tracks</a> on Android.</string>
|
||||
|
||||
<!-- Formatting of track and marker descriptions -->
|
||||
<string name="points">Points</string>
|
||||
@@ -105,6 +103,10 @@ limitations under the License.
|
||||
<string name="success">Success</string>
|
||||
<string name="waypoint">Waypoint</string>
|
||||
<string name="license">License</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="save">Save</string>
|
||||
|
||||
<!-- Placemark labels for start and end of track -->
|
||||
<string name="start">(Start)</string>
|
||||
@@ -112,8 +114,8 @@ limitations under the License.
|
||||
|
||||
<!-- TrackList -->
|
||||
<string name="tracklist_show_track">Show</string>
|
||||
<string name="tracklist_send_to_mymaps">Send to Google My Maps</string>
|
||||
<string name="tracklist_send_to_new_mymap">Send to New Google My Map</string>
|
||||
<string name="tracklist_send_to_fusiontables">Send to Google Fusion Tables</string>
|
||||
<string name="tracklist_send_to_new_fusiontable">Send to New Google Fusion Table</string>
|
||||
<string name="tracklist_send_to_docs">Send to Google Docs</string>
|
||||
<string name="tracklist_send_to_google">Send to Google...</string>
|
||||
<string name="tracklist_delete_track">Delete from phone...</string>
|
||||
@@ -123,7 +125,7 @@ limitations under the License.
|
||||
<string name="tracklist_save_as_kml">Save a KML file</string>
|
||||
<string name="tracklist_save_as_csv">Save a CSV file</string>
|
||||
<string name="tracklist_save_as_tcx">Save a TCX file</string>
|
||||
<string name="tracklist_btn_send_all_to_mymaps">Send to Google My Maps</string>
|
||||
<string name="tracklist_btn_send_all_to_fusiontables">Send to Google Fusion Tables</string>
|
||||
<string name="tracklist_btn_delete_all">Delete all tracks</string>
|
||||
<!-- Export All -->
|
||||
<string name="tracklist_btn_export_all">Export all tracks</string>
|
||||
@@ -132,7 +134,7 @@ limitations under the License.
|
||||
<string name="tracklist_all_tracks">All Tracks:</string>
|
||||
<string name="tracklist_this_track">This Track</string>
|
||||
<string name="tracklist_share_track">Share with Friends...</string>
|
||||
<string name="tracklist_share_link">A link to Google My Map</string>
|
||||
<string name="tracklist_share_linkfusion">A link to Google Fusion Tables</string>
|
||||
<string name="tracklist_share_gpx_file">A GPX file</string>
|
||||
<string name="tracklist_share_kml_file">A KML file</string>
|
||||
<string name="tracklist_share_csv_file">A CSV file</string>
|
||||
@@ -224,6 +226,7 @@ limitations under the License.
|
||||
<string name="bluetooth_sensor_summary">The bluetooth sensor to connect to</string>
|
||||
<string name="settings_modular_stats_display_title">Modular Stats Display</string>
|
||||
|
||||
<!-- Backups -->
|
||||
<string name="settings_backup">Backup (BETA)</string>
|
||||
<string name="settings_sd_backup">SD Card Backup</string>
|
||||
<string name="settings_backup_to_sd">Backup now</string>
|
||||
@@ -232,7 +235,6 @@ limitations under the License.
|
||||
<string name="settings_restore_from_sd_summary">Restore all tracks and settings from the SD card</string>
|
||||
<string name="settings_no_backup_while_recording">Cannot manipulate backups while recording a track.</string>
|
||||
|
||||
<!-- Backups -->
|
||||
<string name="restore_overwrites_warning">Restoring a backup from the SD card will OVERWRITE all your current tracks and settings. Are you sure?</string>
|
||||
<string name="select_backup_to_restore">Select the backup to restore</string>
|
||||
<string name="no_backups">No backups found.</string>
|
||||
@@ -242,37 +244,17 @@ limitations under the License.
|
||||
<!-- MyTracksSky -->
|
||||
<string name="mytracks_sky">GPS Satellites</string>
|
||||
|
||||
<!-- My Maps -->
|
||||
<string name="select_map_message">Pick a map from your account:</string>
|
||||
<string name="create_new_map">Create new map...</string>
|
||||
<string name="map_list_title">Send to Google My Maps</string>
|
||||
<string name="maps_list_loading">Loading maps...</string>
|
||||
<string name="maps_list_failed">Your maps could not be loaded at this time!</string>
|
||||
<string name="open_map">Select this Map</string>
|
||||
<string name="edit_map_details">Edit Map Details</string>
|
||||
<string name="share_map">Share Map with Friends</string>
|
||||
<string name="share_track">Share Track with Friends</string>
|
||||
<string name="delete_map">Delete Map from your Account</string>
|
||||
<string name="map_title">Map Title:</string>
|
||||
<string name="map_description">Map Description:</string>
|
||||
<string name="map_searchable_edit">Make this map and its contents show up in search results</string>
|
||||
<string name="map_not_searchable_edit">Do not let this map or its contents show up in search results, though they may still be viewed be others through the direct URL</string>
|
||||
<string name="map_searchable">This map and its contents will show in search results</string>
|
||||
<string name="map_not_searchable">Neither this map nor its contents will show in search results, but they may still be viewed by others through a direct link</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="save">Save</string>
|
||||
|
||||
<!-- Sharing -->
|
||||
<string name="share_map_subject">I would like to share a map with you</string>
|
||||
<string name="share_map_body_format">I think you might be interested in this map:
|
||||
<string name="share_map">Share map with friends</string>
|
||||
<string name="share_map_subject">I would like to share a track with you</string>
|
||||
<string name="share_map_body_format">I think you might be interested in this track:
|
||||
|
||||
%s</string>
|
||||
<string name="send_track_subject">I would like to share a track with you</string>
|
||||
<string name="send_track_body_format">I think you might be interested in this track.
|
||||
|
||||
To view this track use Google Earth or other mapping software.</string>
|
||||
<string name="share_track">Share track with friends</string>
|
||||
|
||||
<!-- Visibility -->
|
||||
<string name="public_map">Public</string>
|
||||
@@ -346,7 +328,7 @@ limitations under the License.
|
||||
<string name="min_per_mile">min/mi</string>
|
||||
<string name="per_mile">per mile</string>
|
||||
|
||||
<!-- Power -->
|
||||
<!-- Sensors -->
|
||||
<string name="watts">W</string>
|
||||
|
||||
<string name="revolutions_per_minute">rpm</string>
|
||||
@@ -384,11 +366,9 @@ limitations under the License.
|
||||
|
||||
<!-- Send to Google -->
|
||||
<string name="sendtogoogle_title">Send to...</string>
|
||||
<string name="sendtogoogle_google_mymaps">Google My Maps</string>
|
||||
<string name="sendtogoogle_google_fusiontables">Google Fusion Tables</string>
|
||||
<string name="sendtogoogle_google_docs">Google Docs</string>
|
||||
<string name="sendtogoogle_send_now">Send now</string>
|
||||
<string name="sendtogoogle_create_new_map">Create a new map</string>
|
||||
<string name="sendtogoogle_pick_existing_map">Pick an existing map</string>
|
||||
<string name="sendtogoogle_send_stats">Send track statistics</string>
|
||||
<string name="sendtogoogle_send_stats_and_points">Send statistics and points</string>
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
|
||||
import com.google.wireless.gdata.maps.MapsClient;
|
||||
|
||||
/**
|
||||
* Constants for My Maps.
|
||||
*/
|
||||
public class MyMapsConstants {
|
||||
public static final String MAPSHOP_BASE_URL =
|
||||
"http://maps.google.com/maps/ms";
|
||||
public static final String MAPSHOP_SERVICE = MapsClient.SERVICE;
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
private MyMapsConstants() { }
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
|
||||
import com.google.android.maps.GeoPoint;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* MyMapsFeature contains all of the data associated with a feature in My Maps,
|
||||
* where a feature is a marker, line, or shape. Some of the data is stored in a
|
||||
* {@link MyMapsFeatureMetadata} object so that it can be more efficiently
|
||||
* transmitted to other activities.
|
||||
*/
|
||||
public class MyMapsFeature implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8439035544430497236L;
|
||||
|
||||
/** A marker feature displays an icon at a single point on the map. */
|
||||
public static final int MARKER = 0;
|
||||
|
||||
/**
|
||||
* A line feature displays a line connecting a set of points on the map.
|
||||
*/
|
||||
public static final int LINE = 1;
|
||||
|
||||
/**
|
||||
* A shape feature displays a border defined by connecting a set of points,
|
||||
* including connecting the last to the first, and displays the area
|
||||
* confined by this border.
|
||||
*/
|
||||
public static final int SHAPE = 2;
|
||||
|
||||
/** The local feature id for this feature, if needed. */
|
||||
private String androidId;
|
||||
|
||||
/**
|
||||
* The latitudes of the points of this feature in order, specified in
|
||||
* millionths of a degree north.
|
||||
*/
|
||||
private final Vector<Integer> latitudeE6 = new Vector<Integer>();
|
||||
|
||||
/**
|
||||
* The longitudes of the points of this feature in order, specified in
|
||||
* millionths of a degree east.
|
||||
*/
|
||||
private final Vector<Integer> longitudeE6 = new Vector<Integer>();
|
||||
|
||||
/** The metadata of this feature in a format efficient for transmission. */
|
||||
private MyMapsFeatureMetadata featureInfo = new MyMapsFeatureMetadata();
|
||||
|
||||
/**
|
||||
* Initializes a valid but empty feature. It will default to a
|
||||
* {@link #MARKER} with a blue placemark with a dot as an icon at the
|
||||
* location (0, 0).
|
||||
*/
|
||||
public MyMapsFeature() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new point to the end of this feature.
|
||||
*
|
||||
* @param point The new point to add
|
||||
*/
|
||||
public synchronized void addPoint(GeoPoint point) {
|
||||
latitudeE6.add(point.getLatitudeE6());
|
||||
longitudeE6.add(point.getLongitudeE6());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new local id for this feature based on the current time and
|
||||
* a random number.
|
||||
*/
|
||||
public synchronized void generateAndroidId() {
|
||||
Long time = System.currentTimeMillis();
|
||||
Integer random = (int) (Math.random() * 10000);
|
||||
androidId = time.toString() + "." + random.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current local id for this feature if one is available.
|
||||
*
|
||||
* @return The local id for this feature
|
||||
*/
|
||||
public String getAndroidId() {
|
||||
return androidId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current (html) description of this feature. The description
|
||||
* is stored in the feature metadata.
|
||||
*
|
||||
* @return The description of this feature
|
||||
*/
|
||||
public String getDescription() {
|
||||
return featureInfo.getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description of this feature. That description is stored in the
|
||||
* feature metadata.
|
||||
*
|
||||
* @param description The new description of this feature
|
||||
*/
|
||||
public synchronized void setDescription(String description) {
|
||||
featureInfo.setDescription(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the point at the given index for this feature.
|
||||
*
|
||||
* @param index The index of the point desired
|
||||
* @return A {@link GeoPoint} representing the point or null if that point
|
||||
* doesn't exist
|
||||
*/
|
||||
public GeoPoint getPoint(int index) {
|
||||
if (latitudeE6.size() <= index) {
|
||||
return null;
|
||||
}
|
||||
return new GeoPoint(latitudeE6.get(index), longitudeE6.get(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of points in this feature and return that count.
|
||||
*
|
||||
* @return The number of points in this feature
|
||||
*/
|
||||
public int getPointCount() {
|
||||
return latitudeE6.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the title of this feature. That title is stored in the feature
|
||||
* metadata.
|
||||
*
|
||||
* @return the current title of this feature
|
||||
*/
|
||||
public String getTitle() {
|
||||
return featureInfo.getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this feature. That type is stored in the feature
|
||||
* metadata.
|
||||
*
|
||||
* @return One of {@link #MARKER}, {@link #LINE}, or {@link #SHAPE}
|
||||
* identifying the type of this feature
|
||||
*/
|
||||
public int getType() {
|
||||
return featureInfo.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current color of this feature as an ARGB color integer.
|
||||
* That color is stored in the feature metadata.
|
||||
*
|
||||
* @return The ARGB color of this feature
|
||||
*/
|
||||
public int getColor() {
|
||||
return featureInfo.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current line width of this feature. That line width is
|
||||
* stored in the feature metadata.
|
||||
*
|
||||
* @return The line width of this feature
|
||||
*/
|
||||
public int getLineWidth() {
|
||||
return featureInfo.getLineWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current fill color of this feature as an ARGB color
|
||||
* integer. That color is stored in the feature metadata.
|
||||
*
|
||||
* @return The ARGB fill color of this feature
|
||||
*/
|
||||
public int getFillColor() {
|
||||
return featureInfo.getFillColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current icon url of this feature. That icon url is stored
|
||||
* in the feature metadata.
|
||||
*
|
||||
* @return The icon url for this feature
|
||||
*/
|
||||
public String getIconUrl() {
|
||||
return featureInfo.getIconUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of this feature. That title is stored in the feature
|
||||
* metadata.
|
||||
*
|
||||
* @param title The new title of this feature
|
||||
*/
|
||||
public synchronized void setTitle(String title) {
|
||||
featureInfo.setTitle(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of this feature. That type is stored in the feature
|
||||
* metadata.
|
||||
*
|
||||
* @param type The new type of the feature. That type must be one of
|
||||
* {@link #MARKER}, {@link #LINE}, or {@link #SHAPE}
|
||||
*/
|
||||
public synchronized void setType(int type) {
|
||||
featureInfo.setType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ARGB color of this feature. That color is stored in the feature
|
||||
* metadata.
|
||||
*
|
||||
* @param color The new ARGB color of this feature
|
||||
*/
|
||||
public synchronized void setColor(int color) {
|
||||
featureInfo.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon url of this feature. That icon url is stored in the feature
|
||||
* metadata.
|
||||
*
|
||||
* @param url The new icon url of the feature
|
||||
*/
|
||||
public synchronized void setIconUrl(String url) {
|
||||
featureInfo.setIconUrl(url);
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
|
||||
/**
|
||||
* Metadata about a maps feature.
|
||||
*/
|
||||
public class MyMapsFeatureMetadata {
|
||||
|
||||
private static final String BLUE_DOT_URL =
|
||||
"http://maps.google.com/mapfiles/ms/micons/blue-dot.png";
|
||||
private static final int DEFAULT_COLOR = 0x800000FF;
|
||||
private static final int DEFAULT_FILL_COLOR = 0xC00000FF;
|
||||
|
||||
private String title;
|
||||
private String description;
|
||||
private int type;
|
||||
private int color;
|
||||
private int lineWidth;
|
||||
private int fillColor;
|
||||
private String iconUrl;
|
||||
|
||||
public MyMapsFeatureMetadata() {
|
||||
title = "";
|
||||
description = "";
|
||||
type = MyMapsFeature.MARKER;
|
||||
color = DEFAULT_COLOR;
|
||||
lineWidth = 5;
|
||||
fillColor = DEFAULT_FILL_COLOR;
|
||||
iconUrl = BLUE_DOT_URL;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public int getLineWidth() {
|
||||
return lineWidth;
|
||||
}
|
||||
|
||||
public void setLineWidth(int width) {
|
||||
lineWidth = width;
|
||||
}
|
||||
|
||||
public int getFillColor() {
|
||||
return fillColor;
|
||||
}
|
||||
|
||||
public void setFillColor(int color) {
|
||||
fillColor = color;
|
||||
}
|
||||
|
||||
public String getIconUrl() {
|
||||
return iconUrl;
|
||||
}
|
||||
|
||||
public void setIconUrl(String url) {
|
||||
iconUrl = new String(url);
|
||||
}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
|
||||
import com.google.wireless.gdata.data.Entry;
|
||||
import com.google.wireless.gdata.data.StringUtils;
|
||||
import com.google.wireless.gdata.maps.MapFeatureEntry;
|
||||
import com.google.wireless.gdata.maps.MapsClient;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.util.Log;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Converter from GData objects to MyMaps objects.
|
||||
*/
|
||||
public class MyMapsGDataConverter {
|
||||
|
||||
private final XmlSerializer xmlSerializer;
|
||||
|
||||
public MyMapsGDataConverter() throws XmlPullParserException {
|
||||
xmlSerializer = XmlPullParserFactory.newInstance().newSerializer();
|
||||
}
|
||||
|
||||
public static MyMapsMapMetadata getMapMetadataForEntry(
|
||||
MapFeatureEntry entry) {
|
||||
MyMapsMapMetadata metadata = new MyMapsMapMetadata();
|
||||
if ("public".equals(entry.getPrivacy())) {
|
||||
metadata.setSearchable(true);
|
||||
} else {
|
||||
metadata.setSearchable(false);
|
||||
}
|
||||
metadata.setTitle(entry.getTitle());
|
||||
|
||||
String desc = entry.getContent();
|
||||
if (desc.length() > 12) {
|
||||
metadata.setDescription(desc.substring(9, desc.length() - 3));
|
||||
}
|
||||
metadata.setGDataEditUri(entry.getEditUri());
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public static String getMapidForEntry(Entry entry) {
|
||||
return MapsClient.getMapIdFromMapEntryId(entry.getId());
|
||||
}
|
||||
|
||||
public static Entry getMapEntryForMetadata(MyMapsMapMetadata metadata) {
|
||||
MapFeatureEntry entry = new MapFeatureEntry();
|
||||
entry.setEditUri(metadata.getGDataEditUri());
|
||||
entry.setTitle(metadata.getTitle());
|
||||
entry.setContent(metadata.getDescription());
|
||||
entry.setPrivacy(metadata.getSearchable() ? "public" : "unlisted");
|
||||
entry.setAuthor("android");
|
||||
entry.setEmail("nobody@google.com");
|
||||
return entry;
|
||||
}
|
||||
|
||||
public MapFeatureEntry getEntryForFeature(MyMapsFeature feature) {
|
||||
MapFeatureEntry entry = new MapFeatureEntry();
|
||||
entry.setTitle(feature.getTitle());
|
||||
entry.setAuthor("android");
|
||||
entry.setEmail("nobody@google.com");
|
||||
entry.setCategoryScheme("http://schemas.google.com/g/2005#kind");
|
||||
entry.setCategory("http://schemas.google.com/g/2008#mapfeature");
|
||||
entry.setEditUri("");
|
||||
if (!StringUtils.isEmpty(feature.getAndroidId())) {
|
||||
entry.setAttribute("_androidId", feature.getAndroidId());
|
||||
}
|
||||
try {
|
||||
StringWriter writer = new StringWriter();
|
||||
xmlSerializer.setOutput(writer);
|
||||
xmlSerializer.startTag(null, "Placemark");
|
||||
xmlSerializer.attribute(null, "xmlns", "http://earth.google.com/kml/2.2");
|
||||
xmlSerializer.startTag(null, "Style");
|
||||
if (feature.getType() == MyMapsFeature.MARKER) {
|
||||
xmlSerializer.startTag(null, "IconStyle");
|
||||
xmlSerializer.startTag(null, "Icon");
|
||||
xmlSerializer.startTag(null, "href");
|
||||
xmlSerializer.text(feature.getIconUrl());
|
||||
xmlSerializer.endTag(null, "href");
|
||||
xmlSerializer.endTag(null, "Icon");
|
||||
xmlSerializer.endTag(null, "IconStyle");
|
||||
} else {
|
||||
xmlSerializer.startTag(null, "LineStyle");
|
||||
xmlSerializer.startTag(null, "color");
|
||||
int color = feature.getColor();
|
||||
// Reverse the color because KML is ABGR and Android is ARGB
|
||||
xmlSerializer.text(Integer.toHexString(
|
||||
Color.argb(Color.alpha(color), Color.blue(color),
|
||||
Color.green(color), Color.red(color))));
|
||||
xmlSerializer.endTag(null, "color");
|
||||
xmlSerializer.startTag(null, "width");
|
||||
xmlSerializer.text(Integer.toString(feature.getLineWidth()));
|
||||
xmlSerializer.endTag(null, "width");
|
||||
xmlSerializer.endTag(null, "LineStyle");
|
||||
|
||||
if (feature.getType() == MyMapsFeature.SHAPE) {
|
||||
xmlSerializer.startTag(null, "PolyStyle");
|
||||
xmlSerializer.startTag(null, "color");
|
||||
int fcolor = feature.getFillColor();
|
||||
// Reverse the color because KML is ABGR and Android is ARGB
|
||||
xmlSerializer.text(Integer.toHexString(Color.argb(Color.alpha(fcolor),
|
||||
Color.blue(fcolor), Color.green(fcolor), Color.red(fcolor))));
|
||||
xmlSerializer.endTag(null, "color");
|
||||
xmlSerializer.startTag(null, "fill");
|
||||
xmlSerializer.text("1");
|
||||
xmlSerializer.endTag(null, "fill");
|
||||
xmlSerializer.startTag(null, "outline");
|
||||
xmlSerializer.text("1");
|
||||
xmlSerializer.endTag(null, "outline");
|
||||
xmlSerializer.endTag(null, "PolyStyle");
|
||||
}
|
||||
}
|
||||
xmlSerializer.endTag(null, "Style");
|
||||
xmlSerializer.startTag(null, "name");
|
||||
xmlSerializer.text(feature.getTitle());
|
||||
xmlSerializer.endTag(null, "name");
|
||||
xmlSerializer.startTag(null, "description");
|
||||
xmlSerializer.cdsect(feature.getDescription());
|
||||
xmlSerializer.endTag(null, "description");
|
||||
StringBuilder pointBuilder = new StringBuilder();
|
||||
for (int i = 0; i < feature.getPointCount(); ++i) {
|
||||
if (i > 0) {
|
||||
pointBuilder.append('\n');
|
||||
}
|
||||
pointBuilder.append(feature.getPoint(i).getLongitudeE6() / 1e6);
|
||||
pointBuilder.append(',');
|
||||
pointBuilder.append(feature.getPoint(i).getLatitudeE6() / 1e6);
|
||||
pointBuilder.append(",0.000000");
|
||||
}
|
||||
String pointString = pointBuilder.toString();
|
||||
if (feature.getType() == MyMapsFeature.MARKER) {
|
||||
xmlSerializer.startTag(null, "Point");
|
||||
xmlSerializer.startTag(null, "coordinates");
|
||||
xmlSerializer.text(pointString);
|
||||
xmlSerializer.endTag(null, "coordinates");
|
||||
xmlSerializer.endTag(null, "Point");
|
||||
} else if (feature.getType() == MyMapsFeature.LINE) {
|
||||
xmlSerializer.startTag(null, "LineString");
|
||||
xmlSerializer.startTag(null, "tessellate");
|
||||
xmlSerializer.text("1");
|
||||
xmlSerializer.endTag(null, "tessellate");
|
||||
xmlSerializer.startTag(null, "coordinates");
|
||||
xmlSerializer.text(pointString);
|
||||
xmlSerializer.endTag(null, "coordinates");
|
||||
xmlSerializer.endTag(null, "LineString");
|
||||
} else {
|
||||
xmlSerializer.startTag(null, "Polygon");
|
||||
xmlSerializer.startTag(null, "outerBoundaryIs");
|
||||
xmlSerializer.startTag(null, "LinearRing");
|
||||
xmlSerializer.startTag(null, "tessellate");
|
||||
xmlSerializer.text("1");
|
||||
xmlSerializer.endTag(null, "tessellate");
|
||||
xmlSerializer.startTag(null, "coordinates");
|
||||
xmlSerializer.text(pointString + "\n"
|
||||
+ Double.toString(feature.getPoint(0).getLongitudeE6() / 1e6)
|
||||
+ ","
|
||||
+ Double.toString(feature.getPoint(0).getLatitudeE6() / 1e6)
|
||||
+ ",0.000000");
|
||||
xmlSerializer.endTag(null, "coordinates");
|
||||
xmlSerializer.endTag(null, "LinearRing");
|
||||
xmlSerializer.endTag(null, "outerBoundaryIs");
|
||||
xmlSerializer.endTag(null, "Polygon");
|
||||
}
|
||||
xmlSerializer.endTag(null, "Placemark");
|
||||
xmlSerializer.flush();
|
||||
entry.setContent(writer.toString());
|
||||
Log.d("My Google Maps", "Generated kml:\n" + entry.getContent());
|
||||
Log.d("My Google Maps", "Edit URI: " + entry.getEditUri());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.io.AuthManager;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
|
||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||
import com.google.wireless.gdata.client.GDataClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.maps.MapsClient;
|
||||
import com.google.wireless.gdata.maps.xml.XmlMapsGDataParserFactory;
|
||||
import com.google.wireless.gdata.parser.xml.SimplePullParser.ParseException;
|
||||
import com.google.wireless.gdata2.ConflictDetectedException;
|
||||
import com.google.wireless.gdata2.client.AuthenticationException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* MyMapsGDataWrapper provides a wrapper around GData operations that maintains
|
||||
* the GData client, and provides a method to run gdata queries with proper
|
||||
* error handling. After a query is run, the wrapper can be queried about the
|
||||
* error that occurred.
|
||||
*/
|
||||
public class MyMapsGDataWrapper {
|
||||
/**
|
||||
* A QueryFunction is passed in when executing a query. The query function
|
||||
* of the class is called with the GData client as a parameter. The
|
||||
* function should execute whatever operations it desires on the client
|
||||
* without concern for whether the client will throw an error.
|
||||
*/
|
||||
public interface QueryFunction {
|
||||
public abstract void query(MapsClient client)
|
||||
throws AuthenticationException, IOException, ParseException,
|
||||
ConflictDetectedException, Exception;
|
||||
}
|
||||
|
||||
// The types of error that may be encountered
|
||||
/** No error occurred. */
|
||||
public static final int ERROR_NO_ERROR = 0;
|
||||
/** There was an authentication error, the auth token may be invalid. */
|
||||
public static final int ERROR_AUTH = 1;
|
||||
/** There was an internal error on the server side. */
|
||||
public static final int ERROR_INTERNAL = 2;
|
||||
/** There was an error connecting to the server. */
|
||||
public static final int ERROR_CONNECTION = 3;
|
||||
/** The item queried did not exit. */
|
||||
public static final int ERROR_NOT_FOUND = 4;
|
||||
/** There was an error parsing or serializing locally. */
|
||||
public static final int ERROR_LOCAL = 5;
|
||||
/** There was a conflict, update the entry and try again. */
|
||||
public static final int ERROR_CONFLICT = 6;
|
||||
/**
|
||||
* A query was run after cleaning up the wrapper, so the client was invalid.
|
||||
*/
|
||||
public static final int ERROR_CLEANED_UP = 7;
|
||||
/** An unknown error occurred. */
|
||||
public static final int ERROR_UNKNOWN = 100;
|
||||
|
||||
private final GDataClient androidGdataClient;
|
||||
private final MapsClient client;
|
||||
|
||||
private String errorMessage;
|
||||
private int errorType;
|
||||
private AuthManager auth;
|
||||
private boolean retryOnAuthFailure;
|
||||
private int retriesPending;
|
||||
private boolean cleanupCalled;
|
||||
|
||||
public MyMapsGDataWrapper(Context context) {
|
||||
androidGdataClient = GDataClientFactory.getGDataClient(context);
|
||||
client =
|
||||
new MapsClient(androidGdataClient, new XmlMapsGDataParserFactory(
|
||||
new AndroidXmlParserFactory()));
|
||||
errorType = ERROR_NO_ERROR;
|
||||
errorMessage = null;
|
||||
auth = null;
|
||||
retryOnAuthFailure = false;
|
||||
retriesPending = 0;
|
||||
cleanupCalled = false;
|
||||
}
|
||||
|
||||
public boolean runQuery(final QueryFunction query) {
|
||||
if (client == null) {
|
||||
errorType = ERROR_CLEANED_UP;
|
||||
errorMessage = "GData Wrapper has already been cleaned up!";
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
query.query(client);
|
||||
errorType = ERROR_NO_ERROR;
|
||||
errorMessage = null;
|
||||
return true;
|
||||
} catch (AuthenticationException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception", e);
|
||||
errorType = ERROR_AUTH;
|
||||
errorMessage = e.getMessage();
|
||||
} catch (HttpException e) {
|
||||
Log.e(MyTracksConstants.TAG, "HttpException", e);
|
||||
errorMessage = e.getMessage();
|
||||
if (errorMessage != null && errorMessage.contains("401")) {
|
||||
errorType = ERROR_AUTH;
|
||||
} else {
|
||||
errorType = ERROR_CONNECTION;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception", e);
|
||||
errorMessage = e.getMessage();
|
||||
if (errorMessage != null && errorMessage.contains("503")) {
|
||||
errorType = ERROR_INTERNAL;
|
||||
} else {
|
||||
errorType = ERROR_CONNECTION;
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception", e);
|
||||
errorType = ERROR_LOCAL;
|
||||
errorMessage = e.getMessage();
|
||||
} catch (ConflictDetectedException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception", e);
|
||||
errorType = ERROR_CONFLICT;
|
||||
errorMessage = e.getMessage();
|
||||
} catch (Exception e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception", e);
|
||||
errorType = ERROR_UNKNOWN;
|
||||
errorMessage = e.getMessage();
|
||||
e.printStackTrace();
|
||||
}
|
||||
Log.d(MyTracksConstants.TAG, "GData error encountered: " + errorMessage);
|
||||
if (errorType == ERROR_AUTH && auth != null) {
|
||||
Runnable whenFinished = null;
|
||||
if (retryOnAuthFailure) {
|
||||
retriesPending++;
|
||||
whenFinished = new Runnable() {
|
||||
public void run() {
|
||||
retriesPending--;
|
||||
retryOnAuthFailure = false;
|
||||
runQuery(query);
|
||||
if (cleanupCalled && retriesPending == 0) {
|
||||
cleanUp();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
auth.invalidateAndRefresh(whenFinished);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getErrorType() {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
// cleanUp must be called when done using this wrapper to close the client.
|
||||
// Note that the cleanup will be delayed if auth failure retries were
|
||||
// requested and there is a pending retry.
|
||||
public void cleanUp() {
|
||||
if (retriesPending == 0 && !cleanupCalled) {
|
||||
androidGdataClient.close();
|
||||
}
|
||||
cleanupCalled = true;
|
||||
}
|
||||
|
||||
public void setAuthManager(AuthManager newAuth) {
|
||||
this.auth = newAuth;
|
||||
}
|
||||
|
||||
public void setRetryOnAuthFailure(boolean retry) {
|
||||
retryOnAuthFailure = retry;
|
||||
}
|
||||
}
|
||||
@@ -1,350 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
|
||||
import com.google.android.accounts.Account;
|
||||
import com.google.android.apps.mymaps.MyMapsGDataWrapper.QueryFunction;
|
||||
import com.google.android.apps.mytracks.AccountChooser;
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.io.AuthManager;
|
||||
import com.google.android.apps.mytracks.io.AuthManagerFactory;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.wireless.gdata.maps.MapFeatureEntry;
|
||||
import com.google.wireless.gdata.maps.MapsClient;
|
||||
import com.google.wireless.gdata.parser.GDataParser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Activity which displays the list of "my maps" maps.
|
||||
*/
|
||||
public class MyMapsList extends Activity implements ListAdapter {
|
||||
|
||||
private static final int MENU_OPEN = 0;
|
||||
private static final int MENU_SHARE = 2;
|
||||
private static final int GET_LOGIN = 1;
|
||||
|
||||
private LookupThread listLookup;
|
||||
private Vector<String[]> mapsList;
|
||||
private Vector<Boolean> publicList;
|
||||
private Set<DataSetObserver> observerSet;
|
||||
private Handler loadHandler;
|
||||
private int contextPosition;
|
||||
private AuthManager auth;
|
||||
|
||||
/**
|
||||
* Thread which looks up the maps list.
|
||||
*/
|
||||
private class LookupThread extends Thread {
|
||||
private boolean finished = false;
|
||||
private boolean success = true;
|
||||
|
||||
private GDataParser listParser;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
finished = false;
|
||||
listParser = null;
|
||||
MyMapsGDataWrapper wrapper = new MyMapsGDataWrapper(MyMapsList.this);
|
||||
wrapper.setAuthManager(auth);
|
||||
wrapper.setRetryOnAuthFailure(true);
|
||||
|
||||
success = wrapper.runQuery(new QueryFunction() {
|
||||
public void query(MapsClient client) throws IOException, Exception {
|
||||
listParser = client.getParserForFeed(
|
||||
MapFeatureEntry.class, MapsClient.getMapsFeed(),
|
||||
auth.getAuthToken());
|
||||
listParser.init();
|
||||
while (listParser.hasMoreData()) {
|
||||
MapFeatureEntry entry =
|
||||
(MapFeatureEntry) listParser.readNextEntry(null);
|
||||
MyMapsMapMetadata metadata =
|
||||
MyMapsGDataConverter.getMapMetadataForEntry(entry);
|
||||
String mapid = MyMapsGDataConverter.getMapidForEntry(entry);
|
||||
synchronized (mapsList) {
|
||||
// Search through the maps list to see if it has the mapid and
|
||||
// remove it if so, so that we can replace it with updated info
|
||||
for (int i = 0; i < mapsList.size(); ++i) {
|
||||
if (mapid.equals(mapsList.get(i)[0])) {
|
||||
mapsList.remove(i);
|
||||
publicList.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
mapsList.add(new String[] {
|
||||
mapid, metadata.getTitle(), metadata.getDescription() });
|
||||
publicList.add(metadata.getSearchable());
|
||||
}
|
||||
}
|
||||
listParser.close();
|
||||
listParser = null;
|
||||
success = true;
|
||||
finished = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (listParser != null) {
|
||||
listParser.close();
|
||||
}
|
||||
wrapper.cleanUp();
|
||||
if (success || wrapper.getErrorType() != MyMapsGDataWrapper.ERROR_AUTH) {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public boolean succeeded() {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
private final Runnable checkLoaded = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (listLookup.isFinished()) {
|
||||
findViewById(R.id.loading).setVisibility(View.GONE);
|
||||
if (!listLookup.succeeded()) {
|
||||
findViewById(R.id.failed).setVisibility(View.VISIBLE);
|
||||
}
|
||||
TextView emptyView = (TextView) findViewById(R.id.mapslist_empty);
|
||||
ListView list = (ListView) findViewById(R.id.maplist);
|
||||
list.setEmptyView(emptyView);
|
||||
} else {
|
||||
loadHandler.postDelayed(checkLoaded, 500);
|
||||
}
|
||||
Iterator<DataSetObserver> iter = observerSet.iterator();
|
||||
while (iter.hasNext()) {
|
||||
iter.next().onChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final OnItemClickListener clickListener = new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position,
|
||||
long id) {
|
||||
Intent result = new Intent();
|
||||
result.putExtra("mapid", (String) getItem(position));
|
||||
MyMapsList.this.setResult(RESULT_OK, result);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
private final OnCreateContextMenuListener contextMenuListener =
|
||||
new OnCreateContextMenuListener() {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
AdapterView.AdapterContextMenuInfo info =
|
||||
(AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
contextPosition = info.position;
|
||||
menu.add(0, MENU_OPEN, 0, R.string.open_map);
|
||||
menu.add(0, MENU_SHARE, 0, R.string.share_map);
|
||||
}
|
||||
};
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
auth = AuthManagerFactory.getAuthManager(this, GET_LOGIN, null, true,
|
||||
MapsClient.SERVICE);
|
||||
|
||||
setContentView(R.layout.list);
|
||||
|
||||
ListView list = (ListView) findViewById(R.id.maplist);
|
||||
list.setOnItemClickListener(clickListener);
|
||||
list.setOnCreateContextMenuListener(contextMenuListener);
|
||||
|
||||
mapsList = new Vector<String[]>();
|
||||
publicList = new Vector<Boolean>();
|
||||
|
||||
listLookup = new LookupThread();
|
||||
// TODO fix this for non-froyo devices.
|
||||
if (AuthManagerFactory.useModernAuthManager()) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MyTracks.getInstance().getAccountChooser().chooseAccount(
|
||||
MyMapsList.this,
|
||||
new AccountChooser.AccountHandler() {
|
||||
@Override
|
||||
public void handleAccountSelected(Account account) {
|
||||
if (account != null) {
|
||||
// The user did not quit and there was a valid google
|
||||
// account.
|
||||
doLogin(account);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
doLogin(null);
|
||||
}
|
||||
|
||||
observerSet = new HashSet<DataSetObserver>();
|
||||
|
||||
list.setAdapter(this);
|
||||
|
||||
loadHandler = new Handler();
|
||||
loadHandler.postDelayed(checkLoaded, 500);
|
||||
}
|
||||
|
||||
private void doLogin(final Account account) {
|
||||
auth.doLogin(new Runnable() {
|
||||
public void run() {
|
||||
listLookup.start();
|
||||
}
|
||||
}, account);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
if (requestCode == GET_LOGIN) {
|
||||
auth.authResult(resultCode, data);
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_OPEN:
|
||||
clickListener.onItemClick(null, null, contextPosition, 0);
|
||||
return true;
|
||||
case MENU_SHARE:
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
|
||||
getText(R.string.share_map_subject));
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, String.format(
|
||||
getText(R.string.share_map_body_format).toString(),
|
||||
mapsList.get(contextPosition)[1],
|
||||
MyMapsConstants.MAPSHOP_BASE_URL + "?msa=0&msid="
|
||||
+ mapsList.get(contextPosition)[0]));
|
||||
startActivity(Intent.createChooser(shareIntent,
|
||||
getText(R.string.share_map).toString()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areAllItemsEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mapsList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return mapsList.get(position)[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView =
|
||||
getLayoutInflater().inflate(R.layout.listitem, parent, false);
|
||||
}
|
||||
|
||||
String[] map = mapsList.get(position);
|
||||
((TextView) convertView.findViewById(R.id.maplistitem)).setText(map[1]);
|
||||
((TextView) convertView.findViewById(R.id.maplistdesc)).setText(map[2]);
|
||||
TextView publicUnlisted =
|
||||
(TextView) convertView.findViewById(R.id.maplistpublic);
|
||||
if (publicList.get(position)) {
|
||||
publicUnlisted.setTextColor(Color.RED);
|
||||
publicUnlisted.setText(R.string.public_map);
|
||||
} else {
|
||||
publicUnlisted.setTextColor(Color.GREEN);
|
||||
publicUnlisted.setText(R.string.unlisted_map);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return mapsList.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
observerSet.add(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
observerSet.remove(observer);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
|
||||
/**
|
||||
* Metadata about a "my maps" map.
|
||||
*/
|
||||
public class MyMapsMapMetadata {
|
||||
|
||||
private String title;
|
||||
private String description;
|
||||
private String gdataEditUri;
|
||||
private boolean searchable;
|
||||
|
||||
public MyMapsMapMetadata() {
|
||||
title = "";
|
||||
description = "";
|
||||
gdataEditUri = "";
|
||||
searchable = false;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = new String(title);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = new String(description);
|
||||
}
|
||||
|
||||
public boolean getSearchable() {
|
||||
return searchable;
|
||||
}
|
||||
|
||||
public void setSearchable(boolean searchable) {
|
||||
this.searchable = searchable;
|
||||
}
|
||||
|
||||
public String getGDataEditUri() {
|
||||
return gdataEditUri;
|
||||
}
|
||||
|
||||
public void setGDataEditUri(String editUri) {
|
||||
this.gdataEditUri = new String(editUri);
|
||||
}
|
||||
}
|
||||
@@ -85,13 +85,13 @@ public class DialogManager {
|
||||
builder.setTitle("Title");
|
||||
builder.setMessage("Message");
|
||||
builder.setPositiveButton(activity.getString(R.string.ok), null);
|
||||
builder.setNeutralButton(activity.getString(R.string.share_map),
|
||||
builder.setNeutralButton(activity.getString(R.string.share_track),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
activity.shareLinkToMyMap(activity.getSendToMyMapsMapId());
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
activity.shareLinkToFusionTable(activity.sendToTrackId);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
sendToGoogleResultDialog = builder.create();
|
||||
return sendToGoogleResultDialog;
|
||||
case DIALOG_WRITE_PROGRESS:
|
||||
@@ -119,9 +119,9 @@ public class DialogManager {
|
||||
sendToGoogleResultDialog.setIcon(success
|
||||
? android.R.drawable.ic_dialog_info
|
||||
: android.R.drawable.ic_dialog_alert);
|
||||
sendToGoogleResultDialog.setMessage(activity.getMapsResultMessage());
|
||||
sendToGoogleResultDialog.setMessage(activity.getFusionTablesResultMessage());
|
||||
|
||||
boolean canShare = activity.getSendToMyMapsMapId() != null;
|
||||
boolean canShare = activity.getSendToFusionTablesTableId() != null;
|
||||
View share =
|
||||
sendToGoogleResultDialog.findViewById(android.R.id.button3);
|
||||
if (share != null) {
|
||||
|
||||
@@ -21,8 +21,6 @@ import static com.google.android.apps.mytracks.DialogManager.DIALOG_SEND_TO_GOOG
|
||||
import static com.google.android.apps.mytracks.DialogManager.DIALOG_WRITE_PROGRESS;
|
||||
|
||||
import com.google.android.accounts.Account;
|
||||
import com.google.android.apps.mymaps.MyMapsConstants;
|
||||
import com.google.android.apps.mymaps.MyMapsList;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
@@ -30,11 +28,11 @@ import com.google.android.apps.mytracks.io.AuthManager;
|
||||
import com.google.android.apps.mytracks.io.AuthManagerFactory;
|
||||
import com.google.android.apps.mytracks.io.GpxImporter;
|
||||
import com.google.android.apps.mytracks.io.SendToDocs;
|
||||
import com.google.android.apps.mytracks.io.SendToMyMaps;
|
||||
import com.google.android.apps.mytracks.io.SendToFusionTables;
|
||||
import com.google.android.apps.mytracks.io.TempFileCleaner;
|
||||
import com.google.android.apps.mytracks.io.TrackWriter;
|
||||
import com.google.android.apps.mytracks.io.TrackWriterFactory;
|
||||
import com.google.android.apps.mytracks.io.SendToMyMaps.OnSendCompletedListener;
|
||||
import com.google.android.apps.mytracks.io.SendToFusionTables.OnSendCompletedListener;
|
||||
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||
@@ -93,7 +91,6 @@ import org.xml.sax.SAXException;
|
||||
*/
|
||||
public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
OnSharedPreferenceChangeListener, ProgressIndicator {
|
||||
|
||||
/**
|
||||
* Singleton instance
|
||||
*/
|
||||
@@ -124,10 +121,11 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
* Used by SendToGoogleResultDialog.
|
||||
*/
|
||||
|
||||
public boolean sendToMyMapsSuccess = false;
|
||||
public long sendToTrackId = -1;
|
||||
public boolean sendToFusionTablesSuccess = false;
|
||||
public boolean sendToDocsSuccess = false;
|
||||
public String sendToMyMapsMapId;
|
||||
public String sendToMyMapsMessage = "";
|
||||
public String sendToFusionTablesTableId;
|
||||
public String sendToFusionTablesMessage = "";
|
||||
public String sendToDocsMessage = "";
|
||||
|
||||
/**
|
||||
@@ -148,9 +146,9 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
*/
|
||||
private long selectedTrackId = -1;
|
||||
|
||||
/**
|
||||
* Does the user want to share the current track.
|
||||
*/
|
||||
/**
|
||||
* Does the user want to share the current track.
|
||||
*/
|
||||
private boolean shareRequested = false;
|
||||
|
||||
/**
|
||||
@@ -451,13 +449,13 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
case MyTracksConstants.GET_MAP: {
|
||||
if (resultCode == RESULT_OK) {
|
||||
results.putExtra("trackid", selectedTrackId);
|
||||
if (results.hasExtra("mapid")) {
|
||||
sendToMyMapsMapId = results.getStringExtra("mapid");
|
||||
if (results.hasExtra("tableid")) {
|
||||
sendToFusionTablesTableId = results.getStringExtra("tableid");
|
||||
}
|
||||
setProgressMessage(
|
||||
R.string.progress_message_authenticating_mymaps);
|
||||
R.string.progress_message_authenticating_fusiontables);
|
||||
authenticate(results, MyTracksConstants.SEND_TO_GOOGLE,
|
||||
MyMapsConstants.MAPSHOP_SERVICE);
|
||||
SendToFusionTables.SERVICE_ID);
|
||||
} else {
|
||||
dialogManager.dismissDialogSafely(DIALOG_PROGRESS);
|
||||
}
|
||||
@@ -517,13 +515,14 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MyTracksConstants.AUTHENTICATE_TO_DOCS: {
|
||||
case MyTracksConstants.AUTHENTICATE_TO_DOCLIST: {
|
||||
if (resultCode == RESULT_OK) {
|
||||
setProgressValue(0);
|
||||
setProgressMessage(
|
||||
R.string.progress_message_authenticating_docs);
|
||||
authenticate(results,
|
||||
MyTracksConstants.AUTHENTICATE_TO_TRIX, "writely");
|
||||
MyTracksConstants.AUTHENTICATE_TO_TRIX,
|
||||
SendToDocs.GDATA_SERVICE_NAME_DOCLIST);
|
||||
} else {
|
||||
dialogManager.dismissDialogSafely(DIALOG_PROGRESS);
|
||||
}
|
||||
@@ -534,7 +533,8 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
setProgressValue(30);
|
||||
setProgressMessage(
|
||||
R.string.progress_message_authenticating_docs);
|
||||
authenticate(results, MyTracksConstants.SEND_TO_DOCS, "wise");
|
||||
authenticate(results, MyTracksConstants.SEND_TO_DOCS,
|
||||
SendToDocs.GDATA_SERVICE_NAME_TRIX);
|
||||
} else {
|
||||
dialogManager.dismissDialogSafely(DIALOG_PROGRESS);
|
||||
}
|
||||
@@ -546,8 +546,10 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
setProgressValue(50);
|
||||
setProgressMessage(R.string.progress_message_sending_docs);
|
||||
final long trackId = results.getLongExtra("trackid", selectedTrackId);
|
||||
final SendToDocs sender = new SendToDocs(this, authMap.get("wise"),
|
||||
authMap.get("writely"), trackId);
|
||||
final SendToDocs sender = new SendToDocs(this,
|
||||
authMap.get(SendToDocs.GDATA_SERVICE_NAME_TRIX),
|
||||
authMap.get(SendToDocs.GDATA_SERVICE_NAME_DOCLIST), trackId);
|
||||
sendToTrackId = trackId;
|
||||
Runnable onCompletion = new Runnable() {
|
||||
public void run() {
|
||||
setProgressValue(100);
|
||||
@@ -556,7 +558,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
public void run() {
|
||||
sendToDocsMessage = sender.getStatusMessage();
|
||||
sendToDocsSuccess = sender.wasSuccess();
|
||||
handleMapsFinish();
|
||||
handleFusionTablesFinish(trackId);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -569,61 +571,55 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
break;
|
||||
}
|
||||
case MyTracksConstants.SEND_TO_GOOGLE_DIALOG: {
|
||||
shareRequested = false;
|
||||
shareRequested = false;
|
||||
dialogManager.showDialogSafely(DIALOG_SEND_TO_GOOGLE);
|
||||
break;
|
||||
}
|
||||
case MyTracksConstants.SEND_TO_GOOGLE: {
|
||||
if (results != null && resultCode == RESULT_OK) {
|
||||
final String mapId;
|
||||
final long trackId;
|
||||
if (results.hasExtra("mapid")) {
|
||||
mapId = results.getStringExtra("mapid");
|
||||
} else {
|
||||
mapId = "new";
|
||||
}
|
||||
if (results.hasExtra("trackid")) {
|
||||
trackId = results.getLongExtra("trackid", -1);
|
||||
} else {
|
||||
trackId = selectedTrackId;
|
||||
}
|
||||
|
||||
OnSendCompletedListener onCompletion = new OnSendCompletedListener() {
|
||||
@Override
|
||||
public void onSendCompleted(String mapId, boolean success,
|
||||
public void onSendCompleted(String tableId, boolean success,
|
||||
int statusMessage) {
|
||||
sendToMyMapsMessage = getString(statusMessage);
|
||||
sendToMyMapsSuccess = success;
|
||||
if (sendToMyMapsSuccess) {
|
||||
sendToMyMapsMapId = mapId;
|
||||
// Update the map id for this track:
|
||||
sendToFusionTablesMessage = getString(statusMessage);
|
||||
sendToFusionTablesSuccess = success;
|
||||
if (sendToFusionTablesSuccess) {
|
||||
sendToFusionTablesTableId = tableId;
|
||||
// Update the table id for this track:
|
||||
try {
|
||||
Track track = providerUtils.getTrack(trackId);
|
||||
track.setMapId(mapId);
|
||||
track.setTableId(tableId);
|
||||
providerUtils.updateTrack(track);
|
||||
} catch (RuntimeException e) {
|
||||
// If that fails whatever reasons we'll just log an error, but
|
||||
// continue.
|
||||
Log.w(MyTracksConstants.TAG, "Updating map id failed.", e);
|
||||
Log.w(MyTracksConstants.TAG, "Updating table id failed.", e);
|
||||
}
|
||||
}
|
||||
if (dialogManager.getSendToGoogleDialog().getSendToDocs()) {
|
||||
onActivityResult(MyTracksConstants.AUTHENTICATE_TO_DOCS,
|
||||
onActivityResult(MyTracksConstants.AUTHENTICATE_TO_DOCLIST,
|
||||
RESULT_OK, new Intent());
|
||||
} else {
|
||||
dialogManager.dismissDialogSafely(DIALOG_PROGRESS);
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
handleMapsFinish();
|
||||
handleFusionTablesFinish(trackId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
final SendToMyMaps sender = new SendToMyMaps(this, mapId, auth,
|
||||
sendToTrackId = trackId;
|
||||
final SendToFusionTables sender = new SendToFusionTables(this, auth,
|
||||
trackId, this/*progressIndicator*/, onCompletion);
|
||||
|
||||
HandlerThread handlerThread = new HandlerThread("SendToMyMaps");
|
||||
HandlerThread handlerThread = new HandlerThread("SendToFusionTables");
|
||||
handlerThread.start();
|
||||
Handler handler = new Handler(handlerThread.getLooper());
|
||||
handler.post(sender);
|
||||
@@ -654,8 +650,8 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
case MyTracksConstants.SHARE_LINK: {
|
||||
Track selectedTrack = providerUtils.getTrack(selectedTrackId);
|
||||
if (selectedTrack != null) {
|
||||
if (selectedTrack.getMapId().length() > 0) {
|
||||
shareLinkToMyMap(selectedTrack.getMapId());
|
||||
if (selectedTrack.getTableId().length() > 0) {
|
||||
shareLinkToFusionTable(selectedTrackId);
|
||||
} else {
|
||||
shareRequested = true;
|
||||
dialogManager.showDialogSafely(DIALOG_SEND_TO_GOOGLE);
|
||||
@@ -724,11 +720,11 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
* Resets status information for sending to MyMaps/Docs.
|
||||
*/
|
||||
public void resetSendToGoogleStatus() {
|
||||
sendToMyMapsMessage = "";
|
||||
sendToMyMapsSuccess = true;
|
||||
sendToFusionTablesMessage = "";
|
||||
sendToFusionTablesSuccess = true;
|
||||
sendToDocsMessage = "";
|
||||
sendToDocsSuccess = true;
|
||||
sendToMyMapsMapId = null;
|
||||
sendToFusionTablesTableId = null;
|
||||
}
|
||||
|
||||
private void importGpxFile(final String fileName) {
|
||||
@@ -796,10 +792,10 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
}
|
||||
|
||||
/**
|
||||
* Shares a link to a My Map via external app (email, gmail, ...)
|
||||
* Shares a link to a fusion table via external app (email, gmail, ...)
|
||||
* A chooser with apps that support text/plain will be shown to the user.
|
||||
*/
|
||||
public void shareLinkToMyMap(String mapId) {
|
||||
public void shareLinkToFusionTable(long trackId) {
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
|
||||
@@ -811,7 +807,8 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
getString(R.string.share_url_only_key), false);
|
||||
}
|
||||
|
||||
String url = MyMapsConstants.MAPSHOP_BASE_URL + "?msa=0&msid=" + mapId;
|
||||
Track track = providerUtils.getTrack(trackId);
|
||||
String url = SendToFusionTables.getMapVisualizationUrl(track);
|
||||
String msg = shareUrlOnly ? url : String.format(
|
||||
getResources().getText(R.string.share_map_body_format).toString(), url);
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, msg);
|
||||
@@ -930,7 +927,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
service);
|
||||
authMap.put(service, auth);
|
||||
}
|
||||
Log.d(MyTracksConstants.TAG, "Loggin in to " + service + "...");
|
||||
Log.d(MyTracksConstants.TAG, "Logging in to " + service + "...");
|
||||
if (AuthManagerFactory.useModernAuthManager()) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@@ -1040,19 +1037,13 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
setProgressValue(0);
|
||||
clearProgressMessage();
|
||||
dialogManager.showDialogSafely(DIALOG_PROGRESS);
|
||||
if (sendToGoogleDialog.getSendToMyMaps()) {
|
||||
if (!sendToGoogleDialog.getCreateNewMap()) {
|
||||
Intent listIntent = new Intent(this, MyMapsList.class);
|
||||
startActivityForResult(listIntent, MyTracksConstants.GET_MAP);
|
||||
} else {
|
||||
setProgressValue(0);
|
||||
setProgressMessage(
|
||||
R.string.progress_message_authenticating_mymaps);
|
||||
authenticate(new Intent(), MyTracksConstants.SEND_TO_GOOGLE,
|
||||
MyMapsConstants.MAPSHOP_SERVICE);
|
||||
}
|
||||
if (sendToGoogleDialog.getSendToFusionTables()) {
|
||||
setProgressValue(0);
|
||||
setProgressMessage(R.string.progress_message_authenticating_fusiontables);
|
||||
authenticate(new Intent(), MyTracksConstants.SEND_TO_GOOGLE,
|
||||
SendToFusionTables.SERVICE_ID);
|
||||
} else {
|
||||
onActivityResult(MyTracksConstants.AUTHENTICATE_TO_DOCS, RESULT_OK,
|
||||
onActivityResult(MyTracksConstants.AUTHENTICATE_TO_DOCLIST, RESULT_OK,
|
||||
new Intent());
|
||||
}
|
||||
}
|
||||
@@ -1167,25 +1158,24 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that uploading to maps is finished.
|
||||
* Notifies that uploading to fusion tables is finished.
|
||||
*/
|
||||
private void handleMapsFinish() {
|
||||
if (shareRequested && sendToMyMapsSuccess) {
|
||||
private void handleFusionTablesFinish(long trackId) {
|
||||
if (shareRequested && sendToFusionTablesSuccess) {
|
||||
// Just share
|
||||
Toast.makeText(this, getMapsResultMessage(), Toast.LENGTH_LONG).show();
|
||||
shareLinkToMyMap(sendToMyMapsMapId);
|
||||
Toast.makeText(this, getFusionTablesResultMessage(), Toast.LENGTH_LONG).show();
|
||||
shareLinkToFusionTable(trackId);
|
||||
} else {
|
||||
dialogManager.showDialogSafely(
|
||||
DialogManager.DIALOG_SEND_TO_GOOGLE_RESULT);
|
||||
dialogManager.showDialogSafely(DialogManager.DIALOG_SEND_TO_GOOGLE_RESULT);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMapsResultMessage() {
|
||||
public String getFusionTablesResultMessage() {
|
||||
StringBuilder message = new StringBuilder();
|
||||
SendToGoogleDialog sendToGoogleDialog =
|
||||
dialogManager.getSendToGoogleDialog();
|
||||
if (sendToGoogleDialog.getSendToMyMaps()) {
|
||||
message.append(sendToMyMapsMessage);
|
||||
if (sendToGoogleDialog.getSendToFusionTables()) {
|
||||
message.append(sendToFusionTablesMessage);
|
||||
}
|
||||
if (sendToGoogleDialog.getSendToDocs()) {
|
||||
if (message.length() > 0) {
|
||||
@@ -1193,10 +1183,6 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
}
|
||||
message.append(sendToDocsMessage);
|
||||
}
|
||||
if (sendToMyMapsSuccess && sendToDocsSuccess) {
|
||||
message.append(' ');
|
||||
message.append(getString(R.string.status_mymap_info));
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
@@ -1216,12 +1202,12 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
||||
return dialogManager;
|
||||
}
|
||||
|
||||
public String getSendToMyMapsMapId() {
|
||||
return sendToMyMapsMapId;
|
||||
public String getSendToFusionTablesTableId() {
|
||||
return sendToFusionTablesTableId;
|
||||
}
|
||||
|
||||
public boolean getSendToGoogleSuccess() {
|
||||
return sendToMyMapsSuccess && sendToDocsSuccess;
|
||||
return sendToFusionTablesSuccess && sendToDocsSuccess;
|
||||
}
|
||||
|
||||
// @VisibleForTesting
|
||||
|
||||
@@ -50,7 +50,7 @@ public abstract class MyTracksConstants {
|
||||
public static final int FEATURE_DETAILS = 5;
|
||||
public static final int START_RECORDING = 6;
|
||||
public static final int STOP_RECORDING = 7;
|
||||
public static final int AUTHENTICATE_TO_DOCS = 8;
|
||||
public static final int AUTHENTICATE_TO_DOCLIST = 8;
|
||||
public static final int AUTHENTICATE_TO_TRIX = 9;
|
||||
public static final int SEND_TO_DOCS = 10;
|
||||
public static final int DELETE_TRACK = 11;
|
||||
@@ -213,6 +213,9 @@ public abstract class MyTracksConstants {
|
||||
}
|
||||
}
|
||||
|
||||
public static final String MAPSHOP_BASE_URL =
|
||||
"http://maps.google.com/maps/ms";
|
||||
|
||||
/**
|
||||
* This is an abstract utility class.
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mymaps.GeoRect;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
||||
@@ -24,6 +23,7 @@ import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.apps.mytracks.util.GeoRect;
|
||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.android.maps.MapActivity;
|
||||
@@ -789,7 +789,7 @@ public class MyTracksMap extends MapActivity
|
||||
SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0,
|
||||
R.string.tracklist_share_track);
|
||||
share.add(0, MyTracksConstants.MENU_SHARE_LINK, 0,
|
||||
R.string.tracklist_share_link);
|
||||
R.string.tracklist_share_linkfusion);
|
||||
share.add(0, MyTracksConstants.MENU_SHARE_GPX_FILE, 0,
|
||||
R.string.tracklist_share_gpx_file);
|
||||
share.add(0, MyTracksConstants.MENU_SHARE_KML_FILE, 0,
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
/**
|
||||
* A dialog where the user can choose where to send the tracks to, i.e.
|
||||
@@ -39,10 +38,7 @@ import android.widget.RadioGroup;
|
||||
*/
|
||||
public class SendToGoogleDialog extends Dialog {
|
||||
|
||||
private RadioGroup groupMyMaps;
|
||||
private RadioButton createNewMapRadioButton;
|
||||
private RadioButton pickMapRadioButton;
|
||||
private CheckBox sendToMyMapsCheckBox;
|
||||
private CheckBox sendToFusionTablesCheckBox;
|
||||
private CheckBox sendToDocsCheckBox;
|
||||
private RadioButton sendStatsRadioButton;
|
||||
private RadioButton sendStatsAndPointsRadioButton;
|
||||
@@ -76,26 +72,19 @@ public class SendToGoogleDialog extends Dialog {
|
||||
OnCheckedChangeListener checkBoxListener = new OnCheckedChangeListener() {
|
||||
public void onCheckedChanged(CompoundButton button, boolean checked) {
|
||||
sendButton.setEnabled(
|
||||
sendToMyMapsCheckBox.isChecked() ||
|
||||
sendToFusionTablesCheckBox.isChecked() ||
|
||||
sendToDocsCheckBox.isChecked());
|
||||
groupMyMaps.setVisibility(
|
||||
sendToMyMapsCheckBox.isChecked() ? View.VISIBLE : View.INVISIBLE);
|
||||
//groupDocs.setVisibility(
|
||||
// sendToDocsCheckBox.isChecked() ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
};
|
||||
|
||||
sendButton = (Button) findViewById(R.id.sendtogoogle_send_now);
|
||||
groupMyMaps = (RadioGroup) findViewById(R.id.sendtogoogle_group_mymaps);
|
||||
sendToMyMapsCheckBox =
|
||||
(CheckBox) findViewById(R.id.sendtogoogle_google_mymaps);
|
||||
sendToMyMapsCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
sendToFusionTablesCheckBox =
|
||||
(CheckBox) findViewById(R.id.sendtogoogle_google_fusiontables);
|
||||
sendToFusionTablesCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
sendToDocsCheckBox = (CheckBox) findViewById(R.id.sendtogoogle_google_docs);
|
||||
sendToDocsCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
createNewMapRadioButton =
|
||||
(RadioButton) findViewById(R.id.sendtogoogle_create_new_map);
|
||||
pickMapRadioButton =
|
||||
(RadioButton) findViewById(R.id.sendtogoogle_pick_existing_map);
|
||||
sendStatsRadioButton =
|
||||
(RadioButton) findViewById(R.id.sendtogoogle_send_stats);
|
||||
sendStatsAndPointsRadioButton = (RadioButton) findViewById(
|
||||
@@ -104,18 +93,12 @@ public class SendToGoogleDialog extends Dialog {
|
||||
SharedPreferences prefs =
|
||||
getContext().getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||
if (prefs != null) {
|
||||
sendToMyMapsCheckBox.setChecked(
|
||||
sendToFusionTablesCheckBox.setChecked(
|
||||
prefs.getBoolean(
|
||||
getContext().getString(R.string.send_to_my_maps_key), true));
|
||||
sendToDocsCheckBox.setChecked(
|
||||
prefs.getBoolean(
|
||||
getContext().getString(R.string.send_to_docs_key), true));
|
||||
pickMapRadioButton.setChecked(
|
||||
prefs.getBoolean(
|
||||
getContext().getString(R.string.pick_existing_map_key), false));
|
||||
createNewMapRadioButton.setChecked(
|
||||
!prefs.getBoolean(
|
||||
getContext().getString(R.string.pick_existing_map_key), false));
|
||||
sendStatsAndPointsRadioButton.setChecked(
|
||||
prefs.getBoolean(
|
||||
getContext().getString(R.string.send_stats_and_points_key),
|
||||
@@ -136,13 +119,10 @@ public class SendToGoogleDialog extends Dialog {
|
||||
if (editor != null) {
|
||||
editor.putBoolean(
|
||||
getContext().getString(R.string.send_to_my_maps_key),
|
||||
sendToMyMapsCheckBox.isChecked());
|
||||
sendToFusionTablesCheckBox.isChecked());
|
||||
editor.putBoolean(
|
||||
getContext().getString(R.string.send_to_docs_key),
|
||||
sendToDocsCheckBox.isChecked());
|
||||
editor.putBoolean(
|
||||
getContext().getString(R.string.pick_existing_map_key),
|
||||
pickMapRadioButton.isChecked());
|
||||
editor.putBoolean(
|
||||
getContext().getString(R.string.send_stats_and_points_key),
|
||||
sendStatsAndPointsRadioButton.isChecked());
|
||||
@@ -152,18 +132,14 @@ public class SendToGoogleDialog extends Dialog {
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
public boolean getSendToMyMaps() {
|
||||
return sendToMyMapsCheckBox.isChecked();
|
||||
public boolean getSendToFusionTables() {
|
||||
return sendToFusionTablesCheckBox.isChecked();
|
||||
}
|
||||
|
||||
public boolean getSendToDocs() {
|
||||
return sendToDocsCheckBox.isChecked();
|
||||
}
|
||||
|
||||
public boolean getCreateNewMap() {
|
||||
return createNewMapRadioButton.isChecked();
|
||||
}
|
||||
|
||||
public boolean getSendStatsAndPoints() {
|
||||
return sendStatsAndPointsRadioButton.isChecked();
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ public class StatsActivity extends Activity
|
||||
super.onCreateOptionsMenu(menu);
|
||||
MenuItem currentSegment = menu.add(0,
|
||||
MyTracksConstants.MENU_CURRENT_SEGMENT, 0, R.string.current_segment);
|
||||
currentSegment.setIcon(R.drawable.menu_by_distance);
|
||||
currentSegment.setIcon(R.drawable.ic_menu_lastsegment);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,16 +15,13 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@@ -84,16 +81,7 @@ public class WelcomeActivity extends Activity {
|
||||
builder.setIcon(R.drawable.arrow_icon);
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
// Get the version from the manifest.
|
||||
try {
|
||||
PackageInfo pi =
|
||||
getPackageManager().getPackageInfo("com.google.android.maps.mytracks",
|
||||
PackageManager.GET_META_DATA);
|
||||
((TextView) dialog.findViewById(R.id.about_version_register)).
|
||||
setText(pi.versionName);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(MyTracksConstants.TAG, "Failed to get version info.", e);
|
||||
}
|
||||
((TextView) dialog.findViewById(R.id.about_version_register)).
|
||||
setText(MyTracksUtils.getMyTracksVersion(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,8 @@ public class MyTracksProvider extends ContentProvider {
|
||||
+ TracksColumns.ELEVATIONGAIN + " FLOAT, "
|
||||
+ TracksColumns.MINGRADE + " FLOAT, "
|
||||
+ TracksColumns.MAXGRADE + " FLOAT, "
|
||||
+ TracksColumns.MAPID + " STRING);");
|
||||
+ TracksColumns.MAPID + " STRING, "
|
||||
+ TracksColumns.TABLEID + " STRING);");
|
||||
db.execSQL("CREATE TABLE " + WAYPOINTS_TABLE + " ("
|
||||
+ WaypointsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ WaypointsColumns.NAME + " STRING, "
|
||||
@@ -146,13 +147,15 @@ public class MyTracksProvider extends ContentProvider {
|
||||
} else {
|
||||
// Incremental updates go here.
|
||||
// Each time you increase the DB version, add a corresponding if clause.
|
||||
|
||||
|
||||
// Sensor data.
|
||||
if (oldVersion <= 17) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", adding sensor column.");
|
||||
+ newVersion + ", adding sensor and tableid columns.");
|
||||
db.execSQL("ALTER TABLE " + TRACKPOINTS_TABLE
|
||||
+ " ADD " + TrackPointsColumns.SENSOR + " BLOB");
|
||||
db.execSQL("ALTER TABLE " + TRACKS_TABLE
|
||||
+ " ADD " + TracksColumns.TABLEID + " STRING");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.location.Location;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
@@ -58,8 +59,8 @@ public class GpxTrackWriter implements TrackFormatWriter {
|
||||
@Override
|
||||
public void writeHeader() {
|
||||
if (pw != null) {
|
||||
pw.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ");
|
||||
pw.println("standalone=\"yes\"?>");
|
||||
pw.format("<?xml version=\"1.0\" encoding=\"%s\" standalone=\"yes\"?>\n",
|
||||
Charset.defaultCharset().name());
|
||||
pw.println("<?xml-stylesheet type=\"text/xsl\" href=\"details.xsl\"?>");
|
||||
pw.println("<gpx");
|
||||
pw.println(" version=\"1.0\"");
|
||||
|
||||
@@ -15,32 +15,21 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mymaps.MyMapsConstants;
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.docs.DocsHelper;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper.QueryFunction;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.ResourceUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.wireless.gdata.client.GDataClient;
|
||||
import com.google.wireless.gdata.client.GDataServiceClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.data.Entry;
|
||||
import com.google.wireless.gdata.docs.DocumentsClient;
|
||||
import com.google.wireless.gdata.docs.SpreadsheetsClient;
|
||||
import com.google.wireless.gdata.docs.SpreadsheetsClient.WorksheetEntry;
|
||||
import com.google.wireless.gdata.maps.xml.XmlMapsGDataParserFactory;
|
||||
import com.google.wireless.gdata.parser.GDataParser;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
import com.google.wireless.gdata2.client.AuthenticationException;
|
||||
import com.google.wireless.gdata.client.GDataServiceClient;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -48,16 +37,7 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
/**
|
||||
* A helper class used to transmit tracks statistics to Google Docs/Trix.
|
||||
@@ -65,29 +45,15 @@ import java.text.NumberFormat;
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SendToDocs {
|
||||
private static final NumberFormat LARGE_UNIT_FORMAT =
|
||||
new DecimalFormat("#,###,###.00");
|
||||
private static final NumberFormat SMALL_UNIT_FORMAT =
|
||||
new DecimalFormat("###,###");
|
||||
private static final String DOCS_FEED_URL =
|
||||
"http://docs.google.com/feeds/documents/private/full";
|
||||
private static final String DOCS_MY_SPREADSHEETS_FEED_URL =
|
||||
"http://docs.google.com/feeds/documents/private/full?"
|
||||
+ "category=mine,spreadsheet";
|
||||
private static final String DOCS_SPREADSHEET_URL =
|
||||
"http://docs.google.com/feeds/documents/private/full/spreadsheet%3A";
|
||||
private static final String DOCS_SPREADSHEET_URL_FORMAT =
|
||||
"http://spreadsheets.google.com/feeds/list/%s/%s/private/full";
|
||||
protected static final String DOCS_WORKSHEETS_URL_FORMAT =
|
||||
"http://spreadsheets.google.com/feeds/worksheets/%s/private/full";
|
||||
private static final String CONTENT_TYPE_PARAM = "Content-Type";
|
||||
private static final String ATOM_FEED_MIME_TYPE = "application/atom+xml";
|
||||
private static final String OPENDOCUMENT_SPREADSHEET_MIME_TYPE =
|
||||
"application/x-vnd.oasis.opendocument.spreadsheet";
|
||||
|
||||
/** The GData service name for Google Spreadsheets (aka Trix) */
|
||||
public static final String GDATA_SERVICE_NAME_TRIX = "wise";
|
||||
|
||||
/** The GData service name for the Google Docs Document List */
|
||||
public static final String GDATA_SERVICE_NAME_DOCLIST = "writely";
|
||||
|
||||
private final Activity activity;
|
||||
private final AuthManager wiseAuth;
|
||||
private final AuthManager writelyAuth;
|
||||
private final AuthManager trixAuth;
|
||||
private final AuthManager docListAuth;
|
||||
private final long trackId;
|
||||
private final boolean metricUnits;
|
||||
private final HandlerThread handlerThread;
|
||||
@@ -95,18 +61,15 @@ public class SendToDocs {
|
||||
|
||||
private boolean createdNewSpreadSheet = false;
|
||||
|
||||
private String spreadSheetId = null;
|
||||
private String workSheetId = null;
|
||||
|
||||
private boolean success = true;
|
||||
private String statusMessage = "";
|
||||
private Runnable onCompletion = null;
|
||||
|
||||
public SendToDocs(Activity activity, AuthManager wiseAuth,
|
||||
AuthManager writelyAuth, long trackId) {
|
||||
public SendToDocs(Activity activity, AuthManager trixAuth,
|
||||
AuthManager docListAuth, long trackId) {
|
||||
this.activity = activity;
|
||||
this.wiseAuth = wiseAuth;
|
||||
this.writelyAuth = writelyAuth;
|
||||
this.trixAuth = trixAuth;
|
||||
this.docListAuth = docListAuth;
|
||||
this.trackId = trackId;
|
||||
|
||||
SharedPreferences preferences = activity.getSharedPreferences(
|
||||
@@ -196,22 +159,24 @@ public class SendToDocs {
|
||||
* @param track the track
|
||||
*/
|
||||
private boolean uploadToDocs(Track track) {
|
||||
GDataWrapper wiseWrapper = new GDataWrapper();
|
||||
wiseWrapper.setAuthManager(wiseAuth);
|
||||
wiseWrapper.setRetryOnAuthFailure(true);
|
||||
GDataWrapper<GDataServiceClient> docListWrapper = new GDataWrapper<GDataServiceClient>();
|
||||
docListWrapper.setAuthManager(docListAuth);
|
||||
docListWrapper.setRetryOnAuthFailure(true);
|
||||
|
||||
GDataWrapper<GDataServiceClient> trixWrapper = new GDataWrapper<GDataServiceClient>();
|
||||
trixWrapper.setAuthManager(trixAuth);
|
||||
trixWrapper.setRetryOnAuthFailure(true);
|
||||
|
||||
GDataWrapper writelyWrapper = new GDataWrapper();
|
||||
writelyWrapper.setAuthManager(writelyAuth);
|
||||
writelyWrapper.setRetryOnAuthFailure(true);
|
||||
DocsHelper docsHelper = new DocsHelper();
|
||||
|
||||
GDataClient androidClient = null;
|
||||
try {
|
||||
androidClient = GDataClientFactory.getGDataClient(activity);
|
||||
SpreadsheetsClient gdataClient = new SpreadsheetsClient(androidClient,
|
||||
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||
wiseWrapper.setClient(gdataClient);
|
||||
trixWrapper.setClient(gdataClient);
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"GData connection prepared: " + this.writelyAuth);
|
||||
"GData connection prepared: " + this.docListAuth);
|
||||
String sheetTitle = "My Tracks";
|
||||
|
||||
if (track.getCategory() != null && !track.getCategory().equals("")) {
|
||||
@@ -220,15 +185,19 @@ public class SendToDocs {
|
||||
|
||||
DocumentsClient docsGdataClient = new DocumentsClient(androidClient,
|
||||
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||
writelyWrapper.setClient(docsGdataClient);
|
||||
docListWrapper.setClient(docsGdataClient);
|
||||
|
||||
// First try to find the spreadsheet:
|
||||
if (!getSpreadsheetId(writelyWrapper, sheetTitle)) {
|
||||
Log.i(MyTracksConstants.TAG, "Spreadsheet lookup failed.");
|
||||
String spreadsheetId = null;
|
||||
try {
|
||||
spreadsheetId = docsHelper.requestSpreadsheetId(docListWrapper,
|
||||
sheetTitle);
|
||||
} catch (IOException e) {
|
||||
Log.i(MyTracksConstants.TAG, "Spreadsheet lookup failed.", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spreadSheetId == null) {
|
||||
if (spreadsheetId == null) {
|
||||
MyTracks.getInstance().setProgressValue(65);
|
||||
// Waiting a few seconds and trying again. Maybe the server just had a
|
||||
// hickup (unfortunately that happens quite a lot...).
|
||||
@@ -237,22 +206,33 @@ public class SendToDocs {
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Sleep interrupted", e);
|
||||
}
|
||||
if (!getSpreadsheetId(writelyWrapper, sheetTitle)) {
|
||||
Log.i(MyTracksConstants.TAG, "2nd spreadsheet lookup failed.");
|
||||
|
||||
try {
|
||||
spreadsheetId = docsHelper.requestSpreadsheetId(docListWrapper,
|
||||
sheetTitle);
|
||||
} catch (IOException e) {
|
||||
Log.i(MyTracksConstants.TAG, "2nd spreadsheet lookup failed.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We were unable to find an existing spreadsheet, so create a new one.
|
||||
MyTracks.getInstance().setProgressValue(70);
|
||||
if (spreadSheetId == null) {
|
||||
if (spreadsheetId == null) {
|
||||
Log.i(MyTracksConstants.TAG, "Creating new spreadsheet: " + sheetTitle);
|
||||
|
||||
if (!createSpreadSheet(writelyWrapper, sheetTitle)) {
|
||||
try {
|
||||
spreadsheetId = docsHelper.createSpreadsheet(activity, docListWrapper,
|
||||
sheetTitle);
|
||||
} catch (IOException e) {
|
||||
Log.i(MyTracksConstants.TAG, "Failed to create new spreadsheet "
|
||||
+ sheetTitle, e);
|
||||
return false;
|
||||
}
|
||||
MyTracks.getInstance().setProgressValue(80);
|
||||
createdNewSpreadSheet = true;
|
||||
|
||||
if (spreadSheetId == null) {
|
||||
if (spreadsheetId == null) {
|
||||
MyTracks.getInstance().setProgressValue(85);
|
||||
// The previous creation might have succeeded even though GData
|
||||
// reported an error. Seems to be a know bug,
|
||||
@@ -265,10 +245,16 @@ public class SendToDocs {
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Sleep interrupted", e);
|
||||
}
|
||||
if (!getSpreadsheetId(writelyWrapper, sheetTitle)) {
|
||||
|
||||
try {
|
||||
spreadsheetId = docsHelper.requestSpreadsheetId(docListWrapper,
|
||||
sheetTitle);
|
||||
} catch (IOException e) {
|
||||
Log.i(MyTracksConstants.TAG, "Failed create-failed lookup", e);
|
||||
return false;
|
||||
}
|
||||
if (spreadSheetId == null) {
|
||||
|
||||
if (spreadsheetId == null) {
|
||||
MyTracks.getInstance().setProgressValue(87);
|
||||
// Re-try
|
||||
try {
|
||||
@@ -276,11 +262,16 @@ public class SendToDocs {
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Sleep interrupted", e);
|
||||
}
|
||||
if (!getSpreadsheetId(writelyWrapper, sheetTitle)) {
|
||||
|
||||
try {
|
||||
spreadsheetId = docsHelper.requestSpreadsheetId(docListWrapper,
|
||||
sheetTitle);
|
||||
} catch (IOException e) {
|
||||
Log.i(MyTracksConstants.TAG, "Failed create-failed relookup", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (spreadSheetId == null) {
|
||||
if (spreadsheetId == null) {
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Creating new spreadsheet really failed.");
|
||||
return false;
|
||||
@@ -288,14 +279,21 @@ public class SendToDocs {
|
||||
}
|
||||
}
|
||||
|
||||
if (!getWorkSheetId(wiseWrapper)) {
|
||||
Log.i(MyTracksConstants.TAG, "Looking up worksheet id failed.");
|
||||
String worksheetId = null;
|
||||
try {
|
||||
worksheetId = docsHelper.getWorksheetId(trixWrapper, spreadsheetId);
|
||||
if (worksheetId == null) {
|
||||
throw new IOException("Worksheet ID lookup returned empty");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.i(MyTracksConstants.TAG, "Looking up worksheet id failed.", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
MyTracks.getInstance().setProgressValue(90);
|
||||
|
||||
insertRowNet(track, spreadSheetId, workSheetId);
|
||||
docsHelper.addTrackRow(activity, trixAuth, spreadsheetId, worksheetId,
|
||||
track, metricUnits);
|
||||
Log.i(MyTracksConstants.TAG, "Done uploading to docs.");
|
||||
} catch (IOException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to upload docs.", e);
|
||||
@@ -307,223 +305,4 @@ public class SendToDocs {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean getSpreadsheetId(GDataWrapper wrapper, final String title) {
|
||||
spreadSheetId = null;
|
||||
return wrapper.runQuery(new QueryFunction() {
|
||||
@Override
|
||||
public void query(GDataServiceClient client)
|
||||
throws IOException, ParseException, HttpException {
|
||||
GDataParser listParser;
|
||||
listParser = client.getParserForFeed(Entry.class,
|
||||
DOCS_MY_SPREADSHEETS_FEED_URL, writelyAuth.getAuthToken());
|
||||
listParser.init();
|
||||
while (listParser.hasMoreData()) {
|
||||
Entry entry = listParser.readNextEntry(null);
|
||||
String entryTitle = entry.getTitle();
|
||||
Log.i(MyTracksConstants.TAG, "Found docs entry: " + entryTitle);
|
||||
if (entryTitle.equals(title)) {
|
||||
String entryId = entry.getId();
|
||||
int lastSlash = entryId.lastIndexOf('/');
|
||||
spreadSheetId = entryId.substring(lastSlash + 15);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean getWorkSheetId(GDataWrapper wrapper) {
|
||||
workSheetId = null;
|
||||
return wrapper.runQuery(new QueryFunction() {
|
||||
@Override
|
||||
public void query(GDataServiceClient client)
|
||||
throws AuthenticationException, IOException, ParseException {
|
||||
String uri = String.format(DOCS_WORKSHEETS_URL_FORMAT, spreadSheetId);
|
||||
GDataParser sheetParser =
|
||||
((SpreadsheetsClient) client).getParserForWorksheetsFeed(uri,
|
||||
wiseAuth.getAuthToken());
|
||||
sheetParser.init();
|
||||
if (!sheetParser.hasMoreData()) {
|
||||
Log.i(MyTracksConstants.TAG, "Found no worksheets");
|
||||
return; // failure
|
||||
}
|
||||
|
||||
// just grab the first.
|
||||
WorksheetEntry worksheetEntry =
|
||||
(WorksheetEntry) sheetParser.readNextEntry(new WorksheetEntry());
|
||||
|
||||
int lastSlash = worksheetEntry.getId().lastIndexOf('/');
|
||||
workSheetId = worksheetEntry.getId().substring(lastSlash + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MyTracks spreadsheet with the given name.
|
||||
*/
|
||||
private boolean createSpreadSheet(GDataWrapper writelyWrapper,
|
||||
final String name) {
|
||||
spreadSheetId = null;
|
||||
|
||||
return writelyWrapper.runQuery(new QueryFunction() {
|
||||
@Override
|
||||
public void query(GDataServiceClient client) throws IOException {
|
||||
// Construct data
|
||||
// Send data
|
||||
URL url = new URL(DOCS_FEED_URL);
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.addRequestProperty(CONTENT_TYPE_PARAM,
|
||||
OPENDOCUMENT_SPREADSHEET_MIME_TYPE);
|
||||
conn.addRequestProperty("Slug", name);
|
||||
conn.addRequestProperty("Authorization",
|
||||
"GoogleLogin auth=" + writelyAuth.getAuthToken());
|
||||
conn.setDoOutput(true);
|
||||
OutputStream os = conn.getOutputStream();
|
||||
ResourceUtils.readBinaryFileToOutputStream(
|
||||
activity, R.raw.mytracks_empty_spreadsheet, os);
|
||||
|
||||
// Get the response
|
||||
// TODO: The following is a horrible ugly hack.
|
||||
// Hopefully we can retire it when there is a proper gdata api.
|
||||
BufferedReader rd = null;
|
||||
String line;
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
try {
|
||||
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
while ((line = rd.readLine()) != null) {
|
||||
resultBuilder.append(line);
|
||||
}
|
||||
os.close();
|
||||
rd.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
// The GData API sometimes throws an error, even though creation of
|
||||
// the document succeeded. In that case let's just return. The caller
|
||||
// then needs to check if the doc actually exists.
|
||||
return;
|
||||
} finally {
|
||||
os.close();
|
||||
if (rd != null) {
|
||||
rd.close();
|
||||
}
|
||||
}
|
||||
|
||||
String result = resultBuilder.toString();
|
||||
// Try to find the id.
|
||||
int idTagIndex = result.indexOf("<id>");
|
||||
if (idTagIndex == -1) {
|
||||
return;
|
||||
}
|
||||
int idTagCloseIndex = result.indexOf("</id>", idTagIndex);
|
||||
if (idTagCloseIndex == -1) {
|
||||
return;
|
||||
}
|
||||
int idStringStart = result.indexOf(DOCS_SPREADSHEET_URL, idTagIndex);
|
||||
if (idStringStart == -1) {
|
||||
return;
|
||||
}
|
||||
spreadSheetId = result.substring(
|
||||
idStringStart + DOCS_SPREADSHEET_URL.length(), idTagCloseIndex);
|
||||
Log.i(MyTracksConstants.TAG, "Created new spreadsheet: " + spreadSheetId);
|
||||
}});
|
||||
}
|
||||
|
||||
private void insertRowNet(Track track, String spreadsheetId,
|
||||
String worksheetId) throws IOException {
|
||||
String worksheetUri = String.format(DOCS_SPREADSHEET_URL_FORMAT,
|
||||
spreadsheetId, worksheetId);
|
||||
TripStatistics stats = track.getStatistics();
|
||||
|
||||
/* Prepare the Post-Text we are going to send. */
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<entry xmlns='http://www.w3.org/2005/Atom' ");
|
||||
sb.append("xmlns:gsx='http://schemas.google.com/spreadsheets/"
|
||||
+ "2006/extended'>");
|
||||
appendTag("name", track.getName(), sb);
|
||||
appendTag("description", track.getDescription(), sb);
|
||||
appendTag("date", String.format("%tc", stats.getStartTime()), sb);
|
||||
appendTag("totaltime", StringUtils.formatTimeAlwaysShowingHours(
|
||||
stats.getTotalTime()), sb);
|
||||
appendTag("movingtime", StringUtils.formatTimeAlwaysShowingHours(
|
||||
stats.getMovingTime()), sb);
|
||||
appendLargeUnitsTag("distance", stats.getTotalDistance() / 1000, sb);
|
||||
appendTag("distanceunit",
|
||||
metricUnits
|
||||
? activity.getString(R.string.kilometer)
|
||||
: activity.getString(R.string.mile),
|
||||
sb);
|
||||
appendLargeUnitsTag("averagespeed", stats.getAverageSpeed() * 3.6, sb);
|
||||
appendLargeUnitsTag("averagemovingspeed",
|
||||
stats.getAverageMovingSpeed() * 3.6, sb);
|
||||
appendLargeUnitsTag("maxspeed", stats.getMaxSpeed() * 3.6, sb);
|
||||
appendTag("speedunit",
|
||||
metricUnits
|
||||
? activity.getString(R.string.kilometer_per_hour)
|
||||
: activity.getString(R.string.mile_per_hour),
|
||||
sb);
|
||||
appendSmallUnitsTag("elevationgain", stats.getTotalElevationGain(), sb);
|
||||
appendSmallUnitsTag("minelevation", stats.getMinElevation(), sb);
|
||||
appendSmallUnitsTag("maxelevation", stats.getMaxElevation(), sb);
|
||||
appendTag("elevationunit",
|
||||
metricUnits
|
||||
? activity.getString(R.string.meter)
|
||||
: activity.getString(R.string.feet),
|
||||
sb);
|
||||
if (track.getMapId().length() > 0) {
|
||||
appendTag("map", MyMapsConstants.MAPSHOP_BASE_URL + "?msa=0&msid="
|
||||
+ track.getMapId(), sb);
|
||||
}
|
||||
sb.append("</entry>");
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Inserting at: " + spreadsheetId + " => " + worksheetUri);
|
||||
|
||||
String postText = sb.toString();
|
||||
Log.i(MyTracksConstants.TAG, postText);
|
||||
|
||||
// Send data
|
||||
// No need for a wrapper because we know that the authorization was good
|
||||
// enough to get this far
|
||||
URL url = new URL(worksheetUri);
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.addRequestProperty(CONTENT_TYPE_PARAM, ATOM_FEED_MIME_TYPE);
|
||||
conn.addRequestProperty("Authorization",
|
||||
"GoogleLogin auth=" + wiseAuth.getAuthToken());
|
||||
conn.setDoOutput(true);
|
||||
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
|
||||
wr.write(postText);
|
||||
wr.flush();
|
||||
|
||||
// Get the response
|
||||
BufferedReader rd =
|
||||
new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String line;
|
||||
while ((line = rd.readLine()) != null) {
|
||||
// Process line...
|
||||
Log.i(MyTracksConstants.TAG, "r: " + line);
|
||||
}
|
||||
wr.close();
|
||||
rd.close();
|
||||
|
||||
Log.i(MyTracksConstants.TAG, "Post finished.");
|
||||
}
|
||||
|
||||
private void appendTag(String name, String value, StringBuilder sb) {
|
||||
sb.append("<gsx:");
|
||||
sb.append(name);
|
||||
sb.append('>');
|
||||
sb.append(StringUtils.stringAsCData(value));
|
||||
sb.append("</gsx:");
|
||||
sb.append(name);
|
||||
sb.append('>');
|
||||
}
|
||||
|
||||
private void appendLargeUnitsTag(String name, double d, StringBuilder sb) {
|
||||
double value = metricUnits ? d : (d * UnitConversions.KM_TO_MI);
|
||||
appendTag(name, LARGE_UNIT_FORMAT.format(value), sb);
|
||||
}
|
||||
|
||||
private void appendSmallUnitsTag(String name, double d, StringBuilder sb) {
|
||||
double value = metricUnits ? d : (d * UnitConversions.M_TO_FT);
|
||||
appendTag(name, SMALL_UNIT_FORMAT.format(value), sb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.ProgressIndicator;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper.QueryFunction;
|
||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.api.client.googleapis.GoogleHeaders;
|
||||
import com.google.api.client.googleapis.GoogleTransport;
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.client.http.HttpResponseException;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.InputStreamContent;
|
||||
import com.google.api.client.util.Strings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* A helper class used to transmit tracks to Google Fusion Tables.
|
||||
* A new instance should be used for each upload.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class SendToFusionTables implements Runnable {
|
||||
|
||||
/**
|
||||
* Listener invoked when sending to fusion tables completes.
|
||||
*/
|
||||
public interface OnSendCompletedListener {
|
||||
void onSendCompleted(String tableId, boolean success, int statusMessage);
|
||||
}
|
||||
|
||||
/** The GData service id for Fusion Tables. */
|
||||
public static final String SERVICE_ID = "fusiontables";
|
||||
|
||||
/** The path for viewing a map visualization of a table. */
|
||||
private static final String FUSIONTABLES_MAP =
|
||||
"http://www.google.com/fusiontables/embedviz?" +
|
||||
"viz=MAP&q=select+col0,+col1,+col2,+col3+from+%s+&h=false&" +
|
||||
"lat=%f&lng=%f&z=%d&t=1&l=col2";
|
||||
|
||||
/** Standard base feed url for Fusion Tables. */
|
||||
private static final String FUSIONTABLES_BASE_FEED_URL =
|
||||
"http://www.google.com/fusiontables/api/query";
|
||||
|
||||
private static final int MAX_POINTS_PER_UPLOAD = 2048;
|
||||
|
||||
private static final String GDATA_VERSION = "2";
|
||||
|
||||
private final Activity context;
|
||||
private final AuthManager auth;
|
||||
private final long trackId;
|
||||
private final ProgressIndicator progressIndicator;
|
||||
private final OnSendCompletedListener onCompletion;
|
||||
private final StringUtils stringUtils;
|
||||
private final MyTracksProviderUtils providerUtils;
|
||||
|
||||
// Progress status
|
||||
private int totalLocationsRead;
|
||||
private int totalLocationsPrepared;
|
||||
private int totalLocationsUploaded;
|
||||
private int totalLocations;
|
||||
private int totalSegmentsUploaded;
|
||||
|
||||
private HttpTransport transport;
|
||||
private String tableId;
|
||||
|
||||
private static String MARKER_TYPE_START = "large_green";
|
||||
private static String MARKER_TYPE_END = "large_red";
|
||||
private static String MARKER_TYPE_WAYPOINT = "large_yellow";
|
||||
|
||||
public SendToFusionTables(Activity context, AuthManager auth,
|
||||
long trackId, ProgressIndicator progressIndicator,
|
||||
OnSendCompletedListener onCompletion) {
|
||||
this.context = context;
|
||||
this.auth = auth;
|
||||
this.trackId = trackId;
|
||||
this.progressIndicator = progressIndicator;
|
||||
this.onCompletion = onCompletion;
|
||||
this.stringUtils = new StringUtils(context);
|
||||
this.providerUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
transport = GoogleTransport.create();
|
||||
GoogleHeaders headers = (GoogleHeaders) transport.defaultHeaders;
|
||||
headers.setApplicationName("Google-MyTracks-" + MyTracksUtils.getMyTracksVersion(context));
|
||||
headers.gdataVersion = GDATA_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(MyTracksConstants.TAG, "Sending to Fusion tables: trackId = " + trackId);
|
||||
doUpload();
|
||||
}
|
||||
|
||||
public static String getMapVisualizationUrl(Track track) {
|
||||
// TODO(leifhendrik): Determine correct bounding box and zoom level that will show the entire track.
|
||||
TripStatistics stats = track.getStatistics();
|
||||
double latE6 = stats.getBottom() + (stats.getTop() - stats.getBottom()) / 2;
|
||||
double lonE6 = stats.getLeft() + (stats.getRight() - stats.getLeft()) / 2;
|
||||
int z = 15;
|
||||
return String.format(FUSIONTABLES_MAP, track.getTableId(), latE6 / 1.E6, lonE6 / 1.E6, z);
|
||||
}
|
||||
|
||||
private void doUpload() {
|
||||
((GoogleHeaders) transport.defaultHeaders).setGoogleLogin(auth.getAuthToken());
|
||||
int statusMessageId = R.string.error_sending_to_fusiontables;
|
||||
boolean success = true;
|
||||
try {
|
||||
progressIndicator.setProgressValue(1);
|
||||
progressIndicator.setProgressMessage(R.string.progress_message_reading_track);
|
||||
|
||||
// Get the track meta-data
|
||||
Track track = providerUtils.getTrack(trackId);
|
||||
String originalDescription = track.getDescription();
|
||||
|
||||
// Create a new table:
|
||||
progressIndicator.setProgressMessage(R.string.progress_message_creating_fusiontable);
|
||||
success = createNewTable(track);
|
||||
|
||||
if (success) {
|
||||
success = makeTableUnlisted(tableId);
|
||||
}
|
||||
|
||||
// Upload all of the segments of the track plus start/end markers
|
||||
if (success) {
|
||||
success = uploadAllTrackPoints(track, originalDescription);
|
||||
}
|
||||
|
||||
// Upload all the waypoints.
|
||||
if (success) {
|
||||
success = uploadWaypoints(track);
|
||||
statusMessageId = R.string.status_new_fusiontable_has_been_created;
|
||||
}
|
||||
Log.d(MyTracksConstants.TAG, "SendToFusionTables: Done: " + success);
|
||||
progressIndicator.setProgressValue(100);
|
||||
} finally {
|
||||
|
||||
final boolean finalSuccess = success;
|
||||
final int finalStatusMessageId = statusMessageId;
|
||||
context.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (onCompletion != null) {
|
||||
onCompletion.onSendCompleted(
|
||||
tableId, finalSuccess, finalStatusMessageId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new table.
|
||||
* If successful sets {@link #tableId}.
|
||||
*
|
||||
* @return true in case of success.
|
||||
*/
|
||||
private boolean createNewTable(Track track) {
|
||||
Log.d(MyTracksConstants.TAG, "Creating a new fusion table.");
|
||||
String query = "CREATE TABLE '" + track.getName() +
|
||||
"' (name:STRING,description:STRING,geometry:LOCATION,marker:STRING)";
|
||||
return runUpdate(query);
|
||||
}
|
||||
|
||||
private boolean makeTableUnlisted(String tableId) {
|
||||
Log.d(MyTracksConstants.TAG, "Setting visibility to unlisted.");
|
||||
String query = "UPDATE TABLE " + tableId + " SET VISIBILITY = unlisted";
|
||||
return runUpdate(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats given values SQL style. Escapes single quotes with a backslash.
|
||||
*
|
||||
* @param values the values to format
|
||||
* @return the values formatted as: ('value1','value2',...,'value_n').
|
||||
*/
|
||||
private static String values(String... values) {
|
||||
StringBuilder builder = new StringBuilder("(");
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (i > 0) {
|
||||
builder.append(',');
|
||||
}
|
||||
builder.append('\'');
|
||||
builder.append(values[i].replaceAll("'", "''"));
|
||||
builder.append('\'');
|
||||
}
|
||||
builder.append(')');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new row representing a marker.
|
||||
*
|
||||
* @param name the marker name
|
||||
* @param description the marker description
|
||||
* @param the marker location
|
||||
* @return true in case of success.
|
||||
*/
|
||||
private boolean createNewPoint(String name, String description, Location location,
|
||||
String marker) {
|
||||
Log.d(MyTracksConstants.TAG, "Creating a new row with a point.");
|
||||
String query = "INSERT INTO " + tableId + " (name,description,geometry,marker) VALUES "
|
||||
+ values(name, description, getKmlPoint(location), marker);
|
||||
return runUpdate(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new row representing a line segment.
|
||||
*
|
||||
* @param track the track/segment to draw
|
||||
* @return true in case of success.
|
||||
*/
|
||||
private boolean createNewLineString(Track track) {
|
||||
Log.d(MyTracksConstants.TAG, "Creating a new row with a point.");
|
||||
String query = "INSERT INTO " + tableId
|
||||
+ " (name,description,geometry) VALUES "
|
||||
+ values(track.getName(), track.getDescription(), getKmlLineString(track));
|
||||
return runUpdate(query);
|
||||
}
|
||||
|
||||
private boolean uploadAllTrackPoints(final Track track, String originalDescription) {
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||
boolean metricUnits = true;
|
||||
if (preferences != null) {
|
||||
metricUnits = preferences.getBoolean(context.getString(R.string.metric_units_key), true);
|
||||
}
|
||||
|
||||
Cursor locationsCursor = providerUtils.getLocationsCursor(track.getId(), 0, -1, false);
|
||||
try {
|
||||
if (!locationsCursor.moveToFirst()) {
|
||||
Log.w(MyTracksConstants.TAG, "Unable to get any points to upload");
|
||||
return false;
|
||||
}
|
||||
|
||||
totalLocationsRead = 0;
|
||||
totalLocationsPrepared = 0;
|
||||
totalLocationsUploaded = 0;
|
||||
totalLocations = locationsCursor.getCount();
|
||||
totalSegmentsUploaded = 0;
|
||||
|
||||
// Limit the number of elevation readings. Ideally we would want around 250.
|
||||
int elevationSamplingFrequency =
|
||||
Math.max(1, (int) (totalLocations / 250.0));
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"Using elevation sampling factor: " + elevationSamplingFrequency
|
||||
+ " on " + totalLocations);
|
||||
double totalDistance = 0;
|
||||
|
||||
Vector<Double> distances = new Vector<Double>();
|
||||
Vector<Double> elevations = new Vector<Double>();
|
||||
DoubleBuffer elevationBuffer = new DoubleBuffer(MyTracksConstants.ELEVATION_SMOOTHING_FACTOR);
|
||||
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
progressIndicator.setProgressMessage(R.string.progress_message_reading_track);
|
||||
Location lastLocation = null;
|
||||
do {
|
||||
if (totalLocationsRead % 100 == 0) {
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
Location loc = providerUtils.createLocation(locationsCursor);
|
||||
locations.add(loc);
|
||||
|
||||
if (totalLocationsRead == 0) {
|
||||
// Put a marker at the first point of the first valid segment:
|
||||
String name = track.getName() + " " + context.getString(R.string.start);
|
||||
createNewPoint(name, "", loc, MARKER_TYPE_START);
|
||||
}
|
||||
|
||||
// Add to the elevation profile.
|
||||
if (loc != null && MyTracksUtils.isValidLocation(loc)) {
|
||||
// All points go into the smoothing buffer...
|
||||
elevationBuffer.setNext(metricUnits ? loc.getAltitude()
|
||||
: loc.getAltitude() * UnitConversions.M_TO_FT);
|
||||
if (lastLocation != null) {
|
||||
double dist = lastLocation.distanceTo(loc);
|
||||
totalDistance += dist;
|
||||
}
|
||||
|
||||
// ...but only a few points are really used to keep the url short.
|
||||
if (totalLocationsRead % elevationSamplingFrequency == 0) {
|
||||
distances.add(totalDistance);
|
||||
elevations.add(elevationBuffer.getAverage());
|
||||
}
|
||||
}
|
||||
|
||||
// If the location was not valid, it's a segment split, so make sure the
|
||||
// distance between the previous segment and the new one is not accounted
|
||||
// for in the next iteration.
|
||||
lastLocation = loc;
|
||||
|
||||
// Every now and then, upload the accumulated points
|
||||
if (totalLocationsRead % MAX_POINTS_PER_UPLOAD == MAX_POINTS_PER_UPLOAD - 1) {
|
||||
if (!prepareAndUploadPoints(track, locations)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
totalLocationsRead++;
|
||||
} while (locationsCursor.moveToNext());
|
||||
|
||||
// Do a final upload with what's left
|
||||
if (!prepareAndUploadPoints(track, locations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Put an end marker at the last point of the last valid segment:
|
||||
if (lastLocation != null) {
|
||||
track.setDescription("<p>" + originalDescription + "</p><p>"
|
||||
+ stringUtils.generateTrackDescription(track, distances, elevations)
|
||||
+ "</p>");
|
||||
String name = track.getName() + " " + context.getString(R.string.end);
|
||||
return createNewPoint(name, track.getDescription(), lastLocation, MARKER_TYPE_END);
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
locationsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given location to the string in the format:
|
||||
* longitude,latitude[,altitude]
|
||||
*
|
||||
* @param location the location to be added
|
||||
* @param builder the string builder to use
|
||||
*/
|
||||
private void appendCoordinate(Location location, StringBuilder builder) {
|
||||
builder
|
||||
.append(location.getLongitude())
|
||||
.append(",")
|
||||
.append(location.getLatitude());
|
||||
if (location.hasAltitude()) {
|
||||
builder.append(",");
|
||||
builder.append(location.getAltitude());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a KML Point tag for the given location.
|
||||
*
|
||||
* @param location The location.
|
||||
* @return the kml.
|
||||
*/
|
||||
private String getKmlPoint(Location location) {
|
||||
StringBuilder builder = new StringBuilder("<Point><coordinates>");
|
||||
appendCoordinate(location, builder);
|
||||
builder.append("</coordinates></Point>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a KML Point tag for the given location.
|
||||
*
|
||||
* @param location The location.
|
||||
* @return the kml.
|
||||
*/
|
||||
private String getKmlLineString(Track track) {
|
||||
StringBuilder builder = new StringBuilder("<LineString><coordinates>");
|
||||
for (Location location : track.getLocations()) {
|
||||
appendCoordinate(location, builder);
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append("</coordinates></LineString>");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private boolean prepareAndUploadPoints(Track track, List<Location> locations) {
|
||||
progressIndicator.setProgressMessage(R.string.progress_message_preparing_track);
|
||||
updateProgress();
|
||||
|
||||
int numLocations = locations.size();
|
||||
if (numLocations < 2) {
|
||||
Log.d(MyTracksConstants.TAG, "Not preparing/uploading too few points");
|
||||
totalLocationsUploaded += numLocations;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepare/pre-process the points
|
||||
ArrayList<Track> splitTracks = prepareLocations(track, locations);
|
||||
|
||||
// Start uploading them
|
||||
progressIndicator.setProgressMessage(R.string.progress_message_sending_fusiontables);
|
||||
for (Track splitTrack : splitTracks) {
|
||||
if (totalSegmentsUploaded > 1) {
|
||||
splitTrack.setName(splitTrack.getName() + " "
|
||||
+ String.format(
|
||||
context.getString(R.string.part), totalSegmentsUploaded));
|
||||
}
|
||||
totalSegmentsUploaded++;
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToFusionTables: Prepared feature for upload w/ "
|
||||
+ splitTrack.getLocations().size() + " points.");
|
||||
|
||||
// Transmit tracks via GData feed:
|
||||
// -------------------------------
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToFusionTables: Uploading to table " + tableId + " w/ auth " + auth);
|
||||
if (!uploadTrackPoints(splitTrack)) {
|
||||
Log.e(MyTracksConstants.TAG, "Uploading failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
locations.clear();
|
||||
totalLocationsUploaded += numLocations;
|
||||
updateProgress();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a buffer of locations for transmission to google fusion tables.
|
||||
*
|
||||
* @param track the original track with meta data
|
||||
* @param buffer a buffer of locations on the track
|
||||
* @return an array of tracks each with a sub section of the points in the
|
||||
* original buffer
|
||||
*/
|
||||
private ArrayList<Track> prepareLocations(
|
||||
Track track, Iterable<Location> locations) {
|
||||
ArrayList<Track> splitTracks = new ArrayList<Track>();
|
||||
|
||||
// Create segments from each full track:
|
||||
Track segment = new Track();
|
||||
TripStatistics segmentStats = segment.getStatistics();
|
||||
TripStatistics trackStats = track.getStatistics();
|
||||
segment.setId(track.getId());
|
||||
segment.setName(track.getName());
|
||||
segment.setDescription(/* track.getDescription() */ "");
|
||||
segment.setCategory(track.getCategory());
|
||||
segmentStats.setStartTime(trackStats.getStartTime());
|
||||
segmentStats.setStopTime(trackStats.getStopTime());
|
||||
boolean startNewTrackSegment = false;
|
||||
for (Location loc : locations) {
|
||||
if (totalLocationsPrepared % 100 == 0) {
|
||||
updateProgress();
|
||||
}
|
||||
if (loc.getLatitude() > 90) {
|
||||
startNewTrackSegment = true;
|
||||
}
|
||||
|
||||
if (startNewTrackSegment) {
|
||||
// Close up the last segment.
|
||||
prepareTrackSegment(segment, splitTracks);
|
||||
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksSendToFusionTables: Starting new track segment...");
|
||||
startNewTrackSegment = false;
|
||||
segment = new Track();
|
||||
segment.setId(track.getId());
|
||||
segment.setName(track.getName());
|
||||
segment.setDescription(/* track.getDescription() */ "");
|
||||
segment.setCategory(track.getCategory());
|
||||
}
|
||||
|
||||
if (loc.getLatitude() <= 90) {
|
||||
segment.addLocation(loc);
|
||||
if (segmentStats.getStartTime() < 0) {
|
||||
segmentStats.setStartTime(loc.getTime());
|
||||
}
|
||||
}
|
||||
totalLocationsPrepared++;
|
||||
}
|
||||
|
||||
prepareTrackSegment(segment, splitTracks);
|
||||
|
||||
return splitTracks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a track segment for sending to google fusion tables.
|
||||
* The main steps are:
|
||||
* - correcting end time
|
||||
* - decimating locations
|
||||
* - splitting into smaller tracks.
|
||||
*
|
||||
* The final track pieces will be put in the array list splitTracks.
|
||||
*
|
||||
* @param segment the original segment of the track
|
||||
* @param splitTracks an array of smaller track segments
|
||||
*/
|
||||
private void prepareTrackSegment(
|
||||
Track segment, ArrayList<Track> splitTracks) {
|
||||
TripStatistics segmentStats = segment.getStatistics();
|
||||
if (segmentStats.getStopTime() < 0
|
||||
&& segment.getLocations().size() > 0) {
|
||||
segmentStats.setStopTime(segment.getLocations().size() - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decimate to 2 meter precision. Fusion tables doesn't like too many
|
||||
* points:
|
||||
*/
|
||||
MyTracksUtils.decimate(segment, 2.0);
|
||||
|
||||
/* If the track still has > 2500 points, split it in pieces: */
|
||||
final int maxPoints = 2500;
|
||||
if (segment.getLocations().size() > maxPoints) {
|
||||
splitTracks.addAll(MyTracksUtils.split(segment, maxPoints));
|
||||
} else if (segment.getLocations().size() >= 2) {
|
||||
splitTracks.add(segment);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean uploadTrackPoints(Track splitTrack) {
|
||||
int numLocations = splitTrack.getLocations().size();
|
||||
if (numLocations < 2) {
|
||||
// Need at least two points for a polyline:
|
||||
Log.w(MyTracksConstants.TAG, "Not uploading too few points");
|
||||
return true;
|
||||
}
|
||||
return createNewLineString(splitTrack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads all of the waypoints associated with this track to a table.
|
||||
*
|
||||
* @param track The track to upload waypoints for.
|
||||
*
|
||||
* @return True on success.
|
||||
*/
|
||||
private boolean uploadWaypoints(final Track track) {
|
||||
// TODO: Stream through the waypoints in chunks.
|
||||
// I am leaving the number of waypoints very high which should not be a
|
||||
// problem because we don't try to load them into objects all at the
|
||||
// same time.
|
||||
boolean success = true;
|
||||
Cursor c = null;
|
||||
c = providerUtils.getWaypointsCursor(
|
||||
track.getId(), 0,
|
||||
MyTracksConstants.MAX_LOADED_WAYPOINTS_POINTS);
|
||||
if (c != null) {
|
||||
if (c.moveToFirst()) {
|
||||
// This will skip the 1st waypoint (it carries the stats for the
|
||||
// last segment).
|
||||
while (c.moveToNext()) {
|
||||
Waypoint wpt = providerUtils.createWaypoint(c);
|
||||
Log.d(MyTracksConstants.TAG, "SendToFusionTables: Creating waypoint.");
|
||||
success = createNewPoint(wpt.getName(), wpt.getDescription(), wpt.getLocation(),
|
||||
MARKER_TYPE_WAYPOINT);
|
||||
if (!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
Log.w(MyTracksConstants.TAG, "SendToFusionTables: upload waypoints failed.");
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current upload progress.
|
||||
*/
|
||||
private void updateProgress() {
|
||||
// The percent of the total that represents the completed part of this
|
||||
// segment.
|
||||
int totalPercentage =
|
||||
(totalLocationsRead + totalLocationsPrepared + totalLocationsUploaded)
|
||||
/ (totalLocations * 3);
|
||||
totalPercentage = Math.min(99, totalPercentage);
|
||||
progressIndicator.setProgressValue(totalPercentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an update query. Handles authentication.
|
||||
*
|
||||
* @param query The given SQL like query
|
||||
* @return true in case of success
|
||||
*/
|
||||
private boolean runUpdate(final String query) {
|
||||
GDataWrapper<HttpTransport> wrapper = new GDataWrapper<HttpTransport>();
|
||||
wrapper.setAuthManager(auth);
|
||||
wrapper.setRetryOnAuthFailure(true);
|
||||
wrapper.setClient(transport);
|
||||
Log.d(MyTracksConstants.TAG, "GData connection prepared: " + this.auth);
|
||||
wrapper.runQuery(new QueryFunction<HttpTransport>() {
|
||||
@Override
|
||||
public void query(HttpTransport client)
|
||||
throws IOException, GDataWrapper.ParseException, GDataWrapper.HttpException,
|
||||
GDataWrapper.AuthenticationException {
|
||||
HttpRequest request = transport.buildPostRequest();
|
||||
request.headers.contentType = "application/x-www-form-urlencoded";
|
||||
GenericUrl url = new GenericUrl(FUSIONTABLES_BASE_FEED_URL);
|
||||
request.url = url;
|
||||
InputStreamContent isc = new InputStreamContent();
|
||||
String sql = "sql=" + URLEncoder.encode(query, "UTF-8");
|
||||
isc.inputStream = new ByteArrayInputStream(Strings.toBytesUtf8(sql));
|
||||
request.content = isc;
|
||||
Log.d(MyTracksConstants.TAG, "Running update query " + url.toString() + ": " + sql);
|
||||
try {
|
||||
HttpResponse response = request.execute();
|
||||
boolean success = response.isSuccessStatusCode;
|
||||
if (success) {
|
||||
byte[] result = new byte[1024];
|
||||
response.getContent().read(result);
|
||||
String s = Strings.fromBytesUtf8(result);
|
||||
String[] lines = s.split(Strings.LINE_SEPARATOR);
|
||||
if (lines[0].equals("tableid")) {
|
||||
tableId = lines[1];
|
||||
Log.d(MyTracksConstants.TAG, "tableId = " + tableId);
|
||||
} else {
|
||||
Log.w(MyTracksConstants.TAG, "Unrecognized response: " + lines[0]);
|
||||
}
|
||||
} else {
|
||||
Log.d(MyTracksConstants.TAG, "Query failed: " + response.statusMessage + " (" +
|
||||
response.statusCode + ")");
|
||||
throw new GDataWrapper.HttpException(response.statusCode, response.statusMessage);
|
||||
}
|
||||
} catch (HttpResponseException e) {
|
||||
if (e.response.statusCode == 401) {
|
||||
throw new GDataWrapper.AuthenticationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
return wrapper.getErrorType() == GDataWrapper.ERROR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -1,689 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mymaps.MyMapsFeature;
|
||||
import com.google.android.apps.mymaps.MyMapsGDataConverter;
|
||||
import com.google.android.apps.mymaps.MyMapsGDataWrapper;
|
||||
import com.google.android.apps.mymaps.MyMapsGDataWrapper.QueryFunction;
|
||||
import com.google.android.apps.mymaps.MyMapsMapMetadata;
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.ProgressIndicator;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.data.Entry;
|
||||
import com.google.wireless.gdata.maps.MapsClient;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
import com.google.wireless.gdata2.client.AuthenticationException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
/**
|
||||
* A helper class used to transmit tracks to Google MyMaps.
|
||||
* A new instance should be used for each upload.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class SendToMyMaps implements Runnable {
|
||||
|
||||
public static final String NEW_MAP_ID = "new";
|
||||
private static final String END_ICON_URL =
|
||||
"http://maps.google.com/mapfiles/ms/micons/red-dot.png";
|
||||
private static final String START_ICON_URL =
|
||||
"http://maps.google.com/mapfiles/ms/micons/green-dot.png";
|
||||
private static final int MAX_POINTS_PER_UPLOAD = 2048;
|
||||
|
||||
private final Activity context;
|
||||
private final AuthManager auth;
|
||||
private final long trackId;
|
||||
private final ProgressIndicator progressIndicator;
|
||||
private final OnSendCompletedListener onCompletion;
|
||||
private final StringUtils stringUtils;
|
||||
private final MyTracksProviderUtils providerUtils;
|
||||
private String mapId;
|
||||
|
||||
// GData service
|
||||
private MyMapsGDataWrapper wrapper;
|
||||
private MyMapsGDataConverter gdataConverter;
|
||||
|
||||
// Progress status
|
||||
private int totalLocationsRead;
|
||||
private int totalLocationsPrepared;
|
||||
private int totalLocationsUploaded;
|
||||
private int totalLocations;
|
||||
private int totalSegmentsUploaded;
|
||||
|
||||
public interface OnSendCompletedListener {
|
||||
void onSendCompleted(String mapId, boolean success, int statusMessage);
|
||||
}
|
||||
|
||||
public SendToMyMaps(Activity context, String mapId, AuthManager auth,
|
||||
long trackId, ProgressIndicator progressIndicator,
|
||||
OnSendCompletedListener onCompletion) {
|
||||
this.context = context;
|
||||
this.mapId = mapId;
|
||||
this.auth = auth;
|
||||
this.trackId = trackId;
|
||||
this.progressIndicator = progressIndicator;
|
||||
this.onCompletion = onCompletion;
|
||||
this.stringUtils = new StringUtils(context);
|
||||
this.providerUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(MyTracksConstants.TAG, "Sending to MyMaps: trackId = " + trackId);
|
||||
doUpload();
|
||||
}
|
||||
|
||||
private void doUpload() {
|
||||
int statusMessageId = R.string.error_sending_to_mymap;
|
||||
boolean success = true;
|
||||
try {
|
||||
gdataConverter = new MyMapsGDataConverter();
|
||||
|
||||
progressIndicator.setProgressValue(1);
|
||||
progressIndicator.setProgressMessage(
|
||||
R.string.progress_message_reading_track);
|
||||
|
||||
// Get the track meta-data
|
||||
Track track = providerUtils.getTrack(trackId);
|
||||
String originalDescription = track.getDescription();
|
||||
track.setDescription("<p>" + track.getDescription() + "</p><p>"
|
||||
+ stringUtils.generateTrackDescription(track, null, null) + "</p>");
|
||||
wrapper = new MyMapsGDataWrapper(context);
|
||||
wrapper.setAuthManager(auth);
|
||||
wrapper.setRetryOnAuthFailure(true);
|
||||
|
||||
// Create a new map if necessary:
|
||||
boolean isNewMap = mapId.equals(NEW_MAP_ID);
|
||||
if (isNewMap) {
|
||||
success = createNewMap(track, originalDescription);
|
||||
}
|
||||
|
||||
// Upload all of the segments of the track plus start/end markers
|
||||
if (success) {
|
||||
success = uploadAllTrackPoints(track, originalDescription);
|
||||
}
|
||||
|
||||
// Put waypoints.
|
||||
if (success) {
|
||||
success = uploadWaypoints(track);
|
||||
if (!success) {
|
||||
Log.w(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: upload waypoints failed.");
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
statusMessageId = isNewMap
|
||||
? R.string.status_new_mymap_has_been_created
|
||||
: R.string.status_tracks_have_been_uploaded;
|
||||
}
|
||||
Log.d(MyTracksConstants.TAG, "SendToMyMaps: Done: " + success);
|
||||
progressIndicator.setProgressValue(100);
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
|
||||
} finally {
|
||||
if (wrapper != null) {
|
||||
wrapper.cleanUp();
|
||||
}
|
||||
|
||||
final boolean finalSuccess = success;
|
||||
final int finalStatusMessageId = statusMessageId;
|
||||
context.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (onCompletion != null) {
|
||||
onCompletion.onSendCompleted(
|
||||
mapId, finalSuccess, finalStatusMessageId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new map for the given track.
|
||||
*
|
||||
* @param track The track that will be uploaded to this map
|
||||
* @return True on success.
|
||||
*/
|
||||
private boolean createNewMap(final Track track, final String description) {
|
||||
progressIndicator.setProgressMessage(
|
||||
R.string.progress_message_creating_map);
|
||||
return wrapper.runQuery(new QueryFunction() {
|
||||
@Override
|
||||
public void query(MapsClient client) throws IOException, Exception {
|
||||
Log.d(MyTracksConstants.TAG, "Creating a new map.");
|
||||
String mapFeed = MapsClient.getMapsFeed();
|
||||
Log.d(MyTracksConstants.TAG, "Map feed is " + mapFeed);
|
||||
MyMapsMapMetadata metaData = new MyMapsMapMetadata();
|
||||
metaData.setTitle(track.getName());
|
||||
metaData.setDescription(description + " - "
|
||||
+ track.getCategory() + " - "
|
||||
+ context.getString(R.string.new_map_description));
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
MyTracksSettings.SETTINGS_NAME, 0);
|
||||
boolean mapPublic = true;
|
||||
if (preferences != null) {
|
||||
mapPublic = preferences.getBoolean(
|
||||
context.getString(R.string.default_map_public_key), true);
|
||||
}
|
||||
metaData.setSearchable(mapPublic);
|
||||
Entry entry = MyMapsGDataConverter.getMapEntryForMetadata(metaData);
|
||||
Log.d(MyTracksConstants.TAG, "Title: " + entry.getTitle());
|
||||
Entry map = client.createEntry(mapFeed, auth.getAuthToken(), entry);
|
||||
mapId = MapsClient.getMapIdFromMapEntryId(map.getId());
|
||||
Log.d(MyTracksConstants.TAG, "New map id is: " + mapId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean uploadAllTrackPoints(
|
||||
final Track track, String originalDescription) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
MyTracksSettings.SETTINGS_NAME, 0);
|
||||
boolean metricUnits = true;
|
||||
if (preferences != null) {
|
||||
metricUnits =
|
||||
preferences.getBoolean(context.getString(R.string.metric_units_key),
|
||||
true);
|
||||
}
|
||||
|
||||
Cursor locationsCursor =
|
||||
providerUtils.getLocationsCursor(track.getId(), 0, -1, false);
|
||||
try {
|
||||
if (!locationsCursor.moveToFirst()) {
|
||||
Log.w(MyTracksConstants.TAG, "Unable to get any points to upload");
|
||||
return false;
|
||||
}
|
||||
|
||||
totalLocationsRead = 0;
|
||||
totalLocationsPrepared = 0;
|
||||
totalLocationsUploaded = 0;
|
||||
totalLocations = locationsCursor.getCount();
|
||||
totalSegmentsUploaded = 0;
|
||||
|
||||
// Limit the number of elevation readings. Ideally we would want around 250.
|
||||
int elevationSamplingFrequency =
|
||||
Math.max(1, (int) (totalLocations / 250.0));
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"Using elevation sampling factor: " + elevationSamplingFrequency
|
||||
+ " on " + totalLocations);
|
||||
double totalDistance = 0;
|
||||
|
||||
Vector<Double> distances = new Vector<Double>();
|
||||
Vector<Double> elevations = new Vector<Double>();
|
||||
DoubleBuffer elevationBuffer =
|
||||
new DoubleBuffer(MyTracksConstants.ELEVATION_SMOOTHING_FACTOR);
|
||||
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
progressIndicator.setProgressMessage(
|
||||
R.string.progress_message_reading_track);
|
||||
Location lastLocation = null;
|
||||
do {
|
||||
if (totalLocationsRead % 100 == 0) {
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
Location loc = providerUtils.createLocation(locationsCursor);
|
||||
locations.add(loc);
|
||||
|
||||
if (totalLocationsRead == 0) {
|
||||
// Put a marker at the first point of the first valid segment:
|
||||
uploadMarker(track, loc, true);
|
||||
}
|
||||
|
||||
// Add to the elevation profile.
|
||||
if (loc != null && MyTracksUtils.isValidLocation(loc)) {
|
||||
// All points go into the smoothing buffer...
|
||||
elevationBuffer.setNext(metricUnits ? loc.getAltitude()
|
||||
: loc.getAltitude() * UnitConversions.M_TO_FT);
|
||||
if (lastLocation != null) {
|
||||
double dist = lastLocation.distanceTo(loc);
|
||||
totalDistance += dist;
|
||||
}
|
||||
|
||||
// ...but only a few points are really used to keep the url short.
|
||||
if (totalLocationsRead % elevationSamplingFrequency == 0) {
|
||||
distances.add(totalDistance);
|
||||
elevations.add(elevationBuffer.getAverage());
|
||||
}
|
||||
}
|
||||
|
||||
// If the location was not valid, it's a segment split, so make sure the
|
||||
// distance between the previous segment and the new one is not accounted
|
||||
// for in the next iteration.
|
||||
lastLocation = loc;
|
||||
|
||||
// Every now and then, upload the accumulated points
|
||||
if (totalLocationsRead % MAX_POINTS_PER_UPLOAD
|
||||
== MAX_POINTS_PER_UPLOAD - 1) {
|
||||
if (!prepareAndUploadPoints(track, locations)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
totalLocationsRead++;
|
||||
} while (locationsCursor.moveToNext());
|
||||
|
||||
// Do a final upload with what's left
|
||||
if (!prepareAndUploadPoints(track, locations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Put an end marker at the last point of the last valid segment:
|
||||
if (lastLocation != null) {
|
||||
track.setDescription("<p>" + originalDescription + "</p><p>"
|
||||
+ stringUtils.generateTrackDescription(
|
||||
track, distances, elevations)
|
||||
+ "</p>");
|
||||
return uploadMarker(track, lastLocation, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
locationsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean uploadMarker(final Track track,
|
||||
final Location location,
|
||||
final boolean isStart) {
|
||||
boolean okay = wrapper.runQuery(new QueryFunction() {
|
||||
@Override
|
||||
public void query(MapsClient client)
|
||||
throws AuthenticationException, IOException, Exception {
|
||||
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
||||
insertMarker(context, client, featureFeed, track, location, isStart);
|
||||
}
|
||||
});
|
||||
return okay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a place mark. Second try if 1st try fails. Will throw exception on
|
||||
* 2nd failure.
|
||||
*/
|
||||
private void insertMarker(Context context, MapsClient client,
|
||||
String featureFeed, Track track, Location loc,
|
||||
boolean isStart) throws IOException, Exception {
|
||||
Entry entry = gdataConverter.getEntryForFeature(
|
||||
buildMyMapsPlacemarkFeature(context, track, loc, isStart));
|
||||
Log.d(MyTracksConstants.TAG, "SendToMyMaps: Creating placemark "
|
||||
+ entry.getTitle());
|
||||
try {
|
||||
client.createEntry(featureFeed, auth.getAuthToken(), entry);
|
||||
Log.d(MyTracksConstants.TAG, "SendToMyMaps: createEntry success!");
|
||||
} catch (IOException e) {
|
||||
Log.w(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: createEntry 1st try failed. Trying again.");
|
||||
// Retry once (often IOException is thrown on a timeout):
|
||||
client.createEntry(featureFeed, auth.getAuthToken(), entry);
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: createEntry success on 2nd try!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a placemark MyMapsFeature from a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @param isStart true if it's the start of the track, or false for end
|
||||
* @return a MyMapsFeature
|
||||
*/
|
||||
private static MyMapsFeature buildMyMapsPlacemarkFeature(Context context,
|
||||
Track track, Location loc, boolean isStart) {
|
||||
MyMapsFeature myMapsFeature = new MyMapsFeature();
|
||||
myMapsFeature.generateAndroidId();
|
||||
myMapsFeature.setType(MyMapsFeature.MARKER);
|
||||
if (isStart) {
|
||||
myMapsFeature.setIconUrl(START_ICON_URL);
|
||||
} else {
|
||||
myMapsFeature.setIconUrl(END_ICON_URL);
|
||||
}
|
||||
myMapsFeature.addPoint(MyTracksUtils.getGeoPoint(loc));
|
||||
String name = track.getName() + " "
|
||||
+ (isStart ? context.getString(R.string.start)
|
||||
: context.getString(R.string.end));
|
||||
myMapsFeature.setTitle(name);
|
||||
myMapsFeature.setDescription(isStart ? "" : track.getDescription());
|
||||
return myMapsFeature;
|
||||
}
|
||||
|
||||
private boolean prepareAndUploadPoints(Track track, List<Location> locations) {
|
||||
progressIndicator.setProgressMessage(
|
||||
R.string.progress_message_preparing_track);
|
||||
updateProgress();
|
||||
|
||||
int numLocations = locations.size();
|
||||
if (numLocations < 2) {
|
||||
Log.d(MyTracksConstants.TAG, "Not preparing/uploading too few points");
|
||||
totalLocationsUploaded += numLocations;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepare/pre-process the points
|
||||
ArrayList<Track> splitTracks = prepareLocations(track, locations);
|
||||
|
||||
// Start uploading them
|
||||
progressIndicator.setProgressMessage(
|
||||
R.string.progress_message_sending_mymaps);
|
||||
for (Track splitTrack : splitTracks) {
|
||||
if (totalSegmentsUploaded > 1) {
|
||||
splitTrack.setName(splitTrack.getName() + " "
|
||||
+ String.format(
|
||||
context.getString(R.string.part), totalSegmentsUploaded));
|
||||
}
|
||||
totalSegmentsUploaded++;
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: Prepared feature for upload w/ "
|
||||
+ splitTrack.getLocations().size() + " points.");
|
||||
|
||||
// Transmit tracks via GData feed:
|
||||
// -------------------------------
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: Uploading to map " + mapId + " w/ auth " + auth);
|
||||
if (!uploadTrackPoints(splitTrack)) {
|
||||
Log.e(MyTracksConstants.TAG, "Uploading failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
locations.clear();
|
||||
totalLocationsUploaded += numLocations;
|
||||
updateProgress();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a buffer of locations for transmission to google maps.
|
||||
*
|
||||
* @param track the original track with meta data
|
||||
* @param buffer a buffer of locations on the track
|
||||
* @return an array of tracks each with a sub section of the points in the
|
||||
* original buffer
|
||||
*/
|
||||
private ArrayList<Track> prepareLocations(
|
||||
Track track, Iterable<Location> locations) {
|
||||
ArrayList<Track> splitTracks = new ArrayList<Track>();
|
||||
|
||||
// Create segments from each full track:
|
||||
Track segment = new Track();
|
||||
TripStatistics segmentStats = segment.getStatistics();
|
||||
TripStatistics trackStats = track.getStatistics();
|
||||
segment.setId(track.getId());
|
||||
segment.setName(track.getName());
|
||||
segment.setDescription(/* track.getDescription() */ "");
|
||||
segment.setCategory(track.getCategory());
|
||||
segmentStats.setStartTime(trackStats.getStartTime());
|
||||
segmentStats.setStopTime(trackStats.getStopTime());
|
||||
boolean startNewTrackSegment = false;
|
||||
for (Location loc : locations) {
|
||||
if (totalLocationsPrepared % 100 == 0) {
|
||||
updateProgress();
|
||||
}
|
||||
if (loc.getLatitude() > 90) {
|
||||
startNewTrackSegment = true;
|
||||
}
|
||||
|
||||
if (startNewTrackSegment) {
|
||||
// Close up the last segment.
|
||||
prepareTrackSegment(segment, splitTracks);
|
||||
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"MyTracksSendToMyMaps: Starting new track segment...");
|
||||
startNewTrackSegment = false;
|
||||
segment = new Track();
|
||||
segment.setId(track.getId());
|
||||
segment.setName(track.getName());
|
||||
segment.setDescription(/* track.getDescription() */ "");
|
||||
segment.setCategory(track.getCategory());
|
||||
}
|
||||
|
||||
if (loc.getLatitude() <= 90) {
|
||||
segment.addLocation(loc);
|
||||
if (segmentStats.getStartTime() < 0) {
|
||||
segmentStats.setStartTime(loc.getTime());
|
||||
}
|
||||
}
|
||||
totalLocationsPrepared++;
|
||||
}
|
||||
|
||||
prepareTrackSegment(segment, splitTracks);
|
||||
|
||||
return splitTracks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a track segment for sending to google maps.
|
||||
* The main steps are:
|
||||
* - correcting end time
|
||||
* - decimating locations
|
||||
* - splitting into smaller tracks.
|
||||
*
|
||||
* The final track pieces will be put in the array list splitTracks.
|
||||
*
|
||||
* @param segment the original segment of the track
|
||||
* @param splitTracks an array of smaller track segments
|
||||
*/
|
||||
private void prepareTrackSegment(
|
||||
Track segment, ArrayList<Track> splitTracks) {
|
||||
TripStatistics segmentStats = segment.getStatistics();
|
||||
if (segmentStats.getStopTime() < 0
|
||||
&& segment.getLocations().size() > 0) {
|
||||
segmentStats.setStopTime(segment.getLocations().size() - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decimate to 2 meter precision. Mapshop doesn't like too many
|
||||
* points:
|
||||
*/
|
||||
MyTracksUtils.decimate(segment, 2.0);
|
||||
|
||||
/* It the track still has > 500 points, split it in pieces: */
|
||||
if (segment.getLocations().size() > 500) {
|
||||
splitTracks.addAll(MyTracksUtils.split(segment, 500));
|
||||
} else if (segment.getLocations().size() >= 2) {
|
||||
splitTracks.add(segment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a given list of tracks to Google MyMaps using the maps GData feed.
|
||||
*/
|
||||
private boolean uploadTrackPoints(final Track track) {
|
||||
return wrapper.runQuery(new QueryFunction() {
|
||||
@Override
|
||||
public void query(MapsClient client)
|
||||
throws AuthenticationException, IOException, Exception {
|
||||
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
||||
Log.d(MyTracksConstants.TAG, "Feature feed url: " + featureFeed);
|
||||
uploadTrackPoints(track, client, featureFeed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean uploadTrackPoints(Track splitTrack,
|
||||
MapsClient client,
|
||||
String featureFeed)
|
||||
throws IOException, Exception {
|
||||
Entry entry = null;
|
||||
int numLocations = splitTrack.getLocations().size();
|
||||
if (numLocations < 2) {
|
||||
// Need at least two points for a polyline:
|
||||
Log.w(MyTracksConstants.TAG, "Not uploading too few points");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Put the line:
|
||||
entry = gdataConverter.getEntryForFeature(
|
||||
buildMyMapsLineFeature(splitTrack));
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: Creating line " + entry.getTitle());
|
||||
try {
|
||||
client.createEntry(featureFeed, auth.getAuthToken(), entry);
|
||||
Log.d(MyTracksConstants.TAG, "SendToMyMaps: createEntry success!");
|
||||
} catch (IOException e) {
|
||||
Log.w(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: createEntry 1st try failed. Trying again.");
|
||||
// Retry once (often IOException is thrown on a timeout):
|
||||
client.createEntry(featureFeed, auth.getAuthToken(), entry);
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: createEntry success on 2nd try!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a MyMapsFeature from a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @return a MyMapsFeature
|
||||
*/
|
||||
private static MyMapsFeature buildMyMapsLineFeature(Track track) {
|
||||
MyMapsFeature myMapsFeature = new MyMapsFeature();
|
||||
myMapsFeature.generateAndroidId();
|
||||
myMapsFeature.setType(MyMapsFeature.LINE);
|
||||
if (track.getName().length() < 1) {
|
||||
// Features must have a name (otherwise GData upload may fail):
|
||||
myMapsFeature.setTitle("-");
|
||||
} else {
|
||||
myMapsFeature.setTitle(track.getName());
|
||||
}
|
||||
myMapsFeature.setColor(0x80FF0000);
|
||||
for (Location loc : track.getLocations()) {
|
||||
myMapsFeature.addPoint(MyTracksUtils.getGeoPoint(loc));
|
||||
}
|
||||
return myMapsFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads all of the waypoints associated with this track to map.
|
||||
*
|
||||
* @param track The track to upload waypoints for.
|
||||
*
|
||||
* @return True on success.
|
||||
*/
|
||||
private boolean uploadWaypoints(final Track track) {
|
||||
return wrapper.runQuery(new QueryFunction() {
|
||||
public void query(MapsClient client) {
|
||||
// TODO: Stream through he waypoints in chunks.
|
||||
// I am leaving the number of waypoints very high which should not be a
|
||||
// problem because we don't try to load them into objects all at the
|
||||
// same time.
|
||||
Cursor c = null;
|
||||
c = providerUtils.getWaypointsCursor(
|
||||
track.getId(), 0,
|
||||
MyTracksConstants.MAX_LOADED_WAYPOINTS_POINTS);
|
||||
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
||||
if (c != null) {
|
||||
try {
|
||||
if (c.moveToFirst()) {
|
||||
// This will skip the 1st waypoint (it carries the stats for the
|
||||
// last segment).
|
||||
while (c.moveToNext()) {
|
||||
Waypoint wpt = providerUtils.createWaypoint(c);
|
||||
Entry entry = gdataConverter.getEntryForFeature(
|
||||
buildMyMapsPlacemarkFeature(wpt));
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: Creating waypoint.");
|
||||
try {
|
||||
client.createEntry(featureFeed, auth.getAuthToken(), entry);
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: createEntry success!");
|
||||
} catch (IOException e) {
|
||||
Log.w(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: createEntry 1st try failed. Retrying.");
|
||||
|
||||
// Retry once (often IOException is thrown on a timeout):
|
||||
client.createEntry(featureFeed, auth.getAuthToken(), entry);
|
||||
Log.d(MyTracksConstants.TAG,
|
||||
"SendToMyMaps: createEntry success on 2nd try!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
Log.w(MyTracksConstants.TAG, "ParseException caught.", e);
|
||||
} catch (HttpException e) {
|
||||
Log.w(MyTracksConstants.TAG, "HttpException caught.", e);
|
||||
} catch (IOException e) {
|
||||
Log.w(MyTracksConstants.TAG, "IOException caught.", e);
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a MyMapsFeature from a track.
|
||||
*
|
||||
* @param wpt the waypoint
|
||||
* @return a MyMapsFeature
|
||||
*/
|
||||
private static MyMapsFeature buildMyMapsPlacemarkFeature(Waypoint wpt) {
|
||||
MyMapsFeature myMapsFeature = new MyMapsFeature();
|
||||
myMapsFeature.generateAndroidId();
|
||||
myMapsFeature.setType(MyMapsFeature.MARKER);
|
||||
myMapsFeature.setIconUrl(wpt.getIcon());
|
||||
myMapsFeature.setDescription(wpt.getDescription());
|
||||
myMapsFeature.addPoint(MyTracksUtils.getGeoPoint(wpt.getLocation()));
|
||||
if (wpt.getName().length() < 1) {
|
||||
// Features must have a name (otherwise GData upload may fail):
|
||||
myMapsFeature.setTitle("-");
|
||||
} else {
|
||||
myMapsFeature.setTitle(wpt.getName());
|
||||
}
|
||||
myMapsFeature.setDescription(wpt.getDescription().replaceAll("\n", "<br>"));
|
||||
return myMapsFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current upload progress.
|
||||
*/
|
||||
private void updateProgress() {
|
||||
// The percent of the total that represents the completed part of this
|
||||
// segment.
|
||||
int totalPercentage =
|
||||
(totalLocationsRead + totalLocationsPrepared + totalLocationsUploaded)
|
||||
/ (totalLocations * 3);
|
||||
totalPercentage = Math.min(99, totalPercentage);
|
||||
progressIndicator.setProgressValue(totalPercentage);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import android.os.Build;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
@@ -77,7 +78,8 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
||||
if (pw == null) {
|
||||
return;
|
||||
}
|
||||
pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>");
|
||||
pw.format("<?xml version=\"1.0\" encoding=\"%s\" standalone=\"no\" ?>\n",
|
||||
Charset.defaultCharset().name());
|
||||
pw.print("<TrainingCenterDatabase ");
|
||||
pw.print("xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
|
||||
pw.print("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
|
||||
|
||||
@@ -37,13 +37,15 @@ public class BackupColumns {
|
||||
TracksColumns.MOVINGTIME, TracksColumns.AVGSPEED, TracksColumns.AVGMOVINGSPEED,
|
||||
TracksColumns.MAXSPEED, TracksColumns.MINELEVATION, TracksColumns.MAXELEVATION,
|
||||
TracksColumns.ELEVATIONGAIN, TracksColumns.MINGRADE, TracksColumns.MAXGRADE,
|
||||
TracksColumns.MINLAT, TracksColumns.MAXLAT, TracksColumns.MINLON, TracksColumns.MAXLON };
|
||||
TracksColumns.MINLAT, TracksColumns.MAXLAT, TracksColumns.MINLON, TracksColumns.MAXLON,
|
||||
TracksColumns.MAPID, TracksColumns.TABLEID};
|
||||
public static final byte[] TRACKS_BACKUP_COLUMN_TYPES = {
|
||||
LONG_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID,
|
||||
LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID,
|
||||
FLOAT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID };
|
||||
FLOAT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, STRING_TYPE_ID,
|
||||
STRING_TYPE_ID};
|
||||
|
||||
public static final String[] WAYPOINTS_BACKUP_COLUMNS = {
|
||||
WaypointsColumns._ID, WaypointsColumns.TRACKID, WaypointsColumns.NAME,
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.docs;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.AuthManager;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper.QueryFunction;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.ResourceUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.wireless.gdata.client.GDataServiceClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.data.Entry;
|
||||
import com.google.wireless.gdata.docs.SpreadsheetsClient;
|
||||
import com.google.wireless.gdata.docs.SpreadsheetsClient.WorksheetEntry;
|
||||
import com.google.wireless.gdata.parser.GDataParser;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
import com.google.wireless.gdata2.client.AuthenticationException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* This class contains helper methods for interacting with Google Docs and
|
||||
* Google Spreadsheets.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DocsHelper {
|
||||
private static final String DOCS_FEED_URL =
|
||||
"http://docs.google.com/feeds/documents/private/full";
|
||||
private static final String DOCS_SPREADSHEET_URL =
|
||||
"http://docs.google.com/feeds/documents/private/full/spreadsheet%3A";
|
||||
private static final String DOCS_WORKSHEETS_URL_FORMAT =
|
||||
"http://spreadsheets.google.com/feeds/worksheets/%s/private/full";
|
||||
private static final String DOCS_MY_SPREADSHEETS_FEED_URL =
|
||||
"http://docs.google.com/feeds/documents/private/full?category=mine,spreadsheet";
|
||||
private static final String DOCS_SPREADSHEET_URL_FORMAT =
|
||||
"http://spreadsheets.google.com/feeds/list/%s/%s/private/full";
|
||||
|
||||
private static final String CONTENT_TYPE_PARAM = "Content-Type";
|
||||
private static final String OPENDOCUMENT_SPREADSHEET_MIME_TYPE =
|
||||
"application/x-vnd.oasis.opendocument.spreadsheet";
|
||||
private static final String ATOM_FEED_MIME_TYPE = "application/atom+xml";
|
||||
|
||||
/**
|
||||
* Creates a new MyTracks spreadsheet with the given name.
|
||||
*
|
||||
* @param context The context associated with this request.
|
||||
* @param docListWrapper The GData handle for the Document List service.
|
||||
* @param name The name for the newly-created spreadsheet.
|
||||
* @return The spreadsheet ID, if one is created. {@code null} will be
|
||||
* returned if a GData error didn't occur, but no spreadsheet ID was
|
||||
* returned.
|
||||
*/
|
||||
public String createSpreadsheet(final Context context,
|
||||
final GDataWrapper<GDataServiceClient> docListWrapper, final String name) throws IOException {
|
||||
final AtomicReference<String> idSaver = new AtomicReference<String>();
|
||||
|
||||
boolean result = docListWrapper.runQuery(new QueryFunction<GDataServiceClient>() {
|
||||
@Override
|
||||
public void query(GDataServiceClient client) throws IOException,
|
||||
GDataWrapper.AuthenticationException {
|
||||
// Construct and send request
|
||||
URL url = new URL(DOCS_FEED_URL);
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.addRequestProperty(CONTENT_TYPE_PARAM,
|
||||
OPENDOCUMENT_SPREADSHEET_MIME_TYPE);
|
||||
conn.addRequestProperty("Slug", name);
|
||||
conn.addRequestProperty("Authorization",
|
||||
"GoogleLogin auth=" +
|
||||
docListWrapper.getAuthManager().getAuthToken());
|
||||
conn.setDoOutput(true);
|
||||
OutputStream os = conn.getOutputStream();
|
||||
ResourceUtils.readBinaryFileToOutputStream(
|
||||
context, R.raw.mytracks_empty_spreadsheet, os);
|
||||
|
||||
// Get the response
|
||||
// TODO: The following is a horrible ugly hack.
|
||||
// Hopefully we can retire it when there is a proper gdata api.
|
||||
BufferedReader rd = null;
|
||||
String line;
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
try {
|
||||
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
while ((line = rd.readLine()) != null) {
|
||||
resultBuilder.append(line);
|
||||
}
|
||||
os.close();
|
||||
rd.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
// The GData API sometimes throws an error, even though creation of
|
||||
// the document succeeded. In that case let's just return. The caller
|
||||
// then needs to check if the doc actually exists.
|
||||
return;
|
||||
} finally {
|
||||
os.close();
|
||||
if (rd != null) {
|
||||
rd.close();
|
||||
}
|
||||
}
|
||||
|
||||
String result = resultBuilder.toString();
|
||||
// Try to find the id.
|
||||
int idTagIndex = result.indexOf("<id>");
|
||||
if (idTagIndex == -1) {
|
||||
return;
|
||||
}
|
||||
int idTagCloseIndex = result.indexOf("</id>", idTagIndex);
|
||||
if (idTagCloseIndex == -1) {
|
||||
return;
|
||||
}
|
||||
int idStringStart = result.indexOf(DOCS_SPREADSHEET_URL, idTagIndex);
|
||||
if (idStringStart == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
String id = result.substring(
|
||||
idStringStart + DOCS_SPREADSHEET_URL.length(), idTagCloseIndex);
|
||||
Log.i(MyTracksConstants.TAG, "Created new spreadsheet: " + id);
|
||||
idSaver.set(id);
|
||||
}});
|
||||
|
||||
if (!result) {
|
||||
throw newIOException(docListWrapper,
|
||||
"Failed to create new spreadsheet.");
|
||||
}
|
||||
|
||||
return idSaver.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ID of a spreadsheet with the given name.
|
||||
*
|
||||
* @param docListWrapper The GData handle for the Document List service.
|
||||
* @param title The name of the spreadsheet whose ID is to be retrieved.
|
||||
* @return The spreadsheet ID, if it can be retrieved. {@code null} will
|
||||
* be returned if no spreadsheet exists by the given name.
|
||||
* @throws IOException If an error occurs during the GData request.
|
||||
*/
|
||||
public String requestSpreadsheetId(final GDataWrapper<GDataServiceClient> docListWrapper,
|
||||
final String title) throws IOException {
|
||||
final AtomicReference<String> idSaver = new AtomicReference<String>();
|
||||
|
||||
boolean result = docListWrapper.runQuery(new QueryFunction<GDataServiceClient>() {
|
||||
@Override
|
||||
public void query(GDataServiceClient client)
|
||||
throws IOException, GDataWrapper.ParseException, GDataWrapper.HttpException,
|
||||
GDataWrapper.AuthenticationException {
|
||||
GDataParser listParser;
|
||||
try {
|
||||
listParser = client.getParserForFeed(Entry.class,
|
||||
DOCS_MY_SPREADSHEETS_FEED_URL,
|
||||
docListWrapper.getAuthManager().getAuthToken());
|
||||
listParser.init();
|
||||
|
||||
while (listParser.hasMoreData()) {
|
||||
Entry entry = listParser.readNextEntry(null);
|
||||
String entryTitle = entry.getTitle();
|
||||
Log.i(MyTracksConstants.TAG, "Found docs entry: " + entryTitle);
|
||||
if (entryTitle.equals(title)) {
|
||||
String entryId = entry.getId();
|
||||
int lastSlash = entryId.lastIndexOf('/');
|
||||
idSaver.set(entryId.substring(lastSlash + 15));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new GDataWrapper.ParseException(e);
|
||||
} catch (HttpException e) {
|
||||
throw new GDataWrapper.HttpException(e.getStatusCode(), e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
throw newIOException(docListWrapper,
|
||||
"Failed to retrieve spreadsheet list.");
|
||||
}
|
||||
|
||||
return idSaver.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ID of the first worksheet in the named spreadsheet.
|
||||
*
|
||||
* @param trixWrapper The GData handle for the spreadsheet service.
|
||||
* @param spreadsheetId The GData ID for the given spreadsheet.
|
||||
* @return The worksheet ID, if it can be retrieved. {@code null} will be
|
||||
* returned if the GData request returns without error, but without an
|
||||
* ID.
|
||||
* @throws IOException If an error occurs during the GData request.
|
||||
*/
|
||||
public String getWorksheetId(final GDataWrapper<GDataServiceClient> trixWrapper,
|
||||
final String spreadsheetId) throws IOException {
|
||||
final AtomicReference<String> idSaver = new AtomicReference<String>();
|
||||
|
||||
boolean result = trixWrapper.runQuery(new QueryFunction<GDataServiceClient>() {
|
||||
@Override
|
||||
public void query(GDataServiceClient client)
|
||||
throws GDataWrapper.AuthenticationException, IOException, GDataWrapper.ParseException, GDataWrapper.HttpException {
|
||||
String uri = String.format(DOCS_WORKSHEETS_URL_FORMAT, spreadsheetId);
|
||||
GDataParser sheetParser;
|
||||
try {
|
||||
sheetParser = ((SpreadsheetsClient) client).getParserForWorksheetsFeed(uri,
|
||||
trixWrapper.getAuthManager().getAuthToken());
|
||||
sheetParser.init();
|
||||
if (!sheetParser.hasMoreData()) {
|
||||
Log.i(MyTracksConstants.TAG, "Found no worksheets");
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the first worksheet.
|
||||
WorksheetEntry worksheetEntry =
|
||||
(WorksheetEntry) sheetParser.readNextEntry(new WorksheetEntry());
|
||||
|
||||
int lastSlash = worksheetEntry.getId().lastIndexOf('/');
|
||||
idSaver.set(worksheetEntry.getId().substring(lastSlash + 1));
|
||||
} catch (ParseException e) {
|
||||
throw new GDataWrapper.ParseException(e);
|
||||
} catch (AuthenticationException e) {
|
||||
throw new GDataWrapper.AuthenticationException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
throw newIOException(trixWrapper, "Failed to retrieve worksheet ID.");
|
||||
}
|
||||
|
||||
return idSaver.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a row to a worksheet containing the stats for a given track.
|
||||
*
|
||||
* @param context The context associated with this request.
|
||||
* @param trixAuth The GData authorization for the spreadsheet service.
|
||||
* @param spreadsheetId The spreadsheet to be modified.
|
||||
* @param worksheetId The worksheet to be modified.
|
||||
* @param track The track whose stats are to be written.
|
||||
* @param metricUnits True if metric units are to be used. If false,
|
||||
* imperial units will be used.
|
||||
* @throws IOException If an error occurs while updating the worksheet.
|
||||
*/
|
||||
public void addTrackRow(Context context, AuthManager trixAuth,
|
||||
String spreadsheetId, String worksheetId, Track track,
|
||||
boolean metricUnits) throws IOException {
|
||||
|
||||
String worksheetUri = String.format(DOCS_SPREADSHEET_URL_FORMAT,
|
||||
spreadsheetId, worksheetId);
|
||||
TripStatistics stats = track.getStatistics();
|
||||
|
||||
String distanceUnit = context.getString(metricUnits ?
|
||||
R.string.kilometer : R.string.mile);
|
||||
String speedUnit = context.getString(metricUnits ?
|
||||
R.string.kilometer_per_hour : R.string.mile_per_hour);
|
||||
String elevationUnit = context.getString(metricUnits ?
|
||||
R.string.meter : R.string.feet);
|
||||
|
||||
// Prepare the Post-Text we are going to send.
|
||||
DocsTagBuilder tagBuilder = new DocsTagBuilder(metricUnits)
|
||||
.append("name", track.getName())
|
||||
.append("description", track.getDescription())
|
||||
.append("date", String.format("%tc", stats.getStartTime()))
|
||||
.append("totaltime", StringUtils.formatTimeAlwaysShowingHours(
|
||||
stats.getTotalTime()))
|
||||
.append("movingtime", StringUtils.formatTimeAlwaysShowingHours(
|
||||
stats.getMovingTime()))
|
||||
.appendLargeUnits("distance", stats.getTotalDistance() / 1000)
|
||||
.append("distanceunit", distanceUnit)
|
||||
.appendLargeUnits("averagespeed", stats.getAverageSpeed() * 3.6)
|
||||
.appendLargeUnits("averagemovingspeed",
|
||||
stats.getAverageMovingSpeed() * 3.6)
|
||||
.appendLargeUnits("maxspeed", stats.getMaxSpeed() * 3.6)
|
||||
.append("speedunit", speedUnit)
|
||||
.appendSmallUnits("elevationgain", stats.getTotalElevationGain())
|
||||
.appendSmallUnits("minelevation", stats.getMinElevation())
|
||||
.appendSmallUnits("maxelevation", stats.getMaxElevation())
|
||||
.append("elevationunit", elevationUnit);
|
||||
|
||||
if (track.getMapId().length() > 0) {
|
||||
tagBuilder.append("map", String.format("%s?msa=0&msid=%s",
|
||||
MyTracksConstants.MAPSHOP_BASE_URL, track.getMapId()));
|
||||
}
|
||||
|
||||
String postText = new StringBuilder()
|
||||
.append("<entry xmlns='http://www.w3.org/2005/Atom' "
|
||||
+ "xmlns:gsx='http://schemas.google.com/spreadsheets/"
|
||||
+ "2006/extended'>")
|
||||
.append(tagBuilder.build())
|
||||
.append("</entry>")
|
||||
.toString();
|
||||
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"Inserting at: " + spreadsheetId + " => " + worksheetUri);
|
||||
|
||||
Log.i(MyTracksConstants.TAG, postText);
|
||||
|
||||
writeRowData(trixAuth, worksheetUri, postText);
|
||||
|
||||
Log.i(MyTracksConstants.TAG, "Post finished.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes spreadsheet row data to the indicated worksheet.
|
||||
*
|
||||
* @param trixAuth The GData authorization for the spreadsheet service.
|
||||
* @param worksheetUri The URI of the worksheet to be altered.
|
||||
* @param postText The XML tags describing the change to be made.
|
||||
* @throws IOException Thrown if an error occurs during the write.
|
||||
*/
|
||||
protected void writeRowData(AuthManager trixAuth, String worksheetUri,
|
||||
String postText) throws IOException {
|
||||
// No need for a wrapper because we know that the authorization was good
|
||||
// enough to get this far.
|
||||
URL url = new URL(worksheetUri);
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.addRequestProperty(CONTENT_TYPE_PARAM, ATOM_FEED_MIME_TYPE);
|
||||
conn.addRequestProperty("Authorization",
|
||||
"GoogleLogin auth=" + trixAuth.getAuthToken());
|
||||
conn.setDoOutput(true);
|
||||
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
|
||||
wr.write(postText);
|
||||
wr.flush();
|
||||
|
||||
// Get the response.
|
||||
// TODO: Should we parse the response, rather than simply throwing it away?
|
||||
BufferedReader rd =
|
||||
new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String line;
|
||||
while ((line = rd.readLine()) != null) {
|
||||
// Process line.
|
||||
Log.i(MyTracksConstants.TAG, "r: " + line);
|
||||
}
|
||||
wr.close();
|
||||
rd.close();
|
||||
}
|
||||
|
||||
private static IOException newIOException(GDataWrapper wrapper,
|
||||
String message) {
|
||||
return new IOException(String.format("%s: %d: %s", message,
|
||||
wrapper.getErrorType(), wrapper.getErrorMessage()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.docs;
|
||||
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
/**
|
||||
* <p>This class builds a string of XML tags used to talk to Docs using GData.
|
||||
*
|
||||
* <p>Sample Usage:
|
||||
*
|
||||
* <code>
|
||||
* String tags = new DocsTagBuilder(false)
|
||||
* .append("tagName", "tagValue")
|
||||
* .appendLargeUnits("bigTagName", 1.0)
|
||||
* .build();
|
||||
* </code>
|
||||
*
|
||||
* <p>results in:
|
||||
*
|
||||
* <code>
|
||||
* <gsx:tagName><![CDATA[tagValue]]></gsx:tagName>
|
||||
* <gsx:bigTagName><![CDATA[1.00]]></gsx:bigTagName>
|
||||
* </code>
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
class DocsTagBuilder {
|
||||
// TODO(simmonmt): These formats aren't I18N-compatible. Not everyone uses
|
||||
// commas for thousands and dots for the decimal point.
|
||||
private static final NumberFormat LARGE_UNIT_FORMAT =
|
||||
new DecimalFormat("#,###,###.00");
|
||||
private static final NumberFormat SMALL_UNIT_FORMAT =
|
||||
new DecimalFormat("###,###");
|
||||
|
||||
protected final boolean metricUnits;
|
||||
protected final StringBuilder stringBuilder;
|
||||
|
||||
/**
|
||||
* @param metricUnits True if metric units are to be used. If false,
|
||||
* imperial units will be used.
|
||||
*/
|
||||
DocsTagBuilder(boolean metricUnits) {
|
||||
this.metricUnits = metricUnits;
|
||||
stringBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
/** Appends a tag containing a string value */
|
||||
DocsTagBuilder append(String name, String value) {
|
||||
appendTag(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag containing a numeric value. The value will be formatted
|
||||
* according to the large distance of the specified measurement system (i.e.
|
||||
* kilometers or miles).
|
||||
*
|
||||
* @param name The tag name.
|
||||
* @param d The value to be formatted, in kilometers.
|
||||
*/
|
||||
DocsTagBuilder appendLargeUnits(String name, double d) {
|
||||
double value = metricUnits ? d : (d * UnitConversions.KM_TO_MI);
|
||||
appendTag(name, LARGE_UNIT_FORMAT.format(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag containing a numeric value. The value will be formatted
|
||||
* according to the small distance of the specified measurement system (i.e.
|
||||
* meters or feet).
|
||||
*
|
||||
* @param name The tag name.
|
||||
* @param d The value to be formatted, in meters.
|
||||
*/
|
||||
DocsTagBuilder appendSmallUnits(String name, double d) {
|
||||
double value = metricUnits ? d : (d * UnitConversions.M_TO_FT);
|
||||
appendTag(name, SMALL_UNIT_FORMAT.format(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns a string containing all tags which have been added. */
|
||||
String build() {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private void appendTag(String name, String value) {
|
||||
stringBuilder.append("<gsx:")
|
||||
.append(name)
|
||||
.append(">")
|
||||
.append(StringUtils.stringAsCData(value))
|
||||
.append("</gsx:")
|
||||
.append(name)
|
||||
.append(">");
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,6 @@ package com.google.android.apps.mytracks.io.gdata;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.io.AuthManager;
|
||||
import com.google.wireless.gdata.client.GDataServiceClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
import com.google.wireless.gdata2.ConflictDetectedException;
|
||||
import com.google.wireless.gdata2.client.AuthenticationException;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
@@ -39,9 +34,60 @@ import java.util.concurrent.TimeoutException;
|
||||
* handling. After a query is run, the wrapper can be queried about the error
|
||||
* that occurred.
|
||||
*
|
||||
* @param C the GData service client
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class GDataWrapper {
|
||||
public class GDataWrapper<C> {
|
||||
|
||||
public static class AuthenticationException extends Exception {
|
||||
private Exception exception;
|
||||
private static final long serialVersionUID = 1L;
|
||||
public AuthenticationException(Exception caught) {
|
||||
this.exception = caught;
|
||||
}
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
};
|
||||
|
||||
public static class ParseException extends Exception {
|
||||
private Exception exception;
|
||||
private static final long serialVersionUID = 1L;
|
||||
public ParseException(Exception caught) {
|
||||
this.exception = caught;
|
||||
}
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
};
|
||||
|
||||
public static class ConflictDetectedException extends Exception {
|
||||
private Exception exception;
|
||||
private static final long serialVersionUID = 1L;
|
||||
public ConflictDetectedException(Exception caught) {
|
||||
this.exception = caught;
|
||||
}
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
};
|
||||
|
||||
public static class HttpException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int statusCode;
|
||||
private String statusMessage;
|
||||
public HttpException(int statusCode, String statusMessage) {
|
||||
super();
|
||||
this.statusCode = statusCode;
|
||||
this.statusMessage = statusMessage;
|
||||
}
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
public String getStatusMessage() {
|
||||
return statusMessage;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A QueryFunction is passed in when executing a query. The query function of
|
||||
@@ -49,8 +95,8 @@ public class GDataWrapper {
|
||||
* should execute whatever operations it desires on the client without concern
|
||||
* for whether the client will throw an error.
|
||||
*/
|
||||
public interface QueryFunction {
|
||||
public abstract void query(GDataServiceClient client)
|
||||
public interface QueryFunction<C> {
|
||||
public abstract void query(C client)
|
||||
throws AuthenticationException, IOException, ParseException,
|
||||
ConflictDetectedException, HttpException;
|
||||
}
|
||||
@@ -92,7 +138,7 @@ public class GDataWrapper {
|
||||
|
||||
private String errorMessage;
|
||||
private int errorType;
|
||||
private GDataServiceClient gdataServiceClient;
|
||||
private C gdataServiceClient;
|
||||
private AuthManager auth;
|
||||
private boolean retryOnAuthFailure;
|
||||
|
||||
@@ -103,7 +149,7 @@ public class GDataWrapper {
|
||||
retryOnAuthFailure = false;
|
||||
}
|
||||
|
||||
public void setClient(GDataServiceClient gdataServiceClient) {
|
||||
public void setClient(C gdataServiceClient) {
|
||||
this.gdataServiceClient = gdataServiceClient;
|
||||
}
|
||||
|
||||
@@ -112,7 +158,7 @@ public class GDataWrapper {
|
||||
return runCommon(function, null);
|
||||
}
|
||||
|
||||
public boolean runQuery(final QueryFunction query) {
|
||||
public boolean runQuery(final QueryFunction<C> query) {
|
||||
return runCommon(null, query);
|
||||
}
|
||||
|
||||
@@ -120,7 +166,7 @@ public class GDataWrapper {
|
||||
* Runs an arbitrary piece of code.
|
||||
*/
|
||||
private boolean runCommon(final AuthenticatedFunction function,
|
||||
final QueryFunction query) {
|
||||
final QueryFunction<C> query) {
|
||||
for (int i = 0; i <= AUTH_TOKEN_INVALIDATE_REFRESH_NUM_RETRIES; i++) {
|
||||
runOne(function, query);
|
||||
if (errorType == ERROR_NO_ERROR) {
|
||||
@@ -144,7 +190,7 @@ public class GDataWrapper {
|
||||
* errorMessage will contain the result/status of the function/query.
|
||||
*/
|
||||
private void runOne(final AuthenticatedFunction function,
|
||||
final QueryFunction query) {
|
||||
final QueryFunction<C> query) {
|
||||
try {
|
||||
if (function != null) {
|
||||
function.run(this.auth.getAuthToken());
|
||||
@@ -160,7 +206,7 @@ public class GDataWrapper {
|
||||
errorMessage = null;
|
||||
|
||||
} catch (AuthenticationException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Exception", e);
|
||||
Log.e(MyTracksConstants.TAG, "AuthenticationException", e);
|
||||
errorType = ERROR_AUTH;
|
||||
errorMessage = e.getMessage();
|
||||
} catch (HttpException e) {
|
||||
@@ -243,6 +289,10 @@ public class GDataWrapper {
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
public AuthManager getAuthManager() {
|
||||
return auth;
|
||||
}
|
||||
|
||||
public void setRetryOnAuthFailure(boolean retry) {
|
||||
retryOnAuthFailure = retry;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mymaps;
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.maps.GeoPoint;
|
||||
|
||||
@@ -61,4 +61,4 @@ public class GeoRect {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,11 @@ import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
import android.location.Location;
|
||||
import android.os.StrictMode;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -267,12 +267,28 @@ public class MyTracksUtils {
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(MyTracksConstants.TAG, "Unable to get signatures", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a utility class w/ only static memebers.
|
||||
* Get the My Tracks version from the manifest.
|
||||
*
|
||||
* @return the version, or an empty string in case of failure.
|
||||
*/
|
||||
public static String getMyTracksVersion(Context context) {
|
||||
try {
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(
|
||||
"com.google.android.maps.mytracks",
|
||||
PackageManager.GET_META_DATA);
|
||||
return pi.versionName;
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(MyTracksConstants.TAG, "Failed to get version info.", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a utility class w/ only static members.
|
||||
*/
|
||||
protected MyTracksUtils() {
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ public class StringUtils implements DescriptionGenerator {
|
||||
+ "<img border=\"0\" src=\"%s\"/>",
|
||||
|
||||
// Line 1
|
||||
context.getString(R.string.my_map_link),
|
||||
context.getString(R.string.fusiontable_link),
|
||||
|
||||
// Line 2
|
||||
context.getString(R.string.total_distance_label),
|
||||
|
||||
@@ -101,6 +101,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
values.put(TracksColumns.NAME, track.getName());
|
||||
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
||||
values.put(TracksColumns.MAPID, track.getMapId());
|
||||
values.put(TracksColumns.TABLEID, track.getTableId());
|
||||
values.put(TracksColumns.CATEGORY, track.getCategory());
|
||||
values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints());
|
||||
values.put(TracksColumns.STARTID, track.getStartId());
|
||||
@@ -269,6 +270,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
int idxDescription =
|
||||
cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||
int idxMapId = cursor.getColumnIndexOrThrow(TracksColumns.MAPID);
|
||||
int idxTableId = cursor.getColumnIndexOrThrow(TracksColumns.TABLEID);
|
||||
int idxCategory = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
||||
int idxStartId = cursor.getColumnIndexOrThrow(TracksColumns.STARTID);
|
||||
int idxStartTime = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
@@ -308,6 +310,9 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
if (!cursor.isNull(idxMapId)) {
|
||||
track.setMapId(cursor.getString(idxMapId));
|
||||
}
|
||||
if (!cursor.isNull(idxTableId)) {
|
||||
track.setTableId(cursor.getString(idxTableId));
|
||||
}
|
||||
if (!cursor.isNull(idxCategory)) {
|
||||
track.setCategory(cursor.getString(idxCategory));
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ public class Track implements Parcelable {
|
||||
track.locations.add(loc);
|
||||
}
|
||||
|
||||
track.tableId = source.readString();
|
||||
return track;
|
||||
}
|
||||
|
||||
@@ -81,6 +82,7 @@ public class Track implements Parcelable {
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String mapId = "";
|
||||
private String tableId = "";
|
||||
private long startId = -1;
|
||||
private long stopId = -1;
|
||||
private String category = "";
|
||||
@@ -104,6 +106,8 @@ public class Track implements Parcelable {
|
||||
for (int i = 0; i < numberOfPoints; ++i) {
|
||||
dest.writeParcelable(locations.get(i), 0);
|
||||
}
|
||||
|
||||
dest.writeString(tableId);
|
||||
}
|
||||
|
||||
// Getters and setters:
|
||||
@@ -161,6 +165,14 @@ public class Track implements Parcelable {
|
||||
this.mapId = mapId;
|
||||
}
|
||||
|
||||
public String getTableId() {
|
||||
return tableId;
|
||||
}
|
||||
|
||||
public void setTableId(String tableId) {
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
@@ -59,4 +59,5 @@ public interface TracksColumns extends BaseColumns {
|
||||
public static final String MINLON = "minlon";
|
||||
public static final String MAXLON = "maxlon";
|
||||
public static final String MAPID = "mapid";
|
||||
public static final String TABLEID = "tableid";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/MyTracks"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
@@ -10,5 +9,7 @@
|
||||
<attribute name="optional" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/MyTracks"/>
|
||||
<classpathentry kind="lib" path="/MyTracks/libs/google-common.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.docs;
|
||||
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.test.mock.MockContext;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Tests for {@link DocsHelper}, with the exception of
|
||||
* {@link DocsHelper#addTrackRow}, which is in
|
||||
* {@link DocsHelper_AddTrackRowTest}.
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DocsHelperTest extends TestCase {
|
||||
private Context mockContext = new MockContext();
|
||||
|
||||
// TODO(simmonmt): Use AndroidMock to mock this class. We do it the hard
|
||||
// way because use of AndroidMock to mock GDataWrapper triggers a compile
|
||||
// error in an unrelated file. Specifically, it causes a NoClassDefFound
|
||||
// exception for com.google.wireless.gdata2.client.AuthenticationException,
|
||||
// (wrongly) attributed to the first source file in the project. Using the
|
||||
// @UsesMocks(GDataWrapper.class) annotation is enough -- you don't have to
|
||||
// touch AndroidMock at all to get this failure.
|
||||
// The bug is filed with Android Mock as
|
||||
// http://code.google.com/p/android-mock/issues/detail?id=3
|
||||
private class MockGDataWrapper extends GDataWrapper {
|
||||
private final boolean returnValue;
|
||||
|
||||
MockGDataWrapper(boolean returnValue) {
|
||||
this.returnValue = returnValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runQuery(QueryFunction queryFunction) {
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void testCreateSpreadsheet_noException() throws Exception {
|
||||
// Our mock GDataWrapper isn't able to affect the return value from
|
||||
// DocsHelper#createSpreadsheet. As such, we're only able to simulate the
|
||||
// case where there weren't any GData errors, but not spreadsheet ID was
|
||||
// actually returned. createSpreadsheet is defined to return null in that
|
||||
// situation.
|
||||
assertNull(new DocsHelper().createSpreadsheet(
|
||||
mockContext, new MockGDataWrapper(true), "sheetName"));
|
||||
}
|
||||
|
||||
public void testCreateSpreadsheet_exception() throws Exception {
|
||||
try {
|
||||
new DocsHelper().createSpreadsheet(
|
||||
mockContext, new MockGDataWrapper(false), "sheetName");
|
||||
fail();
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
|
||||
public void testRequestSpreadsheetId_noException() throws Exception {
|
||||
assertNull(new DocsHelper().requestSpreadsheetId(
|
||||
new MockGDataWrapper(true), "sheetName"));
|
||||
}
|
||||
|
||||
public void testRequestSpreadsheetId_exception() throws Exception {
|
||||
try {
|
||||
new DocsHelper().requestSpreadsheetId(new MockGDataWrapper(false),
|
||||
"sheetName");
|
||||
fail();
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
|
||||
public void testGetWorksheetId_noException() throws Exception {
|
||||
assertNull(new DocsHelper().getWorksheetId(new MockGDataWrapper(true),
|
||||
"sheetId"));
|
||||
}
|
||||
|
||||
public void testGetWorksheetId_exception() throws Exception {
|
||||
try {
|
||||
new DocsHelper().getWorksheetId(new MockGDataWrapper(false), "sheetId");
|
||||
fail();
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.docs;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.AuthManager;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.test.mock.MockContext;
|
||||
import android.test.mock.MockResources;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Tests for {@link DocsHelper#addTrackRow}
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DocsHelper_AddTrackRowTest extends TestCase {
|
||||
private static final long TIME = 1288721514000L;
|
||||
|
||||
private static class StringWritingDocsHelper extends DocsHelper {
|
||||
String writtenSheetUri = null;
|
||||
String writtenData = null;
|
||||
|
||||
@Override
|
||||
protected void writeRowData(AuthManager trixAuth, String worksheetUri,
|
||||
String postText) {
|
||||
writtenSheetUri = worksheetUri;
|
||||
writtenData = postText;
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddTrackRow_imperial() throws Exception {
|
||||
StringWritingDocsHelper docsHelper = new StringWritingDocsHelper();
|
||||
addTrackRow(docsHelper, false);
|
||||
|
||||
String expectedData =
|
||||
"<entry xmlns='http://www.w3.org/2005/Atom' "
|
||||
+ "xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'>"
|
||||
+ "<gsx:name><![CDATA[trackName]]></gsx:name>"
|
||||
+ "<gsx:description><![CDATA[trackDescription]]></gsx:description>"
|
||||
// We format the date here because we can't guarantee/force the timezone
|
||||
+ "<gsx:date><![CDATA[" + String.format("%tc", TIME) + "]]></gsx:date>"
|
||||
+ "<gsx:totaltime><![CDATA[0:00:05]]></gsx:totaltime>"
|
||||
+ "<gsx:movingtime><![CDATA[0:00:04]]></gsx:movingtime>"
|
||||
+ "<gsx:distance><![CDATA[12.43]]></gsx:distance>"
|
||||
+ "<gsx:distanceunit><![CDATA[mile]]></gsx:distanceunit>"
|
||||
+ "<gsx:averagespeed><![CDATA[8,947.75]]></gsx:averagespeed>"
|
||||
+ "<gsx:averagemovingspeed><![CDATA[11,184.68]]>"
|
||||
+ "</gsx:averagemovingspeed>"
|
||||
+ "<gsx:maxspeed><![CDATA[3,355.40]]></gsx:maxspeed>"
|
||||
+ "<gsx:speedunit><![CDATA[mph]]></gsx:speedunit>"
|
||||
+ "<gsx:elevationgain><![CDATA[19,685]]></gsx:elevationgain>"
|
||||
+ "<gsx:minelevation><![CDATA[-1,640]]></gsx:minelevation>"
|
||||
+ "<gsx:maxelevation><![CDATA[1,804]]></gsx:maxelevation>"
|
||||
+ "<gsx:elevationunit><![CDATA[feet]]></gsx:elevationunit>"
|
||||
+ "<gsx:map>"
|
||||
+ "<![CDATA[http://maps.google.com/maps/ms?msa=0&msid=trackMapId]]>"
|
||||
+ "</gsx:map>"
|
||||
+ "</entry>";
|
||||
|
||||
assertEquals(
|
||||
"http://spreadsheets.google.com/feeds/list/ssid/wsid/private/full",
|
||||
docsHelper.writtenSheetUri);
|
||||
assertEquals(expectedData, docsHelper.writtenData);
|
||||
}
|
||||
|
||||
public void testAddTrackRow_metric() throws Exception {
|
||||
StringWritingDocsHelper docsHelper = new StringWritingDocsHelper();
|
||||
addTrackRow(docsHelper, true);
|
||||
|
||||
// The imperial test verifies that the tags come out in the proper order,
|
||||
// and with the proper names. We need only verify that the labels are
|
||||
// correct, and that at least one of the unit-dependent value tags is
|
||||
// correct.
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:distanceunit><![CDATA[km]]></gsx:distanceunit>"));
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:speedunit><![CDATA[kph]]></gsx:speedunit>"));
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:elevationunit><![CDATA[meter]]></gsx:elevationunit>"));
|
||||
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:distance><![CDATA[20.00]]></gsx:distance>"));
|
||||
}
|
||||
|
||||
@UsesMocks({AuthManager.class, MockContext.class, MockResources.class,
|
||||
Track.class})
|
||||
/** Adds a row to the spreadsheet, using the provided helper. */
|
||||
private void addTrackRow(DocsHelper docsHelper, boolean useMetric)
|
||||
throws IOException {
|
||||
Resources mockResources = AndroidMock.createMock(MockResources.class);
|
||||
|
||||
if (useMetric) {
|
||||
AndroidMock.expect(mockResources.getString(R.string.kilometer))
|
||||
.andReturn("km");
|
||||
AndroidMock.expect(mockResources.getString(R.string.kilometer_per_hour))
|
||||
.andReturn("kph");
|
||||
AndroidMock.expect(mockResources.getString(R.string.meter))
|
||||
.andReturn("meter");
|
||||
} else {
|
||||
AndroidMock.expect(mockResources.getString(R.string.mile))
|
||||
.andReturn("mile");
|
||||
AndroidMock.expect(mockResources.getString(R.string.mile_per_hour))
|
||||
.andReturn("mph");
|
||||
AndroidMock.expect(mockResources.getString(R.string.feet))
|
||||
.andReturn("feet");
|
||||
}
|
||||
AndroidMock.replay(mockResources);
|
||||
|
||||
Context mockContext = AndroidMock.createMock(MockContext.class);
|
||||
AndroidMock.expect(mockContext.getResources())
|
||||
.andReturn(mockResources).anyTimes();
|
||||
AndroidMock.replay(mockContext);
|
||||
|
||||
AuthManager mockAuthManager = AndroidMock.createMock(AuthManager.class);
|
||||
AndroidMock.replay(mockAuthManager);
|
||||
|
||||
TripStatistics stats = new TripStatistics();
|
||||
stats.setStartTime(TIME);
|
||||
stats.setTotalTime(5000);
|
||||
stats.setMovingTime(4000);
|
||||
stats.setTotalDistance(20000);
|
||||
stats.setMaxSpeed(1500);
|
||||
stats.setTotalElevationGain(6000);
|
||||
stats.setMinElevation(-500);
|
||||
stats.setMaxElevation(550);
|
||||
|
||||
Track track = new Track();
|
||||
track.setName("trackName");
|
||||
track.setDescription("trackDescription");
|
||||
track.setMapId("trackMapId");
|
||||
track.setStatistics(stats);
|
||||
|
||||
docsHelper.addTrackRow(mockContext, mockAuthManager, "ssid", "wsid",
|
||||
track, useMetric);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.docs;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* This class tests {@link DocsTagBuilder}
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DocsTagBuilderTest extends TestCase {
|
||||
public void testAppend() {
|
||||
assertEquals("<gsx:tag><![CDATA[value]]></gsx:tag>",
|
||||
new DocsTagBuilder(false).append("tag", "value").build());
|
||||
}
|
||||
|
||||
public void testMultiAppend() {
|
||||
String actual = new DocsTagBuilder(false)
|
||||
.append("tag1", "value1")
|
||||
.append("tag2", "value2")
|
||||
.build();
|
||||
|
||||
assertEquals("<gsx:tag1><![CDATA[value1]]></gsx:tag1>"
|
||||
+ "<gsx:tag2><![CDATA[value2]]></gsx:tag2>", actual);
|
||||
}
|
||||
|
||||
public void testAppendLargeUnits_imperial() {
|
||||
assertEquals("<gsx:tag><![CDATA[62.14]]></gsx:tag>",
|
||||
new DocsTagBuilder(false).appendLargeUnits("tag", 100.0).build());
|
||||
}
|
||||
|
||||
public void testAppendLargeUnits_metric() {
|
||||
assertEquals("<gsx:tag><![CDATA[100.00]]></gsx:tag>",
|
||||
new DocsTagBuilder(true).appendLargeUnits("tag", 100.0).build());
|
||||
}
|
||||
|
||||
public void testAppendSmallUnits_imperial() {
|
||||
assertEquals("<gsx:tag><![CDATA[328]]></gsx:tag>",
|
||||
new DocsTagBuilder(false).appendSmallUnits("tag", 100.0).build());
|
||||
}
|
||||
|
||||
public void testAppendSmallUnits_metric() {
|
||||
assertEquals("<gsx:tag><![CDATA[100]]></gsx:tag>",
|
||||
new DocsTagBuilder(true).appendSmallUnits("tag", 100.0).build());
|
||||
}
|
||||
}
|
||||