Merging Fusion Tables changes.

This commit is contained in:
Rodrigo Damazio
2011-01-13 22:23:51 -02:00
43 changed files with 996 additions and 2484 deletions
+2 -1
View File
@@ -6,7 +6,8 @@
<classpathentry kind="lib" path="libs/accounts.jar"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="MyTracksLib_src"/>
<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>
-5
View File
@@ -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" />
+2 -2
View File
@@ -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"/>
Binary file not shown.
-103
View File
@@ -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>
+5 -23
View File
@@ -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>
+21 -41
View File
@@ -35,7 +35,7 @@ limitations under the License.
<!-- 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_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>
@@ -50,11 +50,10 @@ limitations under the License.
<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_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_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>
@@ -66,11 +65,11 @@ limitations under the License.
<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_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_mymaps">Track wird an Google Maps gesendet...</string>
<string name="progress_message_creating_map">Neue \'Meine Karte\' wird erstellt...</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>
@@ -81,7 +80,7 @@ limitations under the License.
<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 -->
<!-- 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 &lt;a href=\'http://mytracks.appspot.com\'&gt;My Tracks&lt;/a&gt; für Android.</string>
<string name="new_map_title">Neue Karte</string>
@@ -102,7 +101,10 @@ limitations under the License.
<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>
<!-- Placemark labels for start and end of track -->
<string name="start">(Beginn)</string>
<string name="end">(Ende)</string>
@@ -129,7 +131,7 @@ limitations under the License.
<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_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>
@@ -236,30 +238,9 @@ limitations under the License.
<!-- 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>
<string name="cancel">Abbrechen</string>
<string name="save">Sichern</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:
@@ -267,13 +248,14 @@ limitations under the License.
<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>
<!-- 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>
%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>
@@ -361,8 +343,6 @@ limitations under the License.
<!-- 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>
+3 -24
View File
@@ -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>
+6 -23
View File
@@ -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>
+5 -23
View File
@@ -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>
+5 -25
View File
@@ -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">ここをタップして,セキュリティ &amp;位置情報 を開いて,「GPS機能をONにする」にチェックして下さい.バックボタンで戻ってください.</string>
<string name="status_approximate_location">ご注意: 携帯電話中継局とWi-Fiネットワークから大まかな位置を特定します.</string>
<string name="status_no_location">現在位置決定ができません.位置取得源を増やし(セキュリティ&amp;位置情報をご覧ください),空が見えるようにして下さい.もう一度試して下さい.</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>
+5 -23
View File
@@ -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>
+5 -23
View File
@@ -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>
+5 -23
View File
@@ -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>
+5 -23
View File
@@ -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>
+20 -40
View File
@@ -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 &lt;a href=\'http://mytracks.appspot.com\'&gt;My Tracks&lt;/a&gt; on Android.</string>
<string name="new_map_title">New map</string>
<string name="fusiontable_link">Created by &lt;a href=\'http://mytracks.appspot.com\'&gt;My Tracks&lt;/a&gt; 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>
@@ -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>
@@ -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,20 +21,18 @@ 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;
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.SendToMyMaps;
import com.google.android.apps.mytracks.io.SendToDocs;
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.SendToDocs;
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;
@@ -123,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 = "";
/**
@@ -147,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;
/**
@@ -450,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);
}
@@ -550,6 +549,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
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);
@@ -558,7 +558,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
public void run() {
sendToDocsMessage = sender.getStatusMessage();
sendToDocsSuccess = sender.wasSuccess();
handleMapsFinish();
handleFusionTablesFinish(trackId);
}
});
}
@@ -571,42 +571,35 @@ 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()) {
@@ -616,16 +609,17 @@ public class MyTracks extends TabActivity implements OnTouchListener,
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);
@@ -656,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);
@@ -726,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) {
@@ -798,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,
@@ -813,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);
@@ -1042,17 +1037,11 @@ 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_DOCLIST, RESULT_OK,
new Intent());
@@ -1169,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) {
@@ -1195,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();
}
@@ -1218,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
@@ -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();
}
@@ -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");
}
}
}
@@ -29,6 +29,7 @@ import com.google.wireless.gdata.client.GDataClient;
import com.google.wireless.gdata.docs.DocumentsClient;
import com.google.wireless.gdata.docs.SpreadsheetsClient;
import com.google.wireless.gdata.maps.xml.XmlMapsGDataParserFactory;
import com.google.wireless.gdata.client.GDataServiceClient;
import android.app.Activity;
import android.content.SharedPreferences;
@@ -158,11 +159,11 @@ public class SendToDocs {
* @param track the track
*/
private boolean uploadToDocs(Track track) {
GDataWrapper docListWrapper = new GDataWrapper();
GDataWrapper<GDataServiceClient> docListWrapper = new GDataWrapper<GDataServiceClient>();
docListWrapper.setAuthManager(docListAuth);
docListWrapper.setRetryOnAuthFailure(true);
GDataWrapper trixWrapper = new GDataWrapper();
GDataWrapper<GDataServiceClient> trixWrapper = new GDataWrapper<GDataServiceClient>();
trixWrapper.setAuthManager(trixAuth);
trixWrapper.setRetryOnAuthFailure(true);
@@ -304,5 +305,4 @@ public class SendToDocs {
}
return true;
}
}
@@ -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);
}
}
@@ -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,
@@ -15,7 +15,6 @@
*/
package com.google.android.apps.mytracks.io.docs;
import com.google.android.apps.mymaps.MyMapsConstants;
import com.google.android.apps.mytracks.MyTracksConstants;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.io.AuthManager;
@@ -82,12 +81,13 @@ public class DocsHelper {
* returned.
*/
public String createSpreadsheet(final Context context,
final GDataWrapper docListWrapper, final String name) throws IOException {
final GDataWrapper<GDataServiceClient> docListWrapper, final String name) throws IOException {
final AtomicReference<String> idSaver = new AtomicReference<String>();
boolean result = docListWrapper.runQuery(new QueryFunction() {
boolean result = docListWrapper.runQuery(new QueryFunction<GDataServiceClient>() {
@Override
public void query(GDataServiceClient client) throws IOException {
public void query(GDataServiceClient client) throws IOException,
GDataWrapper.AuthenticationException {
// Construct and send request
URL url = new URL(DOCS_FEED_URL);
URLConnection conn = url.openConnection();
@@ -165,34 +165,41 @@ public class DocsHelper {
* 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 docListWrapper,
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() {
boolean result = docListWrapper.runQuery(new QueryFunction<GDataServiceClient>() {
@Override
public void query(GDataServiceClient client)
throws IOException, ParseException, HttpException {
throws IOException, GDataWrapper.ParseException, GDataWrapper.HttpException,
GDataWrapper.AuthenticationException {
GDataParser listParser;
listParser = client.getParserForFeed(Entry.class,
DOCS_MY_SPREADSHEETS_FEED_URL,
docListWrapper.getAuthManager().getAuthToken());
listParser.init();
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;
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.");
@@ -211,30 +218,36 @@ public class DocsHelper {
* ID.
* @throws IOException If an error occurs during the GData request.
*/
public String getWorksheetId(final GDataWrapper trixWrapper,
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() {
boolean result = trixWrapper.runQuery(new QueryFunction<GDataServiceClient>() {
@Override
public void query(GDataServiceClient client)
throws AuthenticationException, IOException, ParseException {
throws GDataWrapper.AuthenticationException, IOException, GDataWrapper.ParseException, GDataWrapper.HttpException {
String uri = String.format(DOCS_WORKSHEETS_URL_FORMAT, spreadsheetId);
GDataParser sheetParser =
((SpreadsheetsClient) client).getParserForWorksheetsFeed(uri,
trixWrapper.getAuthManager().getAuthToken());
sheetParser.init();
if (!sheetParser.hasMoreData()) {
Log.i(MyTracksConstants.TAG, "Found no worksheets");
return;
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);
}
// Grab the first worksheet.
WorksheetEntry worksheetEntry =
(WorksheetEntry) sheetParser.readNextEntry(new WorksheetEntry());
int lastSlash = worksheetEntry.getId().lastIndexOf('/');
idSaver.set(worksheetEntry.getId().substring(lastSlash + 1));
}
});
@@ -295,7 +308,7 @@ public class DocsHelper {
if (track.getMapId().length() > 0) {
tagBuilder.append("map", String.format("%s?msa=0&msid=%s",
MyMapsConstants.MAPSHOP_BASE_URL, track.getMapId()));
MyTracksConstants.MAPSHOP_BASE_URL, track.getMapId()));
}
String postText = new StringBuilder()
@@ -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) {
@@ -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";
}