diff --git a/MyTracks/.classpath b/MyTracks/.classpath index 3d5ddb422..0734acf85 100644 --- a/MyTracks/.classpath +++ b/MyTracks/.classpath @@ -6,7 +6,8 @@ - + + diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index 6f3b16692..51487fe03 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -56,11 +56,6 @@ limitations under the License. android:label="MyTracks" android:exported="false" /> - - diff --git a/MyTracks/MyTracks.launch b/MyTracks/MyTracks.launch index 482b143e3..5d64dfff5 100644 --- a/MyTracks/MyTracks.launch +++ b/MyTracks/MyTracks.launch @@ -1,4 +1,4 @@ - + @@ -16,8 +16,8 @@ - + diff --git a/MyTracks/libs/google-api-client-1.2.1-alpha.jar b/MyTracks/libs/google-api-client-1.2.1-alpha.jar new file mode 100644 index 000000000..cf824376b Binary files /dev/null and b/MyTracks/libs/google-api-client-1.2.1-alpha.jar differ diff --git a/MyTracks/res/layout/list.xml b/MyTracks/res/layout/list.xml deleted file mode 100644 index 33567b8d2..000000000 --- a/MyTracks/res/layout/list.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/MyTracks/res/layout/mytracks_send_to_google.xml b/MyTracks/res/layout/mytracks_send_to_google.xml index a55553367..276bd58ac 100644 --- a/MyTracks/res/layout/mytracks_send_to_google.xml +++ b/MyTracks/res/layout/mytracks_send_to_google.xml @@ -42,7 +42,7 @@ limitations under the License. android:layout_marginTop="5dip" android:layout_marginLeft="5dip"> - - - - - - diff --git a/MyTracks/res/values-ca/strings.xml b/MyTracks/res/values-ca/strings.xml index 1a001c3e2..b7b3376b1 100644 --- a/MyTracks/res/values-ca/strings.xml +++ b/MyTracks/res/values-ca/strings.xml @@ -54,7 +54,6 @@ limitations under the License. S\'ha enviat la ruta a maps.google.com. S\'ha enviat la ruta a un full de càlcul a docs.google.com. S\'ha enviat la ruta a docs.google.com. - Poden passar uns minuts abans que les rutes noves apareguin. 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. Nota: Es mostra una ubicació determinada a partir de les xarxes sense fils i antenes telefòniques. 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. @@ -104,6 +103,10 @@ limitations under the License. Error Fet Punt de referència + Cancel·la + Tanca + Edita + Desa (Inici) @@ -241,28 +244,6 @@ limitations under the License. Satèl·lits GPS - - Seleccioneu un mapa del vostre compte: - Crea un mapa nou... - Envia a Google My Maps - S\'estan carregant els mapes... - No s\'han pogut carregar els vostres mapes en aquest moment - Selecciona aquest mapa - Edita els detalls del mapa - Comparteix el mapa amb amics - Comparteix la ruta amb amics - Elimina el mapa del vostre compte - Títol del mapa: - Descripció del mapa: - Fes que aquest mapa i els seus continguts apareguen als resultats de cerca - 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 - Aquest mapa i els seus continguts es mostraran als resultats de cerca - 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. - Tanca - Edita - Cancel·la - Desa - M\'agradaria compartir un mapa amb tu Crec que estaràs interessat en aquest mapa: @@ -272,6 +253,7 @@ limitations under the License. Crec que estaràs interessat en aquesta ruta. Per verue aquesta ruta fes servir Google Earth o altre programari de mapes. + Comparteix la ruta amb amics Públic diff --git a/MyTracks/res/values-de/strings.xml b/MyTracks/res/values-de/strings.xml index 94ba8ecb2..893d3d5d0 100644 --- a/MyTracks/res/values-de/strings.xml +++ b/MyTracks/res/values-de/strings.xml @@ -35,7 +35,7 @@ limitations under the License. Es wurde kein Dienst zur Positionsermittlung auf diesem Gerät gefunden. Es wurde kein GPS-Empfänger auf diesem Gerät gefunden. - Beim Senden des Tracks an Google MyMaps ist ein Fehler aufgetreten. Bitte versuche es später noch einmal. + Beim Senden des Tracks an Google Fusion Tables ist ein Fehler aufgetreten. Bitte versuche es später noch einmal. Beim Senden des Tracks an Google Docs ist ein Fehler aufgetreten. Bitte versuche es später noch einmal. Start der Aufzeichnung fehlgeschlagen. Bitte versuche es noch einmal. Anhalten unmöglich. Bitte versuche es noch einmal. @@ -50,11 +50,10 @@ limitations under the License. Warte auf ein besseres GPS-Signal. Bitte stelle sicher, dass der Himmel sichtbar ist. Ein neuer Track wird aufgezeichnet. Die Aufzeichnung wurde angehalten. - Eine neue Karte wurde auf maps.google.com erstellt. + Eine neue Tabelle wurde auf google.com/fusiontables erstellt. Dein Track wurde auf maps.google.com gespeichert. Dein Track wurde in einer neuen Tabelle bei docs.google.com gespeichert. Dein Track wurde an docs.google.com gesendet. - Manchmal dauert es ein paar Minuten, bis neue Tracks bei maps.google.com erscheinen. 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. Anmerkung: Eine ungefähre Position wird angezeigt, bestimmt mittels Mobilfunkstationen und WiFi-Netzwerken. Die Position kann derzeit nicht bestimmt werden. Aktiviere mehr Positionsquellen (siehe Sicherheits- und Positionseinstellungen) und stelle sicher, dass der Himmel sichtbar ist. @@ -66,11 +65,11 @@ limitations under the License. Dein Track wird an Google gesendet... Lese Track... Bereite Track vor... - Anmeldung bei Google Maps... + Anmeldung bei Google Fusion Tables... Anmeldung bei Google Docs... Track wird an Google Docs gesendet... - Track wird an Google Maps gesendet... - Neue \'Meine Karte\' wird erstellt... + Track wird an Google Fusion Tables gesendet... + Neue \'Fusion Tabelle\' wird erstellt... Importiere GPS-Track. Das kann ein wenig dauern. Schreibe Datei auf SD-Karte. Das kann ein wenig dauern. Aktualisiere Trackstatistik... @@ -81,7 +80,7 @@ limitations under the License. Dieser Track und seine Wegpunkte werden endgültig von diesem Gerät gelöscht. Dieser Wegpunkt wird endgültig von diesem Gerät gelöscht. - + Erstellt von My Tracks für Android. Erstellt von <a href=\'http://mytracks.appspot.com\'>My Tracks</a> für Android. Neue Karte @@ -102,7 +101,10 @@ limitations under the License. Fehler Erfolgreich Wegpunkt - + Abbrechen + Bearbeiten + Sichern + (Beginn) (Ende) @@ -129,7 +131,7 @@ limitations under the License. Alle Tracks Dieser Track Mit Freunden teilen… - Ein Link zur Google MyMaps-Karte + Ein Link zur Google Fusion Tables-Karte Eine GPX-Datei Eine KML-Datei Eine CSV-Datei @@ -236,30 +238,9 @@ limitations under the License. GPS-Satelliten - - - Wähle eine Karte von Deinem Konto aus - Neue Karte erstellen… - An Google My Maps senden - Karten werden geladen… - Deine Karten konnten im Augenblick nicht geladen werden! - Diese Karte auswählen - Kartendetails bearbeiten - Karte mit Freunden teilen - Track mit Freunden teilen - Karte vom Konto löschen - Kartentitel - Trackbeschreibung - Diese Karte und ihren Inhalt in Suchergebnissen sichtbar machen. - Diese Karte und ihren Inhalt nicht in Suchergebnissen sichtbar machen. - Diese Karte und ihr Inhalt sind in Suchergebnissen sichtbar. - Weder diese Karte noch ihr Inhalt sind in Suchergebnissen sichtbar, allerdings kann sie trotzdem von anderen durch einen direkten Link angezeigt werden. - Schließen - Bearbeiten - Abbrechen - Sichern - + + Karte mit Freunden teilen Ich möchte gerne eine Karte mit Dir teilen. Vielleicht interessierst Du Dich für diese Karte: @@ -267,13 +248,14 @@ limitations under the License. Ich möchte einen GPS-Track mit Dir teilen Vielleicht interessierst Du Dich für diese Track: - %s. - - - Öffentlich - Nicht in Suchergebnissen enthalten. - Du hast noch keine Karten. - + %s. + Track mit Freunden teilen + + + Öffentlich + Nicht in Suchergebnissen enthalten. + Du hast noch keine Karten. + Durchschnitt in Bew. @@ -361,8 +343,6 @@ limitations under the License. Senden an… Jetzt senden - Neue Karte erstellen - Eine Karte auswählen Trackstatistik senden Statistik und Punkte senden diff --git a/MyTracks/res/values-es/strings.xml b/MyTracks/res/values-es/strings.xml index 7a58e15e8..7f0ec7670 100644 --- a/MyTracks/res/values-es/strings.xml +++ b/MyTracks/res/values-es/strings.xml @@ -51,7 +51,6 @@ limitations under the License. Su recorrido ha sido enviado a maps.google.com. Su recorrido ha sido enviado a una nueva hoja de cálculo en docs.google.com. Su recorrido ha sido enviado a docs.google.com. - A veces nuevos recorridos pueden tardar algunos minutos en aparecer. 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. Nota: Una ubicación aproximada es mostrada, basada en informacion de torres de telefonía celular y red WIFI. 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. @@ -98,6 +97,7 @@ limitations under the License. Sí Éxito Señal de recorrido + Cancelar (Inicio) @@ -193,30 +193,10 @@ limitations under the License. Satélites GPS - - - Elija un mapa de su cuenta: - Crear mapa nuevo... - Enviar a Google My Maps - Cargando mapas... - Sus mapas no pudieron ser cargados en este momento! - Seleccione este mapa - Editar detalles del mapa - Compartir mapa con amigos Compartir recorrido con amigos - Borrar mapa de su cuenta - Título del mapa: - Descripción del mapa: - Permitir que este mapa y su contenido sean mostrados en resultados de búsqueda - No permitir que este mapa y su contenido sean mostrados en resultados de búsqueda, pero permitir acceso directo mediante su URL - Este mapa y su contenido serán mostrados en resultados de búsqueda - No mostrar ni este mapa ni sus contenidos en resultados de búsqueda, pero permitir acceso directo mediante su URL - Cerrar - Editar - Cancelar - Guardar + Compartir mapa con amigos Me gustaría compartir un mapa contigo Creo que este mapa pudiera interesarte: @@ -225,6 +205,7 @@ limitations under the License. Creo que este recorrido pudiera interesarte. Para ver este recorrido usa Google Earth o algún otro software de mapeo. + Compartir recorrido con amigos Público @@ -315,8 +296,6 @@ limitations under the License. Enviar a... Enviar ahora - Crear un nuevo mapa - Escoja un mapa existente Enviar estadísticas de recorrido Enviar estadísticas y puntos diff --git a/MyTracks/res/values-fr/strings.xml b/MyTracks/res/values-fr/strings.xml index bbe44c007..bf588b041 100644 --- a/MyTracks/res/values-fr/strings.xml +++ b/MyTracks/res/values-fr/strings.xml @@ -53,7 +53,6 @@ limitations under the License. Votre parcours a été envoyé à maps.google.com. Votre parcours a été envoyé dans un tableur à docs.google.com. Votre parcours a été envoyé à docs.google.com. - Votre parcours peut prendre quelques minutes avant d\'être visible. 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. Note : Votre position est approximative, elle a été déterminée par les antennes de téléphonie ou le réseau WiFi. 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. @@ -100,6 +99,10 @@ limitations under the License. Oui Erreur Point de passage + Annuler + Fermer + Modifier + Enregistrer (Début) @@ -194,29 +197,8 @@ limitations under the License. Satellites GPS - - Choisissez une carte de votre compte : - Création d\'une nouvelle carte en cours... - Envoyer à Google Mes cartes - Chargements de vos cartes... - Vos cartes n\'ont pu être chargées - Sélectionnez la carte - Consulter les détails de la carte - Partager. - Partager des parcours. - Supprimer la carte de votre compte. - Titre de la carte : - Description de la carte : - Rendre cette carte et son contenu visibles dans les résultats de recherche. - 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 - Cette carte et son contenu seront visibles dans les résultats de recherche - Ni cette carte, ni son contenu seront visible dans les résultats de recherche, mais ils seront consultables en utilisant l\'adresse URL exacte - Fermer - Modifier - Annuler - Enregistrer - + Partager. Je voudrais partager ma carte avec vous Je pense que cette carte vous intéressera : @@ -225,6 +207,7 @@ limitations under the License. Je pense que ce parcours vous intéressera. Pour voir ce parcours, utilisez Google Earth ou tout autre logiciel de cartographie. + Partager des parcours. Carte non listée diff --git a/MyTracks/res/values-it/strings.xml b/MyTracks/res/values-it/strings.xml index 2fe4ff4ba..0deba74fe 100644 --- a/MyTracks/res/values-it/strings.xml +++ b/MyTracks/res/values-it/strings.xml @@ -54,7 +54,6 @@ limitations under the License. Il tuo percorso è stato inviato a maps.google.com. Il tuo percorso è stato inviato ad un nuovo foglio di calcolo su docs.google.com. Il tuo percorso è stato inviato a docs.google.com. - Potrebbe essere necessario aspettare qualche minuto prima che i nuovi percorsi siano visibili. Premi qui per andare a «Impostazioni di posizione e protezione» e attiva «Utilizza satelliti GPS». Poi premi il tasto di ritorno per tornare qui. Nota: la posizione indicata è un\'approssimazione determinata in base alle antenne cellulari e reti WIFI vicine. 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. @@ -104,6 +103,10 @@ limitations under the License. Errore Successo Punto di passaggio + Annulla + Chiudi + Modifica + Salva (Inizio) @@ -200,28 +203,6 @@ limitations under the License. Satelliti GPS - - Scegli una mappa dal tuo account: - Crea una nuova mappa... - Invia a «Le mie mappe» di Google - Caricamento mappe in corso... - Le tue mappe non possono momentaneamente essere caricate! - Scegli questa mappa - Modifica dettagli della mappa - Condividi la mappa con amici - Condividi il percorso con amici - Elimina la mappa dal tuo account - Titolo della mappa: - Descrizione della mappa: - Rendi questa mappa e il suo contenuto visibili fra i risultati di ricerche - Rendi questa mappa e il suo contenuto invisibili fra i risultati di ricerche. Resteranno comunque visibili ad altri utilizzando l\'indirizzo URL esatto - Questa mappa e il suo contenuto saranno visibili fra i risultati di ricerche - Né questa mappa né il suo contenuto saranno visibili in risultati di ricerche, ma saranno comunque visibili ad altri tramite un link diretto. - Chiudi - Modifica - Annulla - Salva - Vorrei condividere una mappa con te Credo che questa mappa potrebbe interessarti: @@ -231,6 +212,7 @@ limitations under the License. Credo che questo percorso potrebbe interessarti. Per vedere questo percorso, usa Google Earth o un altro software di cartografia. + Condividi il percorso con amici Pubblica diff --git a/MyTracks/res/values-ja/strings.xml b/MyTracks/res/values-ja/strings.xml index 6a5745f77..9f80638dc 100644 --- a/MyTracks/res/values-ja/strings.xml +++ b/MyTracks/res/values-ja/strings.xml @@ -52,7 +52,6 @@ limitations under the License. トラックを maps.google.com に送信しました. トラックを docs.google.com の新しいスプレッドシートに送信しました. トラックを docs.google.com に送信しました. - 新しいトラックが見えるようになるまで数分程度かかります. ここをタップして,セキュリティ &位置情報 を開いて,「GPS機能をONにする」にチェックして下さい.バックボタンで戻ってください. ご注意: 携帯電話中継局とWi-Fiネットワークから大まかな位置を特定します. 現在位置決定ができません.位置取得源を増やし(セキュリティ&位置情報をご覧ください),空が見えるようにして下さい.もう一度試して下さい. @@ -102,6 +101,10 @@ limitations under the License. エラー 成功 ウェイポイント + キャンセル + 閉じる + 編集 + 保存 (開始) @@ -201,30 +204,6 @@ limitations under the License. GPS衛星 - - ご使用のアカウントからのマップ選択: - 新規マップ... - Googleマイマップへの送信 - マップの読み込み中... - マップを読み込めませんでした! - このマップを選択 - マップ詳細の編集 - マップの共有 - トラックの共有 - アカウントからマップの削除 - マップタイトル: - マップ説明: - - - このマップとコンテンツを検索結果に表示させる - このマップとコンテンツはどちらも検索結果に表示させない(直接URLで他の方が見ることは可能) - このマップとコンテンツは検索結果に表示されます - このマップとコンテンツはどちらも検索結果に表示されませんが,直接リンクで他の方が見ることができます. - 閉じる - 編集 - キャンセル - 保存 - マップを共有したいと思います このマップに興味を持って頂けると思います: @@ -234,6 +213,7 @@ limitations under the License. このトラックに興味を持って頂けると思います. このトラックを閲覧するには,Google Earthまたは他のマッピングソフトウェアが必要です. + トラックの共有 公開 diff --git a/MyTracks/res/values-pl/strings.xml b/MyTracks/res/values-pl/strings.xml index 017176bb3..8a639cbdf 100644 --- a/MyTracks/res/values-pl/strings.xml +++ b/MyTracks/res/values-pl/strings.xml @@ -53,7 +53,6 @@ limitations under the License. Twoja trasa został wysłana do maps.google.com. Twoja trasa została wysłana do nowego arkusza na docs.google.com. Twoja trasa została wysłana do docs.google.com. - Trasa będzie widoczna za kilka minut. 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ć. Uwaga: używana jest przybliżona lokalizacja ustalona za pomocą stacji komórkowych i sieci WIFI. 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. @@ -103,6 +102,10 @@ limitations under the License. Błąd Powodzenie Punkt orientacyjny + Anuluj + Zamknij + Edytuj + Zapisz (Początek) @@ -233,28 +236,6 @@ limitations under the License. Satelity GPS - - Wybierz mapę z twojego konta: - Utwórz nową mapę... - Wyślij do Moich Map Google - Wczytywanie map... - Nie można wczytać twoich map! - Wybierz tę mapę - Edytuj szczegóły mapy - Podziel się mapą z przyjaciółmi - Podziel się trasą z przyjaciółmi - Usuń mapę z twojego konta - Tytuł mapy: - Opis mapy: - Pokazuj tę pamę i jej zawartość w wynikach wyszukiwania - Nie pokazuj tej mapy i jej zawartości w wynikach wyszukiwania. Dostęp do mapy będzie ciągle możliwy poprzez odnośnik - Ta mapa i jej zawartość będzie dostępna w wynikach wyszukiwania - Mapa i jej zawartość nie będzie dostępna w wynikach wyszukiwania, ale będzie dostępna przez bezpośredni odnośnik - Zamknij - Edytuj - Anuluj - Zapisz - Chciałbym się z Tobą podzielić mapą Myślę, że może Cię zainteresować ta mapa: @@ -264,6 +245,7 @@ limitations under the License. Myślę, że może Cię zainteresować ta trasa. Trasę możesz obejrzeć w Google Earth lub innym programie do obsługi map. + Podziel się trasą z przyjaciółmi Publiczna diff --git a/MyTracks/res/values-pt-rBR/strings.xml b/MyTracks/res/values-pt-rBR/strings.xml index f84c710ce..e3ecbfa89 100644 --- a/MyTracks/res/values-pt-rBR/strings.xml +++ b/MyTracks/res/values-pt-rBR/strings.xml @@ -52,7 +52,6 @@ limitations under the License. Seu caminho foi enviado para maps.google.com. Seu caminho foi enviado para uma nova planilha em docs.google.com. Seu caminho foi enviado para docs.google.com. - "Pode demorar alguns minutor para que novos caminhos apareçam. "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." Nota: Uma localização aproximada, determinada por torres de telefonia celular, está sendo mostrada. 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. @@ -101,6 +100,10 @@ limitations under the License. Erro Successo Ponto de passagem + Cancelar + Fechar + Editar + Salvar (Início) @@ -184,28 +187,6 @@ limitations under the License. Satélites de GPS - - Escolha um mapa da sua conta: - Criar novo mapa... - Enviar para o Google Meus Mapas - Carregando mapas... - Seus mapas não puderam ser carregados neste momento! - Selecionar este Mapa - Editar detalhes do mapa - Compartilhar mapa com amigos - Compartilhar caminho com amigos - Apagar mapa da sua conta - Título do Mapa: - Descrição do Mapa: - Fazer com que o conteúdo deste mapa apareça em resultados de buscas - 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 - Este mapa e seu conteúdo vão aparecer em resultados de buscas - 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 - Fechar - Editar - Cancelar - Salvar - Eu gostaria de compartilhar um mapa com você Eu acredito que você pode se interessar por este mapa: @@ -215,6 +196,7 @@ limitations under the License. Eu acredito que você pode se interesasr por este caminho. Para visualizar este caminho, use o Google Earth ou outro software de mapas. + Compartilhar caminho com amigos Público diff --git a/MyTracks/res/values-sv/strings.xml b/MyTracks/res/values-sv/strings.xml index 3b18a5f95..4cefa2706 100644 --- a/MyTracks/res/values-sv/strings.xml +++ b/MyTracks/res/values-sv/strings.xml @@ -53,7 +53,6 @@ limitations under the License. Ditt spår har skickats till maps.google.com. Ditt spår har skickats till ett nytt kalkylblad på docs.google.com. Ditt spår har skickats till docs.google.com. - Det kan ibland ta några minuter innan nya spår visas. 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å. OBS: En ungefärlig position som avgörs från mobilmaster och WIFI-nät visas. 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. @@ -103,6 +102,10 @@ limitations under the License. Fel Lyckades Positionsmärke + Avbryt + Stäng + Ändra + Spara (Start) @@ -202,28 +205,6 @@ limitations under the License. GPS-satelliter - - Välj en karta från ditt konto: - Skapa en ny karta... - Skicka till Google Mina Kartor - Laddar kartor... - Dina kartor kunde inte laddas just nu! - Välj den här kartan - Ändra kartdetaljer - Dela kartan med vänner - Dela spår med vänner - Ta bort kartan från ditt konto - Karttitel: - Kartbeskrivning: - Gör så att kartan och dess innehåll visas i sökresultat - Låt inte kartan och dess innehåll visas i sökresultat, men de kan ändå ses av andra genom en direktlänk - Kartan och dess innehåll kommer att visas i sökresultat - Varken kartan eller dess innehåll visas i sökresultat, men de kan ändå ses av andra genom en direktlänk - Stäng - Ändra - Avbryt - Spara - Jag vill dela en karta med dig Jag tror att du skulle vara intresserad av den här kartan: @@ -233,6 +214,7 @@ limitations under the License. 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. + Dela spår med vänner Publik diff --git a/MyTracks/res/values-zh-rCN/strings.xml b/MyTracks/res/values-zh-rCN/strings.xml index cacf8422c..72c2576e9 100644 --- a/MyTracks/res/values-zh-rCN/strings.xml +++ b/MyTracks/res/values-zh-rCN/strings.xml @@ -56,7 +56,6 @@ limitations under the License. 你的轨迹已经发送到 maps.google.com. 你的轨迹已经作为新的电子表格发送到 docs.google.com. 你的轨迹已经发送到 docs.google.com. - 新增的轨迹可能需要等待一段时间才会显示到页面上. 请点击这里进入\'安全和位置设置\'并勾选上\'启用 GPS 卫星\'. 然后用后退键返回. 注意: 当前显示的位置是根据无线信号和无线网络计算的近似位置. 目前无法定位. 请启用更多的位置来源 (在\'安全和位置设置\'中) 并确认天空是可见的. 再重试. @@ -106,6 +105,10 @@ limitations under the License. 错误 成功 路点 + 取消 + 关闭 + 编辑 + 保存 (开始) @@ -239,28 +242,6 @@ limitations under the License. GPS 卫星 - - 从你的帐户里选择地图: - 创建新地图... - 发送到 Google My Maps - 载入地图中... - 现在无法载入你的地图! - 选择此地图 - 编辑地图详细信息 - 与朋友分享地图 - 与朋友分享轨迹 - 删除你帐号下的此地图 - 地图标题: - 地图描述: - 允许此地图及其内容出现在搜索结果中 - 不允许地图及其内容出现在搜索结果中, 但它的内容依然可以通过网址直接访问 - 此地图及其内容会出现在搜索结果中 - 此地图及其内容不会出现在搜索结果中, 但它的内容依然可以通过网址直接访问 - 关闭 - 编辑 - 取消 - 保存 - 我想与你分享一张地图 我想你可能会对这个地图感兴趣: @@ -270,6 +251,7 @@ limitations under the License. 我想你可能会对这条轨迹感兴趣. 请用 Google Earth 或其它地图软件查看此轨迹. + 与朋友分享轨迹 公开 diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index d519d6a0e..fc3caca28 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -35,7 +35,7 @@ limitations under the License. No location manager was found on this device! No GPS location provider was found on this device. - There was an error while sending your track to Google My Map. Please try again later. + There was an error while sending your track to Google Fusion Tables. Please try again later. There was an error while sending your track to Google Docs. Please try again later. Unable to start recording. Try again. Unable to stop recording. Try again. @@ -50,11 +50,10 @@ limitations under the License. Waiting for a better GPS fix. Make sure the sky is visible. Now recording a new track. Stopped recording your track. - A new Google My Map has been created at maps.google.com. + A new Google Fusion table has been created at google.com/fusiontables. Your track has been sent to maps.google.com. Your track has been sent to a new spreadsheet at docs.google.com. Your track has been sent to docs.google.com. - It may sometimes take a few minutes before new tracks appear. Please tap here to go to Security and Location Settings and check \'enable GPS satellites\'. Press back button to return. Note: An approximate location as determined from cell towers and WIFI networks is shown. 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. @@ -66,11 +65,11 @@ limitations under the License. Sending your track to Google. Reading track... Preparing track... - Authenticating to Google Maps... + Authenticating to Google Fusion Tables... Authenticating to Google Docs... Sending track to Google Docs... - Sending track to Google Maps... - Creating new My Map... + Sending track to Fusion Tables... + Creating new Fusion table... Importing a GPS track. This may take a while. Writing file to SD card. This may take a while. Updating track statistics... @@ -81,10 +80,9 @@ limitations under the License. The track and its markers will be permanently deleted from this phone. The marker will be permanently deleted from this phone. - + Created by My Tracks on Android. - Created by <a href=\'http://mytracks.appspot.com\'>My Tracks</a> on Android. - New map + Created by <a href=\'http://mytracks.appspot.com\'>My Tracks</a> on Android. Points @@ -105,6 +103,10 @@ limitations under the License. Success Waypoint License + Cancel + Close + Edit + Save (Start) @@ -112,8 +114,8 @@ limitations under the License. Show - Send to Google My Maps - Send to New Google My Map + Send to Google Fusion Tables + Send to New Google Fusion Table Send to Google Docs Send to Google... Delete from phone... @@ -123,7 +125,7 @@ limitations under the License. Save a KML file Save a CSV file Save a TCX file - Send to Google My Maps + Send to Google Fusion Tables Delete all tracks Export all tracks @@ -132,7 +134,7 @@ limitations under the License. All Tracks: This Track Share with Friends... - A link to Google My Map + A link to Google Fusion Tables A GPX file A KML file A CSV file @@ -242,37 +244,17 @@ limitations under the License. GPS Satellites - - Pick a map from your account: - Create new map... - Send to Google My Maps - Loading maps... - Your maps could not be loaded at this time! - Select this Map - Edit Map Details - Share Map with Friends - Share Track with Friends - Delete Map from your Account - Map Title: - Map Description: - Make this map and its contents show up in search results - 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 - This map and its contents will show in search results - Neither this map nor its contents will show in search results, but they may still be viewed by others through a direct link - Close - Edit - Cancel - Save - - I would like to share a map with you - I think you might be interested in this map: + Share map with friends + I would like to share a track with you + I think you might be interested in this track: %s I would like to share a track with you I think you might be interested in this track. To view this track use Google Earth or other mapping software. + Share track with friends Public @@ -384,11 +366,9 @@ limitations under the License. Send to... - Google My Maps + Google Fusion Tables Google Docs Send now - Create a new map - Pick an existing map Send track statistics Send statistics and points diff --git a/MyTracks/src/com/google/android/apps/mymaps/MyMapsConstants.java b/MyTracks/src/com/google/android/apps/mymaps/MyMapsConstants.java deleted file mode 100644 index d88a2ff5a..000000000 --- a/MyTracks/src/com/google/android/apps/mymaps/MyMapsConstants.java +++ /dev/null @@ -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() { } -} diff --git a/MyTracks/src/com/google/android/apps/mymaps/MyMapsFeature.java b/MyTracks/src/com/google/android/apps/mymaps/MyMapsFeature.java deleted file mode 100644 index dba928ac5..000000000 --- a/MyTracks/src/com/google/android/apps/mymaps/MyMapsFeature.java +++ /dev/null @@ -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 latitudeE6 = new Vector(); - - /** - * The longitudes of the points of this feature in order, specified in - * millionths of a degree east. - */ - private final Vector longitudeE6 = new Vector(); - - /** 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); - } -} diff --git a/MyTracks/src/com/google/android/apps/mymaps/MyMapsFeatureMetadata.java b/MyTracks/src/com/google/android/apps/mymaps/MyMapsFeatureMetadata.java deleted file mode 100644 index 996f4c743..000000000 --- a/MyTracks/src/com/google/android/apps/mymaps/MyMapsFeatureMetadata.java +++ /dev/null @@ -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); - } -} diff --git a/MyTracks/src/com/google/android/apps/mymaps/MyMapsGDataConverter.java b/MyTracks/src/com/google/android/apps/mymaps/MyMapsGDataConverter.java deleted file mode 100644 index 0c726f4c5..000000000 --- a/MyTracks/src/com/google/android/apps/mymaps/MyMapsGDataConverter.java +++ /dev/null @@ -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; - } -} diff --git a/MyTracks/src/com/google/android/apps/mymaps/MyMapsGDataWrapper.java b/MyTracks/src/com/google/android/apps/mymaps/MyMapsGDataWrapper.java deleted file mode 100644 index 365edb94f..000000000 --- a/MyTracks/src/com/google/android/apps/mymaps/MyMapsGDataWrapper.java +++ /dev/null @@ -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; - } -} diff --git a/MyTracks/src/com/google/android/apps/mymaps/MyMapsList.java b/MyTracks/src/com/google/android/apps/mymaps/MyMapsList.java deleted file mode 100644 index 27c881efe..000000000 --- a/MyTracks/src/com/google/android/apps/mymaps/MyMapsList.java +++ /dev/null @@ -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 mapsList; - private Vector publicList; - private Set 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 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(); - publicList = new Vector(); - - 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(); - - 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); - } -} diff --git a/MyTracks/src/com/google/android/apps/mymaps/MyMapsMapMetadata.java b/MyTracks/src/com/google/android/apps/mymaps/MyMapsMapMetadata.java deleted file mode 100644 index d5531cf4a..000000000 --- a/MyTracks/src/com/google/android/apps/mymaps/MyMapsMapMetadata.java +++ /dev/null @@ -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); - } -} diff --git a/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java b/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java index 5aeeb3e23..e8512ac3b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/DialogManager.java @@ -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) { diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java index 670072310..34f6a7361 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java @@ -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 diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java index bcef4ff99..b9d11009b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java @@ -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. */ diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java index d4ad35b14..758c0f550 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java @@ -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, diff --git a/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java b/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java index e3f5b5a60..f39be1d84 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java +++ b/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java @@ -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(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/WelcomeActivity.java b/MyTracks/src/com/google/android/apps/mytracks/WelcomeActivity.java index cfbab6d73..45251a3b7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/WelcomeActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/WelcomeActivity.java @@ -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)); } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java index 247c2bfa2..04132b9ce 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java @@ -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"); } } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java index b0b7789b3..10f1a0044 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java @@ -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 docListWrapper = new GDataWrapper(); docListWrapper.setAuthManager(docListAuth); docListWrapper.setRetryOnAuthFailure(true); - GDataWrapper trixWrapper = new GDataWrapper(); + GDataWrapper trixWrapper = new GDataWrapper(); trixWrapper.setAuthManager(trixAuth); trixWrapper.setRetryOnAuthFailure(true); @@ -304,5 +305,4 @@ public class SendToDocs { } return true; } - } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToFusionTables.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToFusionTables.java new file mode 100644 index 000000000..aa957adc1 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToFusionTables.java @@ -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 distances = new Vector(); + Vector elevations = new Vector(); + DoubleBuffer elevationBuffer = new DoubleBuffer(MyTracksConstants.ELEVATION_SMOOTHING_FACTOR); + + List locations = new ArrayList(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("

" + originalDescription + "

" + + stringUtils.generateTrackDescription(track, distances, elevations) + + "

"); + 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(""); + appendCoordinate(location, builder); + builder.append(""); + 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(""); + for (Location location : track.getLocations()) { + appendCoordinate(location, builder); + builder.append(' '); + } + builder.append(""); + return builder.toString(); + } + + private boolean prepareAndUploadPoints(Track track, List 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 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 prepareLocations( + Track track, Iterable locations) { + ArrayList splitTracks = new ArrayList(); + + // 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 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 wrapper = new GDataWrapper(); + wrapper.setAuthManager(auth); + wrapper.setRetryOnAuthFailure(true); + wrapper.setClient(transport); + Log.d(MyTracksConstants.TAG, "GData connection prepared: " + this.auth); + wrapper.runQuery(new QueryFunction() { + @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; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java deleted file mode 100644 index 0af4f754a..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java +++ /dev/null @@ -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("

" + track.getDescription() + "

" - + stringUtils.generateTrackDescription(track, null, null) + "

"); - 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 distances = new Vector(); - Vector elevations = new Vector(); - DoubleBuffer elevationBuffer = - new DoubleBuffer(MyTracksConstants.ELEVATION_SMOOTHING_FACTOR); - - List locations = new ArrayList(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("

" + originalDescription + "

" - + stringUtils.generateTrackDescription( - track, distances, elevations) - + "

"); - 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 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 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 prepareLocations( - Track track, Iterable locations) { - ArrayList splitTracks = new ArrayList(); - - // 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 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", "
")); - 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); - } -} diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/BackupColumns.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/BackupColumns.java index 9fb3f739a..96de8cfa2 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/backup/BackupColumns.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/BackupColumns.java @@ -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, diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java b/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java index 0bf369a11..e46a2573d 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java @@ -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 docListWrapper, final String name) throws IOException { final AtomicReference idSaver = new AtomicReference(); - boolean result = docListWrapper.runQuery(new QueryFunction() { + boolean result = docListWrapper.runQuery(new QueryFunction() { @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 docListWrapper, final String title) throws IOException { final AtomicReference idSaver = new AtomicReference(); - boolean result = docListWrapper.runQuery(new QueryFunction() { + boolean result = docListWrapper.runQuery(new QueryFunction() { @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 trixWrapper, final String spreadsheetId) throws IOException { final AtomicReference idSaver = new AtomicReference(); - boolean result = trixWrapper.runQuery(new QueryFunction() { + boolean result = trixWrapper.runQuery(new QueryFunction() { @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() diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/gdata/GDataWrapper.java b/MyTracks/src/com/google/android/apps/mytracks/io/gdata/GDataWrapper.java index d45ec9002..751be06bb 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/gdata/GDataWrapper.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/gdata/GDataWrapper.java @@ -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 { + + 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 { + 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 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 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 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) { diff --git a/MyTracks/src/com/google/android/apps/mymaps/GeoRect.java b/MyTracks/src/com/google/android/apps/mytracks/util/GeoRect.java similarity index 97% rename from MyTracks/src/com/google/android/apps/mymaps/GeoRect.java rename to MyTracks/src/com/google/android/apps/mytracks/util/GeoRect.java index f854b55bf..6605f1453 100644 --- a/MyTracks/src/com/google/android/apps/mymaps/GeoRect.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/GeoRect.java @@ -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; } -} +} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java index 52f5dee72..4d6da6786 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java @@ -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() { } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java index 76004303f..b6b8baace 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java @@ -316,7 +316,7 @@ public class StringUtils implements DescriptionGenerator { + "", // Line 1 - context.getString(R.string.my_map_link), + context.getString(R.string.fusiontable_link), // Line 2 context.getString(R.string.total_distance_label), diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java index 4e90d7227..4d4ec7d8f 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java @@ -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)); } diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java index fe4c19453..cf963fff7 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java @@ -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; } diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/TracksColumns.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/TracksColumns.java index 6fb122b89..d3e0fae4d 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/TracksColumns.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/TracksColumns.java @@ -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"; }