Merging Sandor's changes

This commit is contained in:
Rodrigo Damazio
2010-08-24 17:02:36 -03:00
19 changed files with 1652 additions and 319 deletions
@@ -0,0 +1,12 @@
#Thu Aug 12 18:54:52 BRT 2010
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6
+282
View File
@@ -0,0 +1,282 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Task frequency -->
<string-array name="task_frequency_options">
<item>不使用</item>
<item>1 分</item>
<item>2 分</item>
<item>5 分</item>
<item>10 分</item>
<item>15 分</item>
<item>30 分</item>
<item>60 分</item>
</string-array>
<!-- Minimum recording distance -->
<string-array name="min_recording_distance_options">
<item>1 m</item>
<item>2 m</item>
<item>3 m</item>
<item>4 m</item>
<item>5 m (推奨)</item>
<item>10 m</item>
<item>100 m</item>
</string-array>
<string-array name="min_recording_distance_options_ft">
<item>3 ft</item>
<item>6 ft</item>
<item>10 ft</item>
<item>13 ft</item>
<item>16 ft (推奨)</item>
<item>33 ft</item>
<item>328 ft</item>
</string-array>
<!-- Maximum recording distance -->
<string-array name="max_recording_distance_options">
<item>50 m</item>
<item>100 m</item>
<item>200 m (推奨)</item>
<item>500 m</item>
<item>1000 m</item>
<item>5000 m</item>
</string-array>
<string-array name="max_recording_distance_options_ft">
<item>164 ft</item>
<item>328 ft</item>
<item>656 ft (推奨)</item>
<item>1640 ft</item>
<item>0.6 mi</item>
<item>3.1 mi</item>
</string-array>
<!-- Minimum recording interval -->
<string-array name="min_recording_interval_options">
<item>電源節約 (動的制御)</item>
<item>高精度 (動的制御)</item>
<item>最高 (推奨)</item>
<item>2 秒</item>
<item>3 秒</item>
<item>4 秒</item>
<item>5 秒</item>
<item>10 秒</item>
<item>20 秒</item>
<item>30 秒</item>
<item>1 分</item>
<item>2 分</item>
<item>5 分</item>
<item>10 分</item>
<item>15 分</item>
<item>30 分</item>
</string-array>
<!-- Minimum required accuracy -->
<string-array name="min_required_accuracy_options">
<item>10 m (GPS信号強度良好)</item>
<item>20 m</item>
<item>50 m</item>
<item>100 m</item>
<item>200 m (推奨)</item>
<item>500 m</item>
<item>1000 m</item>
<item>2000 m</item>
<item>5000 m (GPS信号強度不良)</item>
</string-array>
<string-array name="min_required_accuracy_options_ft">
<item>33 ft (GPS信号強度良好)</item>
<item>66 ft</item>
<item>165 ft</item>
<item>328 ft</item>
<item>656 ft (推奨)</item>
<item>1640 ft</item>
<item>0.6 mi</item>
<item>1.2 mi</item>
<item>3.1 mi (GPS信号強度不良)</item>
</string-array>
<!-- Split frequency -->
<string-array name="split_frequency_options">
<item>分割なし</item>
<item>100 km</item>
<item>50 km</item>
<item>25 km</item>
<item>10 km</item>
<item>5 km</item>
<item>1 km</item>
<item>60 分</item>
<item>30 分</item>
<item>15 分</item>
<item>10 分</item>
<item>5 分</item>
<item>2 分</item>
<item>1 分</item>
</string-array>
<string-array name="split_frequency_options_ft">
<item>分割なし</item>
<item>100 mi</item>
<item>50 mi</item>
<item>25 mi</item>
<item>10 mi</item>
<item>5 mi</item>
<item>1 mi</item>
<item>60 分</item>
<item>30 分</item>
<item>15 分</item>
<item>10 分</item>
<item>5 分</item>
<item>2 分</item>
<item>1 分</item>
</string-array>
<!-- Other non-preference arrays -->
<array name="activity_types">
<item>不明</item>
<item>混合タイプ</item>
<item>その他</item>
<item>実行中</item>
<item>市街地ランニング</item>
<item>トラックランニング</item>
<item>山道ランニング</item>
<item>徒歩</item>
<item>早歩き</item>
<item>ハイキング</item>
<item>山道ハイキング</item>
<item>原野ハイキング</item>
<item>登山</item>
<item>サイクリング</item>
<item>ロードバイク</item>
<item>マウンテンバイク</item>
<item>トラック自転車</item>
<item>スケート</item>
<item>インラインスケート</item>
<item>スケートボード</item>
<item>スノーシュー</item>
<item>スキー</item>
<item>スノーボード</item>
<item>ソリ</item>
<item>アイスセーリング</item>
<item>帆走</item>
<item>パドリング</item>
<item>カヤック</item>
<item>漕艇</item>
<item>モーターボート</item>
<item>フェリー</item>
<item>ラジコンボート</item>
<item>サーフィン</item>
<item>ウィンドサーフィン</item>
<item>カイトサーフィン</item>
<item>水上スキー</item>
<item>ウェークボード</item>
<item>飛行機</item>
<item>水上機</item>
<item>商用航空機</item>
<item>ヘリコプター</item>
<item>熱気球</item>
<item>飛行船</item>
<item>ラジコン飛行機</item>
<item>ラジコンヘリコプター</item>
<item>スカイダイビング</item>
<item>グライダー</item>
<item>ハンググライダー</item>
<item>パラグライダー</item>
<item>騎乗</item>
<item>乗馬</item>
<item>ロバ騎乗</item>
<item>自動車運転</item>
<item>バス運転</item>
<item>鉄道</item>
<item>オートバイ</item>
<item>オフロードバイク</item>
<item>四輪バギー</item>
<item>ランドセーリング</item>
<item>フリスビー</item>
<item>アルティメットフリスビー</item>
</array>
<array name="waypoint_types">
<item>ATM</item>
<item>銀行</item>
<item>飲み屋</item>
<item>バーベキュー</item>
<item>建物</item>
<item>バス停</item>
<item>カフェ</item>
<item>キャンプファイアー</item>
<item>キャンプ場</item>
<item>教会</item>
<item>コーヒーハウス</item>
<item>コンビニ</item>
<item>水飲み場</item>
<item>がけ崩れ</item>
<item>フェリー</item>
<item>火事場</item>
<item>消防署</item>
<item>釣り場</item>
<item>ガソリンスタンド</item>
<item>ゴルフ場</item>
<item>雑貨店</item>
<item>美容室</item>
<item>散髪屋</item>
<item>ハイキング道</item>
<item>ホテル</item>
<item>家</item>
<item>情報</item>
<item>交差</item>
<item>メールボックス</item>
<item>市場</item>
<item>集合場所</item>
<item>歌劇場</item>
<item>駐車場</item>
<item>郵便局</item>
<item>洞穴</item>
<item>警察署</item>
<item>踏切</item>
<item>レストラン</item>
<item>ショッピング</item>
<item>名所</item>
<item>店</item>
<item>プール</item>
<item>電話</item>
<item>劇場</item>
<item>トイレ</item>
<item>観光案内</item>
<item>トンネル</item>
<item>樹木</item>
<item>滝</item>
<item>噴水</item>
<item>ウェイポイント</item>
</array>
</resources>
+395
View File
@@ -0,0 +1,395 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Used by the Menu -->
<string name="start_recording">トラック記録</string>
<string name="stop_recording">記録停止</string>
<string name="switch_to_sat">衛星モード</string>
<string name="switch_to_map">地図モード</string>
<string name="mylocation">現在地</string>
<string name="list_tracks">トラック</string>
<string name="list_markers">マーカ</string>
<string name="delete_all_tracks">全トラック消去</string>
<string name="settings">設定</string>
<string name="elevation">標高</string>
<string name="choose_track_message">表示するトラックを選択して下さい</string>
<string name="current_track">現在のトラック</string>
<string name="current_segment">最終セグメント</string>
<!-- Error messages -->
<string name="error_no_location_manager">この端末でロケーションマネージャを発見できませんでした</string>
<string name="error_no_gps_location_provider">この端末でGPSロケーションプロバイダを発見できました</string>
<string name="error_sending_to_mymap">Googleマイマップへのトラック送信でエラーが発生しました.しばらく待って試して下さい.</string>
<string name="error_sending_to_docs">Google Docsへのトラック送信でエラーが発生しました.しばらく待って試して下さい.</string>
<string name="error_unable_to_start_recording">記録作成開始できません.もう一度試して下さい.</string>
<string name="error_unable_to_stop_recording">記録停止できません.もう一度試して下さい.</string>
<string name="error_unable_to_read_file">ファイルからの読み込みができません.</string>
<string name="error_invalid_gpx_format">ファイルは正しいGPX書式ではありませんでした.</string>
<string name="error_generic">エラーが発生しました.もう一度試して下さい.</string>
<string name="error_track_does_not_exist">トラックが存在しません.</string>
<string name="error_out_of_memory">大きなファイルです(500KB未満でなければなりません).終了します.</string>
<string name="error_unable_to_insert_marker">マーカの挿入ができません.位置決定ができません.</string>
<!-- Status messages -->
<string name="wait_for_fix">GPS位置精度がよくなるまで待機しています.空が見えるところにいて下さい.</string>
<string name="status_now_recording">新しいトラックを記録中です.</string>
<string name="status_stopped_recording">トラック記録を停止しました.</string>
<string name="status_new_mymap_has_been_created">新しいGoogleマイマップを maps.google.com に作成しました.</string>
<string name="status_tracks_have_been_uploaded">トラックを maps.google.com に送信しました.</string>
<string name="status_tracks_have_been_uploaded_to_new_doc">トラックを docs.google.com の新しいスプレッドシートに送信しました.</string>
<string name="status_tracks_have_been_uploaded_to_docs">トラックを docs.google.com に送信しました.</string>
<string name="status_mymap_info">新しいトラックが見えるようになるまで数分程度かかります.</string>
<string name="status_enable_gps">ここをタップして,セキュリティ &amp;位置情報 を開いて,「GPS機能をONにする」にチェックして下さい.バックボタンで戻ってください.</string>
<string name="status_approximate_location">ご注意: 携帯電話中継局とWi-Fiネットワークから大まかな位置を特定します.</string>
<string name="status_no_location">現在位置決定ができません.位置取得源を増やし(セキュリティ&amp;位置情報をご覧ください),空が見えるようにして下さい.もう一度試して下さい.</string>
<string name="status_waypoint_inserted">ウェイポイントマーカを挿入しました.</string>
<string name="status_statistics_inserted">統計マーカを挿入しました.</string>
<!-- Progress dialog -->
<string name="progress_title">お待ちください...</string>
<string name="progress_message">トラックをGoogleに送信しました.</string>
<string name="progress_message_reading_track">トラックを読んでいます...</string>
<string name="progress_message_preparing_track">トラックを準備しています...</string>
<string name="progress_message_authenticating_mymaps">Googleマップの認証しています...</string>
<string name="progress_message_authenticating_docs">Google Docsへの認証を行っています...</string>
<string name="progress_message_sending_docs">トラックをGoogle Docsに送信しています...</string>
<string name="progress_message_sending_mymaps">トラックをGoogleマップに送信しています...</string>
<string name="progress_message_creating_map">新しいマイマップを生成しています...</string>
<string name="import_progress_message">GPSトラックをインポートしています.若干時間がかかります.</string>
<string name="write_progress_message">SDカードにデータを書き込んでいます.若干時間がかかります.</string>
<string name="progress_message_updating_track">トラック統計情報を更新しています...</string>
<!-- Used by DeleteAllDataHandler -->
<string name="are_you_sure_question">よろしいですか?</string>
<string name="all_data_will_be_permanently_deleted">全てのトラックとマーカをこの端末から削除しました.</string>
<string name="track_will_be_permanently_deleted">トラックとマーカをこの端末から完全に削除しました.</string>
<string name="marker_will_be_permanently_deleted">マーカをこの端末から完全に削除しました.</string>
<!-- Text and links when uploading to Google My Maps -->
<string name="new_map_description">Android用My Tracksで作成</string>
<string name="my_map_link">Android用&lt;a href=\'http://mytracks.appspot.com\'&gt;My Tracks&lt;/a&gt;で作成しました.</string>
<string name="new_map_title">新規マップ</string>
<!-- Formatting of track and marker descriptions -->
<string name="points">ポイント</string>
<string name="recorded_date">記録</string>
<string name="category">活動タイプ</string>
<string name="waypoint_category">マーカタイプ</string>
<string name="part">(Part %d)</string>
<!-- Used by the track recording service -->
<string name="recording_your_track">トラックを保存中...</string>
<string name="new_track">トラック %d</string>
<!-- Common -->
<string name="no">いいえ</string>
<string name="yes">はい</string>
<string name="ok">OK</string>
<string name="error">エラー</string>
<string name="success">成功</string>
<string name="waypoint">ウェイポイント</string>
<!-- Placemark labels for start and end of track -->
<string name="start">(開始)</string>
<string name="end">(終了)</string>
<!-- TrackList -->
<string name="tracklist_show_track">表示</string>
<string name="tracklist_send_to_mymaps">Google My Mapsへ送信</string>
<string name="tracklist_send_to_new_mymap">新規Googleマイマップへ送信</string>
<string name="tracklist_send_to_docs">Google Docsへ送信</string>
<string name="tracklist_send_to_google">Googleへ送信...</string>
<string name="tracklist_delete_track">端末から削除...</string>
<string name="tracklist_edit_track">詳細編集</string>
<string name="tracklist_write_to_sd">SDへの書き込み...</string>
<string name="tracklist_save_as_gpx">GPXファイルの保存</string>
<string name="tracklist_save_as_kml">KMLファイルの保存</string>
<string name="tracklist_save_as_csv">CVSファイルの保存</string>
<string name="tracklist_btn_send_all_to_mymaps">Googleマイマップへの送信</string>
<string name="tracklist_btn_delete_all">全てのトラックの削除</string>
<!-- Export All -->
<string name="tracklist_btn_export_all">全てのトラックのエクスポート</string>
<string name="export_done">エクスポート終了</string>
<string name="tracklist_pick_one">トラックを選択します.オプションを表示するには長押しして下さい.</string>
<string name="tracklist_all_tracks">全てのトラック:</string>
<string name="tracklist_this_track">このトラック</string>
<string name="tracklist_share_track">共有...</string>
<string name="tracklist_share_link">Googleマイマップへのリンク</string>
<string name="tracklist_share_gpx_file">GPXファイル</string>
<string name="tracklist_share_kml_file">KMLファイル</string>
<string name="tracklist_share_csv_file">CSVファイル</string>
<string name="tracklist_clear_map">マップの消去</string>
<!-- Waypoints list -->
<string name="waypointslist_btn_delete_all">全てのマーカの削除</string>
<string name="waypointslist_this_waypoint">このマーカ</string>
<string name="waypointslist_delete_waypoint">削除</string>
<string name="waypointslist_edit_waypoint">詳細編集</string>
<string name="waypointslist_show_waypoint">マップに表示</string>
<string name="waypointslist_btn_insert_waypoint">ウェイポイントの挿入</string>
<string name="waypointslist_btn_insert_statistics">統計情報の挿入</string>
<string name="waypointslist_pick_one">マーカを選択します.オプションを表示するには長押しして下さい.</string>
<!-- MyTracks Details -->
<string name="trackdetails_title">トラック詳細</string>
<string name="trackdetails_name">トラック名</string>
<string name="trackdetails_description">トラック説明</string>
<string name="trackdetails_category">活動タイプ(ハイキング, サイクリング, ランニング, 登山...)</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">マーカ詳細</string>
<string name="waypointdetails_name">マーカ名</string>
<string name="waypointdetails_description">マーカ説明</string>
<string name="waypointdetails_category">マーカタイプ(建物, 洞穴, 名所...)</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">My Tracks設定</string>
<string name="settings_display">表示</string>
<string name="settings_recording">記録</string>
<string name="settings_sharing">共有</string>
<string name="settings_announcement_frequency">定期通知</string>
<string name="settings_announcement_frequency_summary">音声通知間隔の最小時間</string>
<string name="settings_announcement_not_available_summary">お使いのAndroidの版では音声読み上げ機能がありません</string>
<string name="settings_min_recording_distance">ポイント間の最短距離</string>
<string name="settings_min_recording_distance_summary">位置記録間の最短距離</string>
<string name="settings_max_recording_distance">ポイント間の最長距離</string>
<string name="settings_max_recording_distance_summary">これより間隔が長い場合はトラックは分割されます</string>
<string name="settings_min_recording_interval">ポイント間の最小時間間隔</string>
<string name="settings_min_recording_interval_summary">位置記録の最短インターバル</string>
<string name="settings_min_required_accuracy">最低精度</string>
<string name="settings_min_required_accuracy_summary">この精度以上でないと記録を休止します</string>
<string name="settings_metric_units">メートル法を使用</string>
<string name="settings_metric_units_summary_on">全ての距離をメートル法で表示します</string>
<string name="settings_metric_units_summary_off">全ての距離をヤード・ポンド法で表示します</string>
<string name="settings_map_public">マップの公開</string>
<string name="settings_map_public_summary_on">アップロードされたマップはmaps.google.comで誰でも閲覧できます</string>
<string name="settings_map_public_summary_off">アップロードされたマップはmaps.google.comのリストに公開されません.</string>
<string name="settings_report_speed_or_pace">速度表示</string>
<string name="settings_report_speed_or_pace_summary_on">速度が表示されます</string>
<string name="settings_report_speed_or_pace_summary_off">ペースが表示されます</string>
<string name="settings_share_url_only">URLのみ共有</string>
<string name="settings_share_url_only_summary_on">共有時にマップのURLのみ送信します</string>
<string name="settings_share_url_only_summary_off">共有時にトラックのコンテントが含まれます</string>
<string name="settings_signal_sampling_frequency">信号サンプリング周波数</string>
<string name="settings_signal_sampling_frequency_summary">端末の信号強度記録の時間間隔</string>
<string name="settings_split_frequency">分割頻度</string>
<string name="settings_split_frequency_summary">分割マーカを挿入する時間または距離の間隔</string>
<string name="pace_label">ペース</string>
<!-- MyTracksSky -->
<string name="mytracks_sky">GPS衛星</string>
<!-- My Maps -->
<string name="select_map_message">ご使用のアカウントからのマップ選択:</string>
<string name="create_new_map">新規マップ...</string>
<string name="map_list_title">Googleマイマップへの送信</string>
<string name="maps_list_loading">マップの読み込み中...</string>
<string name="maps_list_failed">マップを読み込めませんでした!</string>
<string name="open_map">このマップを選択</string>
<string name="edit_map_details">マップ詳細の編集</string>
<string name="share_map">マップの共有</string>
<string name="share_track">トラックの共有</string>
<string name="delete_map">アカウントからマップの削除</string>
<string name="map_title">マップタイトル:</string>
<string name="map_description">マップ説明:</string>
<string name="map_searchable_edit">このマップとコンテンツを検索結果に表示させる</string>
<string name="map_not_searchable_edit">このマップとコンテンツはどちらも検索結果に表示させない(直接URLで他の方が見ることは可能)</string>
<string name="map_searchable">このマップとコンテンツは検索結果に表示されます</string>
<string name="map_not_searchable">このマップとコンテンツはどちらも検索結果に表示されませんが,直接リンクで他の方が見ることができます.</string>
<string name="close">閉じる</string>
<string name="edit">編集</string>
<string name="cancel">キャンセル</string>
<string name="save">保存</string>
<!-- Sharing -->
<string name="share_map_subject">マップを共有したいと思います</string>
<string name="share_map_body_format">このマップに興味を持って頂けると思います:
%s</string>
<string name="send_track_subject">トラックを共有したいと思います</string>
<string name="send_track_body_format">このトラックに興味を持って頂けると思います.
このトラックを閲覧するには,Google Earthまたは他のマッピングソフトウェアが必要です.</string>
<!-- Visibility -->
<string name="public_map">公開</string>
<string name="unlisted_map">リストに出さない</string>
<string name="mapslist_no_maps_available">まだ地図がありません.</string>
<!-- Statistics View -->
<!-- Average Moving -->
<string name="average_moving_speed_label">平均移動速度</string>
<string name="average_moving_pace_label">平均移動ペース</string>
<!-- Average -->
<string name="average_speed_label">平均速度</string>
<string name="average_pace_label">平均ペース</string>
<string name="elevation_chart_name">標高チャート</string>
<string name="elevation_gain_label">標高差</string>
<string name="elevation_label">標高</string>
<string name="latitude_label">緯度</string>
<string name="longitude_label">経度</string>
<!-- Min -->
<string name="min_elevation_label">最小標高</string>
<string name="min_grade_label">最小勾配</string>
<string name="min_pace_label">最小ペース</string>
<!-- Max -->
<string name="max_elevation_label">最大標高</string>
<string name="max_grade_label">最大勾配</string>
<string name="max_speed_label">最大速度</string>
<string name="loading_track">トラックの読み込み中...</string>
<!-- Chart Settings -->
<string name="chart_settings">チャート設定</string>
<!-- X Axis -->
<string name="x_axis">X軸</string>
<string name="by_distance">距離ベース</string>
<string name="by_time">時間ベース</string>
<!-- Y Axis -->
<string name="y_axis">Y軸</string>
<!-- Units -->
<!-- Time -->
<string name="hour">時</string>
<string name="hours">時</string>
<string name="hr">時</string>
<string name="min">分</string>
<string name="minute">分</string>
<string name="minutes">分</string>
<string name="second">秒</string>
<string name="seconds">秒</string>
<!-- Distance -->
<string name="meter">m</string>
<string name="kilometer">km</string>
<string name="kilometers_long">キロメータ</string>
<string name="feet">ft</string>
<string name="mile">mi</string>
<string name="miles_long">マイル</string>
<!-- Speed and Pace -->
<string name="kilometer_per_hour">km/h</string>
<string name="kilometer_per_hour_long">キロメータ毎時</string>
<string name="min_per_kilometer">min/km</string>
<string name="per_kilometer">毎キロメータ</string>
<string name="mile_per_hour">mi/h</string>
<string name="mile_per_hour_long">マイル毎時</string>
<string name="min_per_mile">min/mi</string>
<string name="per_mile">毎マイル</string>
<!-- Stats Labels -->
<string name="moving_time_label">移動時間</string>
<string name="total_time_label">合計時間</string>
<string name="time_label">時間</string>
<string name="sd_unavail">SD が発見できません</string>
<string name="speed">速度</string>
<string name="start_trip">移動して下さい.</string>
<string name="statistics">統計</string>
<string name="total_distance_label">合計距離</string>
<string name="distance_label">距離</string>
<string name="unknown">-</string>
<string name="waiting">待機中...</string>
<!-- End User License Agreement -->
<string name="eula_title">エンドユーザライセンス契約書</string>
<string name="accept">同意する</string>
<string name="decline">拒否する</string>
<!-- About -->
<string name="about">このアプリケーションについて</string>
<string name="help">ヘルプ</string>
<string name="about_team">チーム:</string>
<string name="about_version">バージョン:</string>
<string name="about_email">サポート</string>
<string name="about_support">サポート:</string>
<!-- Send to Google -->
<string name="sendtogoogle_title">送信...</string>
<string name="sendtogoogle_google_mymaps">Googleマイマップ</string>
<string name="sendtogoogle_google_docs">Google Docs</string>
<string name="sendtogoogle_send_now">今すぐ送信</string>
<string name="sendtogoogle_create_new_map">新規マップ作成</string>
<string name="sendtogoogle_pick_existing_map">既存マップ選択</string>
<string name="sendtogoogle_send_stats">トラック統計情報送信</string>
<string name="sendtogoogle_send_stats_and_points">統計情報とポイント送信</string>
<!-- Getting the account to upload to. -->
<string name="no_account_found_title">アカウントを発見できませんでした.</string>
<string name="no_account_found">トラックをアップロードするためのアカウントを発見できませんでした.</string>
<string name="choose_account_title">アカウント選択</string>
<!-- SD card -->
<string name="io_no_external_storage_found">外部ストレージを発見できませんでした.SDカードが刺さっているか,端末がコンピュータにマウントされているか確認してください.</string>
<string name="io_write_failed">ファイルへの書き込みができません.</string>
<string name="io_create_dir_failed">SDカードにディレクトリを作成できません.</string>
<string name="io_write_finished">SDカードへの書き込み終了.</string>
<!-- VersionChecker -->
<string name="version_check_title">アップグレードチェック</string>
<string name="version_check_upgrade_button">今すぐアップデート</string>
<string name="version_check_continue_button">アップグレードせずに続行</string>
<string name="upgrade_required">お使いの版では,確実には動作せず,サポートもされません.ご使用を続けるには %1$s のアップグレードしてください.</string>
<string name="upgrade_recommended">お使いの版では,確実には動作しません.すぐにアップグレードすることをお勧めします.アップグレードされない場合は問題発生の可能性があることをご認識ください. サーバ同期するような機能の中には,アップグレードされるまで機能しないものがあります.</string>
<string name="upgrade_available">%1$s の新しい版が出ています.現版で問題なくご使用になれますが,アップグレードするまで最新の機能をお使いになれません.</string>
<!-- Welcome -->
<string name="welcome">ようこそ</string>
<string name="welcome_read_later">Ok</string>
<string name="welcome_about">My Tracksについて</string>
<!-- Announcement -->
<string name="announce_no_distance">My Tracks 移動していません</string>
<!-- Format:
$1 : Distance label
$2 : Distance value (5)
$3 : Distance units (mi, km)
$4 : Time description (10 minutes 5 seconds)
$5 : Speed value (5.6) : Pace value (5 minutes 6 seconds)
$6 : Speed units (mph) : Pace units (per mile)
-->
<string name="announce_template">My Tracks %1$s %2$.2f %3$s 時間 %4$s 速度/ペース %5$s %6$s</string>
<!-- Check Units -->
<string name="check_units_title">計測単位についてのご注意</string>
<string name="check_units_message">デフォルトの単位はメートルです.設定を変更しますか?</string>
<!-- Signal Strength -->
<string name="error_rate"> 誤り率: %1$d</string>
<string name="signal_to_noise_ratio"> S/N比: %1$d</string>
<string name="ecio"> Ec/Io比: %1$.1f dB</string>
<string name="gsm_strength">GSM 強度: %1$d</string>
<string name="cdma_strength">CDMA 強度: %1$d dBm</string>
<string name="evdo_strength">EVDO 強度: %1$d dBm</string>
</resources>
@@ -24,7 +24,7 @@ import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.AuthManager;
import com.google.android.apps.mytracks.io.AuthManagerFactory;
import com.google.android.apps.mytracks.io.GpxImport;
import com.google.android.apps.mytracks.io.GpxImporter;
import com.google.android.apps.mytracks.io.SendToDocs;
import com.google.android.apps.mytracks.io.SendToMyMaps;
import com.google.android.apps.mytracks.io.TrackWriter;
@@ -62,24 +62,25 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager.BadTokenException;
import android.widget.RelativeLayout;
import android.widget.TabHost;
import android.widget.Toast;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Random;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
/**
* The super activity that embeds our sub activities.
*
@@ -895,13 +896,14 @@ public class MyTracks extends TabActivity implements OnTouchListener,
Thread t = new Thread() {
@Override
public void run() {
boolean success = false;
ArrayList<Track> tracks = new ArrayList<Track>();
int message = R.string.success;
long[] trackIdsImported = null;
try {
try {
GpxImport.importGPXFile(fileName, tracks);
success = true;
InputStream is = new FileInputStream(fileName);
trackIdsImported = GpxImporter.importGPXFile(is, providerUtils);
} catch (SAXException e) {
Log.e(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
message = R.string.error_generic;
@@ -918,15 +920,11 @@ public class MyTracks extends TabActivity implements OnTouchListener,
Log.e(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
message = R.string.error_out_of_memory;
}
if (success) {
long trackId = -1;
for (Track track : tracks) {
Uri uri = providerUtils.insertTrackAndTrackPoints(track);
trackId = Long.parseLong(uri.getLastPathSegment());
}
setSelectedTrack(trackId);
if (trackIdsImported != null && trackIdsImported.length > 0) {
// select last track from import file
setSelectedTrack(trackIdsImported[trackIdsImported.length - 1]);
} else {
MyTracks.this.showMessageDialog(message, false/*success*/);
MyTracks.this.showMessageDialog(message, false/* success */);
}
} finally {
runOnUiThread(new Runnable() {
@@ -167,24 +167,25 @@ public class MyTracksProvider extends ContentProvider {
@Override
public int delete(Uri url, String where, String[] selectionArgs) {
if (urlMatcher.match(url) == TRACKPOINTS) {
Log.w(MyTracksProvider.TAG, "provider trackpoints delete!");
int count = db.delete(TRACKPOINTS_TABLE, where, selectionArgs);
getContext().getContentResolver().notifyChange(url, null, true);
return count;
} else if (urlMatcher.match(url) == TRACKS) {
Log.w(MyTracksProvider.TAG, "provider track delete!");
int count = db.delete(TRACKS_TABLE, where, selectionArgs);
getContext().getContentResolver().notifyChange(url, null, true);
return count;
} else if (urlMatcher.match(url) == WAYPOINTS) {
Log.w(MyTracksProvider.TAG, "provider waypoint delete!");
int count = db.delete(WAYPOINTS_TABLE, where, selectionArgs);
getContext().getContentResolver().notifyChange(url, null, true);
return count;
} else {
throw new IllegalArgumentException("Unknown URL " + url);
String table;
switch (urlMatcher.match(url)) {
case TRACKPOINTS:
table = TRACKPOINTS_TABLE;
break;
case TRACKS:
table = TRACKS_TABLE;
break;
case WAYPOINTS:
table = WAYPOINTS_TABLE;
break;
default:
throw new IllegalArgumentException("Unknown URL " + url);
}
Log.w(MyTracksProvider.TAG, "provider delete in " + table + "!");
int count = db.delete(table, where, selectionArgs);
getContext().getContentResolver().notifyChange(url, null, true);
return count;
}
@Override
@@ -216,19 +217,49 @@ public class MyTracksProvider extends ContentProvider {
} else {
values = new ContentValues();
}
if (urlMatcher.match(url) == TRACKPOINTS) {
return insertTrackPoint(url, values);
} else if (urlMatcher.match(url) == TRACKS) {
return insertTrack(url, values);
} else if (urlMatcher.match(url) == WAYPOINTS) {
return insertWaypoint(url, values);
} else {
throw new IllegalArgumentException("Unknown URL " + url);
int urlMatchType = urlMatcher.match(url);
return insertType(url, urlMatchType, values);
}
private Uri insertType(Uri url, int urlMatchType, ContentValues values) {
switch (urlMatchType) {
case TRACKPOINTS:
return insertTrackPoint(url, values);
case TRACKS:
return insertTrack(url, values);
case WAYPOINTS:
return insertWaypoint(url, values);
default:
throw new IllegalArgumentException("Unknown URL " + url);
}
}
@Override
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
int numInserted = 0;
try {
// Use a transaction in order to make the insertions run as a single batch
db.beginTransaction();
int urlMatch = urlMatcher.match(url);
for (numInserted = 0; numInserted < valuesBulk.length; numInserted++) {
ContentValues values = valuesBulk[numInserted];
if (values == null) { values = new ContentValues(); }
insertType(url, urlMatch, values);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return numInserted;
}
private Uri insertTrackPoint(Uri url, ContentValues values) {
Log.d(MyTracksProvider.TAG, "MyTracksProvider.insertTrackPoint");
boolean hasLat = values.containsKey(TrackPointsColumns.LATITUDE);
boolean hasLong = values.containsKey(TrackPointsColumns.LONGITUDE);
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
@@ -232,6 +232,17 @@ public interface MyTracksProviderUtils {
*/
Uri insertTrackPoint(Location location, long trackId);
/**
* Inserts multiple track points in a single operation.
*
* @param locations an array of locations to insert
* @param length the number of locations (from the beginning of the array)
* to actually insert, or -1 for all of them
* @param trackId the ID of the track to insert the points into
* @return the number of points inserted
*/
int bulkInsertTrackPoints(Location[] locations, int length, long trackId);
/**
* Inserts a waypoint in the provider.
*
@@ -240,14 +251,6 @@ public interface MyTracksProviderUtils {
*/
Uri insertWaypoint(Waypoint waypoint);
/**
* Inserts a track including its track points in the provider.
*
* @param track the track to insert
* @return the content provider URI for the inserted track
*/
Uri insertTrackAndTrackPoints(Track track);
/**
* Tests if a track with given id exists.
*
@@ -270,7 +273,7 @@ public interface MyTracksProviderUtils {
* @param cursor a cursor pointing at a db or provider with locations
* @return a new location object
*/
public Location createLocation(Cursor cursor);
Location createLocation(Cursor cursor);
/**
* Creates a waypoint object from a given cursor.
@@ -278,7 +281,7 @@ public interface MyTracksProviderUtils {
* @param cursor a cursor pointing at a db or provider with waypoints.
* @return a new waypoint object
*/
public Waypoint createWaypoint(Cursor cursor);
Waypoint createWaypoint(Cursor cursor);
/**
* A factory which can produce instances of {@link MyTracksProviderUtils},
@@ -840,8 +840,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
Cursor cursor = getLocationsCursor(track.getId(),
startingPoint,
buffer.getSize(), false);
final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
if (cursor == null) {
Log.w(MyTracksProvider.TAG, "Cannot get a locations cursor!");
buffer.setInvalid();
@@ -860,6 +858,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
return;
}
final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
do {
Location location = createLocation(cursor);
if (location == null) {
@@ -900,6 +900,18 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
createContentValues(location, trackId));
}
@Override
public int bulkInsertTrackPoints(Location[] locations, int length, long trackId) {
if (length == -1) { length = locations.length; }
ContentValues[] values = new ContentValues[length];
for (int i = 0; i < length; i++) {
values[i] = createContentValues(locations[i], trackId);
}
return context.getContentResolver().bulkInsert(TrackPointsColumns.CONTENT_URI, values);
}
@Override
public Uri insertWaypoint(Waypoint waypoint) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
@@ -908,31 +920,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
createContentValues(waypoint));
}
@Override
public Uri insertTrackAndTrackPoints(Track track) {
// Insert the track right away to have its ID
Uri trackUri = insertTrack(track);
long trackId = Long.parseLong(trackUri.getLastPathSegment());
track.setId(trackId);
// Insert all the points (associated with the track ID)
boolean firstPoint = true;
long pointId = -1;
for (Location location : track.getLocations()) {
Uri pointUri = insertTrackPoint(location, trackId);
pointId = Long.parseLong(pointUri.getLastPathSegment());
if (firstPoint) {
track.setStartId(pointId);
firstPoint = false;
}
}
track.setStopId(pointId);
// Update the track with the start and end point IDs
updateTrack(track);
return trackUri;
}
@Override
public boolean trackExists(long id) {
Cursor cursor = null;
@@ -1,237 +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.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
import com.google.android.apps.mytracks.util.MyTracksUtils;
import android.location.Location;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
/**
* Helper class to parse a GPX file or string and convert it into a track
* object.
*
* TODO: See if we can use a SAX style parser as the DOM style
* parsing uses too much memory and will not allow import of very large GPX
* files (limit currently set to 500KB).
*
* @author Leif Hendrik Wilden
*/
public class GpxImport {
private static final SimpleDateFormat DATE_FORMAT1 =
new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZ");
private static final SimpleDateFormat DATE_FORMAT2 =
new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
private static final SimpleDateFormat DATE_FORMAT3 =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
/**
* Reads GPS tracks from a GPX file and append tracks and their coordinates to
* the given list of tracks.
*
* @param tracks a list of tracks
* @param filename a file name
* @throws SAXException a parsing error
* @throws ParserConfigurationException internal error
* @throws IOException a file reading problem
*/
public static void importGPXFile(
final String filename, final ArrayList<Track> tracks)
throws SAXException, ParserConfigurationException, IOException,
OutOfMemoryError {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
File file = new File(filename);
if (file.length() > 500 * 1024) {
// Better to throw an error now then, to let the VM do it. If the VM does
// it, it will throw another exception while throwing the OutOfMemoryError
// and that will kill out app with a nasty error message. In that way we
// can at least display a reasonable message to the user and let her know
// what the problem is.
throw new OutOfMemoryError();
}
Document doc = builder.parse(file);
importGPXDocument(tracks, doc);
}
/**
* This is a utility class with only static members.
*/
private GpxImport() {
}
/**
* Reads GPS tracks from a GPX document and append then to the given list of
* tracks.
*
* TODO: Break this method down into smaller ones
*
* @param tracks an array list of tracks
* @param doc a document
*/
public static void importGPXDocument(
final ArrayList<Track> tracks, final Document doc) {
NodeList trackNodes = doc.getElementsByTagName("trk");
for (int n = 0; n < trackNodes.getLength(); n++) {
Track track = new Track();
tracks.add(track);
ArrayList<Location> locations = track.getLocations();
Location lastLocation = null;
Node trkNode = trackNodes.item(n);
NodeList segmentNodes = trkNode.getChildNodes();
ArrayList<Node> nodes = new ArrayList<Node>();
int nSegments = 0;
for (int k = 0; k < segmentNodes.getLength(); k++) {
Node segmentNode = segmentNodes.item(k);
if (segmentNode.getNodeName().equals("name")) {
track.setName(segmentNode.getFirstChild().getNodeValue());
} else if (segmentNode.getNodeName().equals("description")) {
track.setDescription(segmentNode.getFirstChild().getNodeValue());
} else if (segmentNode.getNodeName().equals("trkseg")) {
if (nSegments > 0) {
// Add a segment separator:
Location location = new Location("gps");
location.setLatitude(100.0);
location.setLongitude(100.0);
location.setAltitude(0);
if (locations.size() > 0) {
long pointTime = locations.get(locations.size() - 1).getTime();
location.setTime(pointTime);
}
track.addLocation(location);
lastLocation = null;
}
nSegments++;
NodeList segmentChildren = segmentNode.getChildNodes();
nodes.clear();
for (int j = 0; j < segmentChildren.getLength(); j++) {
Node child = segmentChildren.item(j);
if (child.getNodeName().equals("trkpt")) {
nodes.add(child);
}
}
for (int i = 0; i < nodes.size(); i++) {
NamedNodeMap namedNodes = nodes.get(i).getAttributes();
double lat = Double.parseDouble(namedNodes.getNamedItem("lat")
.getNodeValue());
double lon = Double.parseDouble(namedNodes.getNamedItem("lon")
.getNodeValue());
NodeList children = nodes.get(i).getChildNodes();
Node elementNode = null;
Node timeNode = null;
for (int j = 0; j < children.getLength(); j++) {
Node child = children.item(j);
if (child.getNodeName().equals("ele")) {
elementNode = child;
} else if (child.getNodeName().equals("time")) {
timeNode = child;
}
}
String altitudeStr = null;
if (elementNode != null) {
altitudeStr = elementNode.getFirstChild().getNodeValue();
} else {
altitudeStr = "0";
}
String timeContents = null;
if (timeNode != null) {
timeContents = timeNode.getFirstChild().getNodeValue();
} else {
timeContents = "";
}
double altitude = Double.parseDouble(altitudeStr);
long t = -1;
try {
// 1st try with time zone at end a la "+0000"
t = DATE_FORMAT1.parse(timeContents).getTime();
} catch (ParseException e) {
// if that fails, try with a literal "Z" at the end (this is not
// according to xml standard, but some gpx files are like that):
try {
t = DATE_FORMAT2.parse(timeContents).getTime();
} catch (ParseException ex) {
// some gpx timestamps have 3 additional digits at the end.
try {
t = DATE_FORMAT3.parse(timeContents).getTime();
} catch (ParseException exc) {
t = 0;
}
}
}
Location location = new Location("gps");
location.setLatitude(lat);
location.setLongitude(lon);
location.setAltitude(altitude);
location.setTime(t);
// We don't have a speed and bearing in GPX, make something up from
// the last two points:
if (lastLocation != null) {
final long dt = location.getTime() - lastLocation.getTime();
if (dt > 0) {
final float speed =
location.distanceTo(lastLocation) / (dt / 1000);
location.setSpeed(speed);
}
location.setBearing(lastLocation.bearingTo(location));
}
lastLocation = location;
if (MyTracksUtils.isValidLocation(location)) {
track.addLocation(location);
}
}
if (locations.size() > 0) {
long startTime = locations.get(0).getTime();
// Calculate statistics for the imported track
TripStatisticsBuilder statsBuilder = new TripStatisticsBuilder();
statsBuilder.resumeAt(startTime);
for (Location location : locations) {
if (MyTracksUtils.isValidLocation(location)) {
/* Any time works here. The totalTime will be set by "pauseAt" later: */
statsBuilder.addLocation(location, location.getTime());
}
}
long lastPointTime = locations.get(locations.size() - 1).getTime();
statsBuilder.pauseAt(lastPointTime);
track.setStatistics(statsBuilder.getStatistics());
}
}
}
}
}
}
@@ -0,0 +1,574 @@
/*
* 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.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
import com.google.android.apps.mytracks.util.MyTracksUtils;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.SimpleTimeZone;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Imports GPX XML files to the my tracks provider.
*
* TODO: Show progress indication to the user.
*
* @author Leif Hendrik Wilden
* @author Steffen Horlacher
* @author Rodrigo Damazio
*/
public class GpxImporter extends DefaultHandler {
/*
* Different date formats used in GPX files
*/
static final SimpleDateFormat DATE_FORMAT1 = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZ");
static final SimpleDateFormat DATE_FORMAT2 = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'Z'");
static final SimpleDateFormat DATE_FORMAT3 = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSSZ");
static final SimpleTimeZone UTC_TIMEZONE = new SimpleTimeZone(0, "UTC");
/*
* GPX-XML tag names and attributes.
*/
private static final String TAG_TRACK = "trk";
private static final String TAG_TRACK_POINT = "trkpt";
private static final Object TAG_TRACK_SEGMENT = "trkseg";
private static final String TAG_NAME = "name";
private static final String TAG_DESCRIPTION = "desc";
private static final String TAG_ALTITUDE = "ele";
private static final String TAG_TIME = "time";
private static final String ATT_LAT = "lat";
private static final String ATT_LON = "lon";
/**
* The maximum number of locations to buffer for bulk-insertion into the database.
*/
private static final int MAX_BUFFERED_LOCATIONS = 512;
/**
* Utilities for accessing the contnet provider.
*/
private final MyTracksProviderUtils providerUtils;
/**
* List of track ids written in the database. Only contains successfully
* written tracks.
*/
private final List<Long> tracksWritten;
/**
* Contains the current elements content.
*/
private String content;
/**
* Currently reading location.
*/
private Location location;
/**
* Previous location, required for calculations.
*/
private Location lastLocation;
/**
* Currently reading track.
*/
private Track track;
/**
* Statistics builder for the current track.
*/
private TripStatisticsBuilder statsBuilder;
/**
* Buffer of locations to be bulk-inserted into the database.
*/
private Location[] bufferedPointInserts = new Location[MAX_BUFFERED_LOCATIONS];
/**
* Number of locations buffered to be inserted into the database.
*/
private int numBufferedPointInserts = 0;
/**
* Number of locations already processed.
*/
private int numberOfLocations;
/**
* Number of segments already processed.
*/
private int numberOfSegments;
/**
* Used to identify if a track was written to the database but not yet
* finished successfully.
*/
private boolean isCurrentTrackRollbackable;
/**
* Flag to indicate if we're inside a track's xml element.
* Some sub elements like name may be used in other parts of the gpx file,
* and we use this to ignore them.
*/
private boolean isInTrackElement;
/**
* Counter to find out which child level of track we are processing.
*/
private int trackChildDepth;
/**
* SAX-Locator to get current line information.
*/
private Locator locator;
private Location lastSegmentLocation;
/**
* Reads GPS tracks from a GPX file and writes tracks and their coordinates to
* the database.
*
* @param tracks a list of tracks
* @param is a input steam with gpx-xml data
* @return long[] array of track ids written in the database
* @throws SAXException a parsing error
* @throws ParserConfigurationException internal error
* @throws IOException a file reading problem
*/
public static long[] importGPXFile(final InputStream is,
final MyTracksProviderUtils providerUtils)
throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
GpxImporter handler = new GpxImporter(providerUtils);
SAXParser parser = factory.newSAXParser();
long[] trackIds = null;
try {
long start = System.currentTimeMillis();
parser.parse(is, handler);
long end = System.currentTimeMillis();
Log.d(MyTracksConstants.TAG, "Total import time: " + (end - start) + "ms");
trackIds = handler.getImportedTrackIds();
} finally {
// delete track if not finished
handler.rollbackUnfinishedTracks();
}
return trackIds;
}
/**
* Constructor, requires providerUtils for writing tracks the database.
*/
public GpxImporter(MyTracksProviderUtils providerUtils) {
this.providerUtils = providerUtils;
tracksWritten = new ArrayList<Long>();
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String newContent = new String(ch, start, length);
if (content == null) {
content = newContent;
} else {
// In 99% of the cases, a single call to this method will be made for each
// sequence of characters we're interested in, so we'll rarely be
// concatenating strings, thus not justifying the use of a StringBuilder.
content += newContent;
}
}
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
if (isInTrackElement) {
trackChildDepth++;
if (localName.equals(TAG_TRACK_POINT)) {
onTrackPointElementStart(attributes);
} else if (localName.equals(TAG_TRACK_SEGMENT)) {
onTrackSegmentElementStart();
} else if (localName.equals(TAG_TRACK)) {
String msg = createErrorMessage("Invalid GPX-XML detected");
throw new SAXException(msg);
}
} else if (localName.equals(TAG_TRACK)) {
isInTrackElement = true;
trackChildDepth = 0;
onTrackElementStart();
}
}
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
if (!isInTrackElement) { return; }
// process these elements only as sub-elements of track
if (localName.equals(TAG_TRACK_POINT)) {
onTrackPointElementEnd();
} else if (localName.equals(TAG_ALTITUDE)) {
onAltitudeElementEnd();
} else if (localName.equals(TAG_TIME)) {
onTimeElementEnd();
} else if (localName.equals(TAG_NAME)) {
// we are only interested in the first level name element
if (trackChildDepth == 1) {
onNameElementEnd();
}
} else if (localName.equals(TAG_DESCRIPTION)) {
// we are only interested in the first level description element
if (trackChildDepth == 1) {
onDescriptionElementEnd();
}
} else if (localName.equals(TAG_TRACK_SEGMENT)) {
onTrackSegmentElementEnd();
} else if (localName.equals(TAG_TRACK)) {
onTrackElementEnd();
isInTrackElement = false;
trackChildDepth = 0;
}
trackChildDepth--;
// reset element content
content = null;
}
@Override
public void setDocumentLocator(Locator locator) {
this.locator = locator;
}
/**
* Create a new Track object and insert empty track in database. Track will be
* updated with missing values later.
*/
private void onTrackElementStart() {
track = new Track();
numberOfLocations = 0;
Uri trackUri = providerUtils.insertTrack(track);
long trackId = Long.parseLong(trackUri.getLastPathSegment());
track.setId(trackId);
isCurrentTrackRollbackable = true;
}
private void onDescriptionElementEnd() {
track.setDescription(content.toString().trim());
}
private void onNameElementEnd() {
track.setName(content.toString().trim());
}
/**
* Track segment started.
*/
private void onTrackSegmentElementStart() {
if (numberOfSegments > 0) {
// Add a segment separator:
location = new Location(LocationManager.GPS_PROVIDER);
location.setLatitude(100.0);
location.setLongitude(100.0);
location.setAltitude(0);
if (lastLocation != null) {
location.setTime(lastLocation.getTime());
}
insertTrackPoint(location);
lastLocation = location;
lastSegmentLocation = null;
location = null;
}
numberOfSegments++;
}
/**
* Reads trackpoint attributes and assigns them to the current location.
*
* @param attributes xml attributes
*/
private void onTrackPointElementStart(Attributes attributes) throws SAXException {
if (location != null) {
String errorMsg = createErrorMessage("Found a track point inside another one.");
throw new SAXException(errorMsg);
}
location = createLocationFromAttributes(attributes);
}
/**
* Creates and returns a location with the position parsed from the given
* attributes.
*
* @param attributes the attributes to parse
* @return the created location
* @throws SAXException if the attributes cannot be parsed
*/
private Location createLocationFromAttributes(Attributes attributes) throws SAXException {
String latitude = attributes.getValue(ATT_LAT);
String longitude = attributes.getValue(ATT_LON);
if (latitude == null || longitude == null) {
throw new SAXException(createErrorMessage("Point with no longitude or latitude"));
}
// create new location and set attributes
Location loc = new Location(LocationManager.GPS_PROVIDER);
loc.setLatitude(Double.parseDouble(latitude));
loc.setLongitude(Double.parseDouble(longitude));
return loc;
}
/**
* Track point finished, write in database.
*
* @throws SAXException - thrown if track point is invalid
*/
private void onTrackPointElementEnd() throws SAXException {
if (MyTracksUtils.isValidLocation(location)) {
statsBuilder.addLocation(location, location.getTime());
// insert in db
insertTrackPoint(location);
// first track point?
if (lastLocation == null && numberOfSegments == 1) {
track.setStartId(getLastPointId());
}
lastLocation = location;
lastSegmentLocation = location;
location = null;
} else {
// invalid location - abort import
String msg = createErrorMessage("Invalid location detected: " + location);
throw new SAXException(msg);
}
}
private void insertTrackPoint(Location loc) {
bufferedPointInserts[numBufferedPointInserts] = loc;
numBufferedPointInserts++;
numberOfLocations++;
if (numBufferedPointInserts >= MAX_BUFFERED_LOCATIONS) {
flushPointInserts();
}
}
private void flushPointInserts() {
if (numBufferedPointInserts <= 0) { return; }
providerUtils.bulkInsertTrackPoints(bufferedPointInserts, numBufferedPointInserts, track.getId());
numBufferedPointInserts = 0;
}
/**
* Track segment finished.
*/
private void onTrackSegmentElementEnd() {
// Nothing to be done
}
/**
* Track finished - update in database.
*/
private void onTrackElementEnd() {
if (lastLocation != null) {
flushPointInserts();
// Calculate statistics for the imported track and update
statsBuilder.pauseAt(lastLocation.getTime());
track.setStopId(getLastPointId());
track.setNumberOfPoints(numberOfLocations);
track.setStatistics(statsBuilder.getStatistics());
providerUtils.updateTrack(track);
tracksWritten.add(track.getId());
isCurrentTrackRollbackable = false;
lastSegmentLocation = null;
lastLocation = null;
statsBuilder = null;
} else {
// track contains no track points makes no real
// sense to import it as we have no location
// information -> roll back
rollbackUnfinishedTracks();
}
}
/**
* Setting time and doing additional calculations as this is the last value
* required. Also sets the start time for track and statistics as there is no
* start time in the track root element.
*
* @throws SAXException on parsing errors
*/
private void onTimeElementEnd() throws SAXException {
if (location == null) { return; }
long time = parseTimeForAllFormats(content);
if (lastSegmentLocation != null) {
long timeDifference = time - lastSegmentLocation.getTime();
// check for negative time change
if (timeDifference < 0) {
String msg = createErrorMessage("Found negative time change.");
throw new SAXException(msg);
}
// We don't have a speed and bearing in GPX, make something up from
// the last two points.
// TODO GPS points tend to have some inherent imprecision,
// speed and bearing will likely be off, so the statistics for things like
// max speed will also be off.
float speed = location.distanceTo(lastLocation) * 1000.0f / timeDifference;
location.setSpeed(speed);
location.setBearing(lastSegmentLocation.bearingTo(location));
}
location.setTime(time);
// initialize start time with time of first track point
if (statsBuilder == null) {
statsBuilder = new TripStatisticsBuilder();
statsBuilder.resumeAt(time);
}
}
private void onAltitudeElementEnd() {
if (location != null) {
location.setAltitude(Double.parseDouble(content));
}
}
/**
* Deletes the last track if it was not completely imported.
*/
public void rollbackUnfinishedTracks() {
if (isCurrentTrackRollbackable) {
providerUtils.deleteTrack(track.getId());
isCurrentTrackRollbackable = false;
}
}
/**
* Get all track ids of the tracks created by this importer run.
*
* @return array of track ids
*/
private long[] getImportedTrackIds() {
// Convert from java.lang.Long for convenience
long[] result = new long[tracksWritten.size()];
for (int i = 0; i < result.length; i++) {
result[i] = tracksWritten.get(i);
}
return result;
}
/**
* Parse time trying different formats used in GPX files.
*
* @param timeContents string with time information
* @return time as long
* @throws SAXException on time parsing errors
*/
private long parseTimeForAllFormats(String timeContents) throws SAXException {
long time;
timeContents = timeContents.trim();
// 1st try with time zone at end a la "+0000"
time = parseTime(timeContents, DATE_FORMAT1);
if (time > -1) { return time; }
// if that fails, try with a literal "Z" at the end
// (this is not according to xml standard, but some gpx files are like
// that):
time = parseTime(timeContents, DATE_FORMAT2);
if (time > -1) { return time; }
// some gpx timestamps have 3 additional digits at the end.
time = parseTime(timeContents, DATE_FORMAT3);
if (time > -1) { return time; }
// everything failed - abort the import
String msg = createErrorMessage("Invalid time format: " + timeContents);
throw new SAXException(msg);
}
private long parseTime(String timeContents, SimpleDateFormat formatParam) {
SimpleDateFormat format = formatParam;
try {
format.setTimeZone(UTC_TIMEZONE);
return format.parse(timeContents).getTime();
} catch (ParseException ex) {
return -1;
}
}
/**
* Returns the ID of the last point inserted into the database.
*/
private long getLastPointId() {
flushPointInserts();
return providerUtils.getLastLocationId(track.getId());
}
/**
* Builds a parsing error message with current line information.
*
* @param details details about the error, will be appended
* @return error message string with current line information
*/
private String createErrorMessage(String details) {
StringBuffer msg = new StringBuffer();
msg.append("Parsing error at line: ");
msg.append(locator.getLineNumber());
msg.append(" column: ");
msg.append(locator.getColumnNumber());
msg.append(". ");
msg.append(details);
return msg.toString();
}
}