Compare commits

..

5 Commits

Author SHA1 Message Date
Rodrigo Damazio 4577ff9628 Releasing 1.0.21 2010-11-26 14:57:03 -08:00
Rodrigo Damazio e6cd9f524d Replacing tabs with spaces 2010-11-24 19:21:29 -08:00
Rodrigo Damazio 3dad56621f Transplanting Sandor's bugfix change for dialogs in old devices. 2010-11-24 18:57:40 -08:00
Rodrigo Damazio 6c9a225b64 Merging Bart's changes into the bugfix branch. 2010-11-24 18:27:40 -08:00
Rodrigo Damazio 3bbb9bd95d Making a 1.0.20 bugfix branch 2010-11-24 18:26:30 -08:00
168 changed files with 15258 additions and 4872 deletions
+2
View File
@@ -2,3 +2,5 @@ syntax:glob
*/bin
*/gen/**
.metadata
MyTracksTest/annotations.log
+2 -2
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="lib" path="libs/googleloginclient-helper.jar"/>
<classpathentry kind="lib" path="libs/google-common.jar"/>
<classpathentry kind="lib" path="libs/accounts.jar"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -0,0 +1,12 @@
#Wed Sep 29 18:44:16 BRT 2010
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
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
+43 -17
View File
@@ -1,16 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.maps.mytracks"
android:versionCode="16"
android:versionName="1.0.15">
android:installLocation="auto"
android:versionCode="22"
android:versionName="1.0.21">
<application android:icon="@drawable/arrow_icon"
android:label="@string/app_name">
android:label="@string/app_name"
android:backupAgent="com.google.android.apps.mytracks.io.backup.MyTracksBackupAgent">
<uses-library android:name="com.google.android.maps" />
<provider android:name="com.google.android.apps.mytracks.content.MyTracksProvider"
android:multiprocess="true"
android:authorities="com.google.android.maps.mytracks" />
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIi-_QiwoRSc9_bAC9cmuNXTQyU8ajJmGtKdhskQ" />
<provider android:name="com.google.android.apps.mytracks.content.MyTracksProvider"
android:authorities="com.google.android.maps.mytracks" />
<activity android:name="com.google.android.apps.mytracks.MyTracks"
android:label="My Tracks"
@@ -36,16 +39,13 @@
<activity android:name="com.google.android.apps.mymaps.MyMapsList"
android:label="@string/map_list_title"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|fontScale">
</activity>
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|fontScale" />
<activity android:name="com.google.android.apps.mytracks.MyTracksList"
android:label="Recorded Tracks">
</activity>
android:label="Recorded Tracks" />
<activity android:name="com.google.android.apps.mytracks.MyTracksWaypointsList"
android:label="Recorded Waypoints">
</activity>
android:label="Recorded Waypoints" />
<activity android:name="com.google.android.apps.mytracks.MyTracksDetails"
android:label="@string/trackdetails_title" />
@@ -56,23 +56,34 @@
<activity android:name="com.google.android.apps.mytracks.MyTracksSettings"
android:label="@string/mytracks_settings" />
<activity android:name="com.google.android.apps.mytracks.AggregatedStatsActivity"
android:label="@string/aggregated_stats" />
<activity android:name="com.google.android.apps.mytracks.StatsActivity"
android:label="@string/statistics"/>
android:label="@string/statistics" />
<activity android:name="com.google.android.apps.mytracks.ChartActivity"
android:label="@string/elevation_label"/>
<activity android:name="com.google.android.apps.mytracks.WelcomeActivity"
android:label="@string/welcome"/>
android:label="@string/welcome"/>
<service android:name="com.google.android.apps.mytracks.services.TrackRecordingService"
android:process=":trackrecord"
android:label="Track Recording Service"
android:icon="@drawable/arrow_320"
android:enabled="true" />
android:enabled="true"
android:exported="true" />
<receiver android:name="com.google.android.apps.mytracks.MyTracksReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
<!-- Permissions to initialize service -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Permissions for recording tracks and showing maps -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -89,6 +100,22 @@
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.writely" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<!-- Permissions for misc services -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- Features required -->
<uses-feature android:name="android.hardware.location" android:required="true"/>
<uses-feature android:name="android.hardware.location.gps" android:required="true"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="true"/>
<!-- Features optional -->
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.sensor.compass" android:required="false"/>
<uses-feature android:name="android.hardware.telephony" android:required="false"/>
<uses-feature android:name="android.hardware.telephony.gsm" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false"/>
<uses-sdk android:minSdkVersion="3"/>
<supports-screens
android:largeScreens="true"
@@ -96,4 +123,3 @@
android:smallScreens="true"
android:anyDensity="true" />
</manifest>
+2
View File
@@ -9,9 +9,11 @@
<booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/MyTracks"/>
<listEntry value="/MyTracks/AndroidManifest.xml"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
<listEntry value="1"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+1 -1
View File
@@ -10,4 +10,4 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=Google Inc.:Google APIs:7
target=Google Inc.:Google APIs:8
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -17,11 +17,11 @@ limitations under the License.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/btn_arrow_left_default" />
android:drawable="@drawable/left_arrow_chart" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_arrow_left_press" />
android:drawable="@drawable/left_arrow_chart_press" />
<item android:drawable="@drawable/btn_arrow_left_default" />
<item android:drawable="@drawable/left_arrow_chart" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -17,11 +17,11 @@ limitations under the License.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/btn_arrow_right_default" />
android:drawable="@drawable/left_arrow_stats" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_arrow_right_press" />
android:drawable="@drawable/left_arrow_stats_press" />
<item android:drawable="@drawable/btn_arrow_right_default" />
<item android:drawable="@drawable/left_arrow_stats" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/left_arrow_track" />
<item android:state_pressed="true"
android:drawable="@drawable/left_arrow_track_press" />
<item android:drawable="@drawable/left_arrow_track" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/right_arrow_chart" />
<item android:state_pressed="true"
android:drawable="@drawable/right_arrow_chart_press" />
<item android:drawable="@drawable/right_arrow_chart" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/right_arrow_stats" />
<item android:state_pressed="true"
android:drawable="@drawable/right_arrow_stats_press" />
<item android:drawable="@drawable/right_arrow_stats" />
</selector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false"
android:drawable="@drawable/right_arrow_track" />
<item android:state_pressed="true"
android:drawable="@drawable/right_arrow_track_press" />
<item android:drawable="@drawable/right_arrow_track" />
</selector>
+2 -1
View File
@@ -95,8 +95,9 @@ limitations under the License.
android:textSize="20sp" />
<TextView
android:layout_height="wrap_content"
android:id="@+id/about_version_register"
android:layout_width="wrap_content"
android:text="@string/version"
android:text="@string/unknown"
android:textSize="20sp" />
</TableRow>
+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_start_recording"
android:icon="@drawable/menu_record"
android:title="@string/start_recording"/>
<item android:id="@+id/menu_stop_recording"
android:icon="@drawable/menu_stop"
android:title="@string/stop_recording"/>
<item android:id="@+id/menu_list_tracks"
android:icon="@drawable/menu_list_tracks"
android:title="@string/list_tracks"/>
<item android:id="@+id/menu_list_markers"
android:icon="@drawable/menu_marker"
android:title="@string/list_markers"/>
<item android:id="@+id/menu_settings"
android:icon="@android:drawable/ic_menu_preferences"
android:orderInCategory="1001"
android:title="@string/settings"/>
<item android:id="@+id/menu_aggregated_stats"
android:icon="@android:drawable/ic_menu_info_details"
android:orderInCategory="1002"
android:title="@string/aggregated_stats"/>
<item android:id="@+id/menu_help"
android:icon="@android:drawable/ic_menu_info_details"
android:orderInCategory="1003"
android:title="@string/help"/>
</menu>
-1
View File
@@ -155,7 +155,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Dauer zwischen Ansagen:</string>
<string name="settings_announcement_frequency_summary">Kürzeste Zeit zwichen zwei Ansagen</string>
<string name="settings_announcement_not_available_summary">Gesprochene Ansagen sind mit Ihrer Version von Android nicht verfügbar.</string>
<string name="settings_min_recording_distance">Kleinste Entfernung zwischen Punkten:</string>
<string name="settings_min_recording_distance_summary">Kleinste Entfernung zwischen zwei Positionsaufzeichnungen</string>
-1
View File
@@ -158,7 +158,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Tiempo entre anuncios</string>
<string name="settings_announcement_frequency_summary">Tiempo mínimo entre dos anuncios de voz</string>
<string name="settings_announcement_not_available_summary">Conversión de anuncios de texto a voz no esta disponible en su versión de Android</string>
<string name="settings_min_recording_distance">Distancia mínima entre puntos consecutivos</string>
<string name="settings_min_recording_distance_summary">Distancia mínima entre dos grabaciones de posición</string>
+1 -1
View File
@@ -26,6 +26,7 @@ limitations under the License.
<string name="list_markers">Marqueurs</string>
<string name="delete_all_tracks">Supprimer tous les parcours</string>
<string name="settings">Paramètres</string>
<string name="elevation">Altitude</string>
<string name="choose_track_message">Sélectionnez le parcours à visualiser</string>
<string name="current_track">Parcours actuel</string>
<string name="current_segment">Segment actuel</string>
@@ -157,7 +158,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Fréquence des annonces</string>
<string name="settings_announcement_frequency_summary">Temps minimum entre eux annonces vocales</string>
<string name="settings_announcement_not_available_summary">La synthèse vocale n\'est pas disponible pour cette version d\'Android</string>
<string name="settings_min_recording_distance">Distance min. entre 2 points</string>
<string name="settings_min_recording_distance_summary">Plus petite distance entre deux enregistrements de votre position</string>
-1
View File
@@ -164,7 +164,6 @@ limitations under the License.
<string name="settings_announcement_frequency">Intervallo fra due annunci:</string>
<string name="settings_announcement_frequency_summary">Tempo minimo fra due annunci audio</string>
<string name="settings_announcement_not_available_summary">Gli annunci audio non sono disponibili in questa varsione di Android</string>
<string name="settings_min_recording_distance">Distanza minima fra due punti successivi:</string>
<string name="settings_min_recording_distance_summary">Distanza minima fra due successive registrazioni della posizione</string>
+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_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>
+287
View File
@@ -0,0 +1,287 @@
<?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>Brak</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</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 (zalecane)</item>
<item>10 m</item>
<item>100 m</item>
</string-array>
<string-array name="min_recording_distance_options_ft">
<item>3 stopy</item>
<item>6 stóp</item>
<item>10 stóp</item>
<item>13 stóp</item>
<item>16 stóp (zalecane)</item>
<item>33 stopy</item>
<item>328 stóp</item>
</string-array>
<!-- Maximum recording distance -->
<string-array name="max_recording_distance_options">
<item>50 m</item>
<item>100 m</item>
<item>200 m (zalecane)</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 stopy</item>
<item>328 stóp</item>
<item>656 stóp (polecane)</item>
<item>1640 stóp</item>
<item>0.6 mili</item>
<item>3.1 mili</item>
</string-array>
<!-- Minimum recording interval -->
<string-array name="min_recording_interval_options">
<item>oszczędzanie baterii (adaptacyjny)</item>
<item>wysoka dokładność (adaptacyjny)</item>
<item>najwyższa (zalecane)</item>
<item>2 s</item>
<item>3 s</item>
<item>4 s</item>
<item>5 s</item>
<item>10 s</item>
<item>20 s</item>
<item>30 s</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
</string-array>
<!-- Minimum required accuracy -->
<string-array name="min_required_accuracy_options">
<item>10 m (doskonał sygnał GPS)</item>
<item>20 m</item>
<item>50 m</item>
<item>100 m</item>
<item>200 m (zalecane)</item>
<item>500 m</item>
<item>1000 m</item>
<item>2000 m</item>
<item>5000 m (słaby sygnał GPS)</item>
</string-array>
<string-array name="min_required_accuracy_options_ft">
<item>33 stopy (doskonały sygnał GPS)</item>
<item>66 stóp</item>
<item>165 stóp</item>
<item>328 stóp</item>
<item>656 stóp (zalecany)</item>
<item>1640 ft</item>
<item>0.6 mili</item>
<item>1.2 mili</item>
<item>3.1 mili (słaby sygnał GPS)</item>
</string-array>
<!-- Split frequency -->
<string-array name="split_frequency_options">
<item>Brak dzielenia</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 min</item>
<item>30 min</item>
<item>15 min</item>
<item>10 min</item>
<item>5 min</item>
<item>2 min</item>
<item>1 min</item>
</string-array>
<string-array name="split_frequency_options_ft">
<item>Brak dzielenia</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 min</item>
<item>30 min</item>
<item>15 min</item>
<item>10 min</item>
<item>5 min</item>
<item>2 min</item>
<item>1 min</item>
</string-array>
<!-- Auto-resume track timeout -->
<string-array name="auto_resume_track_timeout_options">
<item>Nigdy</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>Zawsze</item>
</string-array>
<!-- Other non-preference arrays -->
<array name="activity_types">
<item>nieznany</item>
<item>typ mieszany</item>
<item>inny</item>
<item>bieanie</item>
<item>bieganie uliczne</item>
<item>bieganie na torze</item>
<item>bieganie terenowe</item>
<item>spacer</item>
<item>szybki spacer</item>
<item>wędrówka</item>
<item>wędrówka po szlaku</item>
<item>wędrówka poza szlakiem</item>
<item>wspinaczka</item>
<item>jazda rowerem</item>
<item>jazda po szosie</item>
<item>jazda górska</item>
<item>jazda na torze</item>
<item>łyżwiarstwo</item>
<item>łyżwiarstwo torowe</item>
<item>deskorolka</item>
<item>wędrówka po sniegu</item>
<item>narciarstwo</item>
<item>snowboard</item>
<item>saneczkastwo</item>
<item>żeglarstwo lodowe</item>
<item>żeglowanie</item>
<item>paddling</item>
<item>kajak</item>
<item>wioślarstwo</item>
<item>motorówka</item>
<item>prom</item>
<item>łódź zdalnie st.</item>
<item>surfing</item>
<item>wind surfing</item>
<item>kite surfing</item>
<item>narty wodne</item>
<item>wake boarding</item>
<item>samolot</item>
<item>wodolot</item>
<item>commercial airplane</item>
<item>helikopter</item>
<item>balon powietrzny</item>
<item>sterowiec</item>
<item>samolot zdalnie st.</item>
<item>helikopter zalnie st.</item>
<item>sky jumping</item>
<item>szybowiec</item>
<item>hang gliding</item>
<item>para gliding</item>
<item>jeździectwo</item>
<item>jazda konna</item>
<item>jazda na ośle</item>
<item>samochód</item>
<item>autobus</item>
<item>pociąg</item>
<item>motocykl</item>
<item>rower terenowy</item>
<item>ATV</item>
<item>żeglarstwo lądowe</item>
<item>ringo</item>
<item>ultimate ringo</item>
</array>
<array name="waypoint_types">
<item>bankomat</item>
<item>bank</item>
<item>bar</item>
<item>grill</item>
<item>budynek</item>
<item>przystanek autobusowy</item>
<item>kawa</item>
<item>ognisko</item>
<item>kemping</item>
<item>kościół</item>
<item>kawiarnia</item>
<item>delikatesy</item>
<item>woda pitna</item>
<item>spadające skały</item>
<item>prom</item>
<item>kominek</item>
<item>remiza strażacka</item>
<item>baza wędkarska</item>
<item>stcja paliw</item>
<item>pole golfowe</item>
<item>sklep spożywczy</item>
<item>fryzjer</item>
<item>zatoka</item>
<item>szlak</item>
<item>hotel</item>
<item>dom</item>
<item>informacja</item>
<item>skrzyżowanie</item>
<item>skrzynka pocztowa</item>
<item>market</item>
<item>miejsce zbiórki</item>
<item>opera</item>
<item>parking</item>
<item>poczta</item>
<item>grota</item>
<item>posterunek policji</item>
<item>przejazd kolejowy</item>
<item>restauracja</item>
<item>zakupy</item>
<item>widok</item>
<item>sklep</item>
<item>basen</item>
<item>telefon</item>
<item>teatr</item>
<item>toaleta</item>
<item>informacja turystyczna</item>
<item>tunel</item>
<item>drzewo</item>
<item>wodospad</item>
<item>zdrój</item>
<item>punkt orientacyjny</item>
</array>
</resources>
+427
View File
@@ -0,0 +1,427 @@
<?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">Rejestruj trasę</string>
<string name="stop_recording">Zakończ rejestrowanie</string>
<string name="switch_to_sat">Tryb satelitarny</string>
<string name="switch_to_map">Tryb mapy</string>
<string name="mylocation">Moja lokalizacja</string>
<string name="list_tracks">Trasy</string>
<string name="list_markers">Znaczniki</string>
<string name="delete_all_tracks">Usuń wszystkie trasy</string>
<string name="settings">Ustawienia</string>
<string name="elevation">Wysokość n.p.m.</string>
<string name="choose_track_message">Wybierz trasę do podglądu</string>
<string name="current_track">Obecna trasa</string>
<string name="current_segment">Ostatni segment</string>
<string name="aggregated_stats">Statystyki Grupowe</string>
<!-- Error messages -->
<string name="error_no_location_manager">Brak menedżera lokalizacji na urządzeniu!</string>
<string name="error_no_gps_location_provider">Brak dostawcy lokalizacji GPS na urządzeniu.</string>
<string name="error_sending_to_mymap">Podczas przesyłania pliku do Moich Map Google pojawił się błąd. Spróbuj ponownie później.</string>
<string name="error_sending_to_docs">Podczas przesyłania pliku do Dokumentów Google pojawił się błąd. Spróbuj ponownie później.</string>
<string name="error_unable_to_start_recording">Nie można rozpocząć rejestrowania. Spróbuj ponownie.</string>
<string name="error_unable_to_stop_recording">Nie można zakończyć rejestrowania. Spróbuj ponownie.</string>
<string name="error_unable_to_read_file">Nie można czytać z pliku.</string>
<string name="error_invalid_gpx_format">Plik nie jest poprawnym plikiem GPX.</string>
<string name="error_generic">Wystąpił błąd. Spróbuj ponownie.</string>
<string name="error_track_does_not_exist">Trasa nie istnieje.</string>
<string name="error_out_of_memory">Plik jest za duży (musi być mniejszy nić 500 KB). Przerywam.</string>
<string name="error_unable_to_insert_marker">Nie można wstawić markera. Brak poprawnych danych o lokalizacji.</string>
<!-- Status messages -->
<string name="wait_for_fix">Oczekiwanie na lepsze ustalenie pozycji GPS. Upewnij się, że niebo jest widoczne.</string>
<string name="status_now_recording">Rejestrowanie nowej trasy.</string>
<string name="status_stopped_recording">Zatrzymano rejestrowanie nowej trasy.</string>
<string name="status_new_mymap_has_been_created">Utworzona nową Mapę Google na maps.google.com.</string>
<string name="status_tracks_have_been_uploaded">Twoja trasa został wysłana do maps.google.com.</string>
<string name="status_tracks_have_been_uploaded_to_new_doc">Twoja trasa została wysłana do nowego arkusza na docs.google.com.</string>
<string name="status_tracks_have_been_uploaded_to_docs">Twoja trasa została wysłana do docs.google.com.</string>
<string name="status_mymap_info">Trasa będzie widoczna za kilka minut.</string>
<string name="status_enable_gps">Tapnij tutaj, aby przejść do ustawień Lokalizacji i zabezpieczeń, a następnie zaznacz Użyj satelitów GPS. Naciśnij klawisz Wstecz, aby powrócić.</string>
<string name="status_approximate_location">Uwaga: używana jest przybliżona lokalizacja ustalona za pomocą stacji komórkowych i sieci WIFI.</string>
<string name="status_no_location">Lokalizacja nie może być obecnie ustalona. Włącz wiecej źródeł (zobacz ustawienia Lokalizacji i zabezpieczeń) i upewnij się, że niebo jest widoczne. Spróbuj później.</string>
<string name="status_waypoint_inserted">Wstawiono znacznik punktu orientacyjnego.</string>
<string name="status_statistics_inserted">Wstawiono znacznik statystyk.</string>
<!-- Progress dialog -->
<string name="progress_title">Proszę czekać...</string>
<string name="progress_message">Przesyłanie trasy do Google.</string>
<string name="progress_message_reading_track">Czytanie trasy...</string>
<string name="progress_message_preparing_track">Przygotowywanie trasy...</string>
<string name="progress_message_authenticating_mymaps">Uwierzytelnianie w Mapach Google...</string>
<string name="progress_message_authenticating_docs">Uwierzytelnianie w Dokumentach Google...</string>
<string name="progress_message_sending_docs">Przesyłanie trasy do Dokumentów Google...</string>
<string name="progress_message_sending_mymaps">Przesyłanie trasy do Map Google...</string>
<string name="progress_message_creating_map">Tworzenie nowej Mojej Mapy...</string>
<string name="import_progress_message">Importowanie trasy GPS. Poczekaj chwilę.</string>
<string name="write_progress_message">Zapisywanie pliku na kartę SD. Poczekaj chwilę.</string>
<string name="progress_message_updating_track">Aktualizacja statystyk trasy...</string>
<!-- Used by DeleteAllDataHandler -->
<string name="are_you_sure_question">Jesteś pewien?</string>
<string name="all_data_will_be_permanently_deleted">Wszystkie trasy i znaczniki zostaną usunięte z telefonu.</string>
<string name="track_will_be_permanently_deleted">Trasa i jej znaczniki zostanie całkowicie usunięta z telefonu.</string>
<string name="marker_will_be_permanently_deleted">Znacznik zostanie całkowicie usunięty z telefonu.</string>
<!-- Text and links when uploading to Google My Maps -->
<string name="new_map_description">Utworzono za pomocą My Tracks na Android.</string>
<string name="my_map_link">Utworzona przez &lt;a href=\'http://mytracks.appspot.com\'&gt;My Tracks&lt;/a&gt; na Android.</string>
<string name="new_map_title">Nowa mapa</string>
<!-- Formatting of track and marker descriptions -->
<string name="points">Punkty</string>
<string name="recorded_date">Zapisana</string>
<string name="category">Typ aktywności</string>
<string name="waypoint_category">Typ znacznika</string>
<string name="part">(Część %d)</string>
<!-- Used by the track recording service -->
<string name="recording_your_track">Rejestrowanie twojej trasy...</string>
<string name="new_track">Trasa %d</string>
<!-- Common -->
<string name="no">Nie</string>
<string name="yes">Tak</string>
<string name="ok">OK</string>
<string name="error">Błąd</string>
<string name="success">Powodzenie</string>
<string name="waypoint">Punkt orientacyjny</string>
<!-- Placemark labels for start and end of track -->
<string name="start">(Początek)</string>
<string name="end">(Koniec)</string>
<!-- TrackList -->
<string name="tracklist_show_track">Pokaż</string>
<string name="tracklist_send_to_mymaps">Wyślij do Moich Map Google</string>
<string name="tracklist_send_to_new_mymap">Wyślij do Moich Map Google jako nową mapę</string>
<string name="tracklist_send_to_docs">Wyślij do Dokumentów Google</string>
<string name="tracklist_send_to_google">Wyślij do Google...</string>
<string name="tracklist_delete_track">Usuń z telefonu...</string>
<string name="tracklist_edit_track">Edytuj szczegóły</string>
<string name="tracklist_write_to_sd">Zapisz na karcie SD...</string>
<string name="tracklist_save_as_gpx">Zapisz plik GPX</string>
<string name="tracklist_save_as_kml">Zapisz plik KML</string>
<string name="tracklist_save_as_csv">Zapisz plik CSV</string>
<string name="tracklist_btn_send_all_to_mymaps">Wyślij do Moich Map Google</string>
<string name="tracklist_btn_delete_all">Usuń wszystkie trasy</string>
<!-- Export All -->
<string name="tracklist_btn_export_all">Eksportuj wszystkie trasy</string>
<string name="export_done">Eksport zakończony</string>
<string name="tracklist_pick_one">Wybierz trasę. Naciśnij i przytrzymaj, aby wybrać opcje.</string>
<string name="tracklist_all_tracks">Wszystkie trasy:</string>
<string name="tracklist_this_track">Ta trasa</string>
<string name="tracklist_share_track">Podziel się z przyjaciółmi...</string>
<string name="tracklist_share_link">Link do Moich Map Google</string>
<string name="tracklist_share_gpx_file">Plik GPX</string>
<string name="tracklist_share_kml_file">PLik KML</string>
<string name="tracklist_share_csv_file">Plik CSV</string>
<string name="tracklist_clear_map">Wyczyść mapę</string>
<!-- Waypoints list -->
<string name="waypointslist_btn_delete_all">Wyczyść wszystkie znaczniki</string>
<string name="waypointslist_this_waypoint">Ten znacznik</string>
<string name="waypointslist_delete_waypoint">Usuń</string>
<string name="waypointslist_edit_waypoint">Edytuj szczegóły</string>
<string name="waypointslist_show_waypoint">Pokaż na mapie</string>
<string name="waypointslist_btn_insert_waypoint">Wstaw punkt orientacyjny</string>
<string name="waypointslist_btn_insert_statistics">Wstaw statystyki</string>
<string name="waypointslist_pick_one">Wybierz znacznik. Naciśnij i przytrzymaj, aby wybrać opcje.</string>
<!-- MyTracks Details -->
<string name="trackdetails_title">Szczegóły trasy</string>
<string name="trackdetails_name">Nazwa trasy</string>
<string name="trackdetails_description">Opis trasy</string>
<string name="trackdetails_category">Typ aktywności (np. wspinanie, jazda rowerem, bieganie...)</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Szczegóły znacznika</string>
<string name="waypointdetails_name">Nazwa znacznika</string>
<string name="waypointdetails_description">Opis znacznika</string>
<string name="waypointdetails_category">Typ znacznika (np. budynek, jaskinia, punkt widokowy...)</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Ustawienia My Tracks</string>
<string name="settings_display">Wyświetlanie</string>
<string name="settings_recording">Rejestrowanie</string>
<string name="settings_sharing">Dzielenie</string>
<string name="settings_announcement_frequency">Częstotliwość komunikatów</string>
<string name="settings_announcement_frequency_summary">Minimalny czas między dwoma komunikatami głosowymi</string>
<string name="settings_min_recording_distance">Min. odległość między punktami</string>
<string name="settings_min_recording_distance_summary">Najmniejsza odległość między dwoma zapisami twojej pozycji</string>
<string name="settings_max_recording_distance">Max. odległość między punktami</string>
<string name="settings_max_recording_distance_summary">Jeśli dwa punkty są dalej niż ta wartość, trasa zostanie podzielona</string>
<string name="settings_min_recording_interval">Min. czas między punktami</string>
<string name="settings_min_recording_interval_summary">Najmniejsza odstęp czasowy między dwoma zapisami twojej pozycji</string>
<string name="settings_min_required_accuracy">Minimalna dokładność</string>
<string name="settings_min_required_accuracy_summary">Zapis zostanie zatrzymany jeśli twoja pozycja jest mniej dokładana niż ta wartość</string>
<string name="settings_metric_units">Używaj jednostek metrycznych</string>
<string name="settings_metric_units_summary_on">Wszystkie odległości będą wyświetlane w jednostkach metrycznych</string>
<string name="settings_metric_units_summary_off">Wszystkie odległości będą wyświetlane w jednostkach imperialnych</string>
<string name="settings_map_public">Ustaw mapy jako publiczne</string>
<string name="settings_map_public_summary_on">Przesłane mapy będą dostępne dla wszystkich na maps.google.com</string>
<string name="settings_map_public_summary_off">Przesłane mapy nie będą dostępne publicznie na maps.google.com</string>
<string name="settings_report_speed_or_pace">Wyświetlanie prędkości</string>
<string name="settings_report_speed_or_pace_summary_on">Prędkość będzie wyświetlana</string>
<string name="settings_report_speed_or_pace_summary_off">Tempo będzie wyświetlane</string>
<string name="settings_share_url_only">Dziel się tylko odnośnikiem</string>
<string name="settings_share_url_only_summary_on">Tylko odnośnik do mapy będzie wysyłany podczas dzielenia się</string>
<string name="settings_share_url_only_summary_off">Zawartość trasy będzie również przesyłana</string>
<string name="settings_signal_sampling_frequency">Częstotliowść próbkowania sygnału</string>
<string name="settings_signal_sampling_frequency_summary">Czas między dwoma zapisami siły sygnału telefonu</string>
<string name="settings_split_frequency">Częstotliwość rozdzielania</string>
<string name="settings_split_frequency_summary">Maksymalny czas lub odległość po którym wstawiony zostanie znacznik rozdzielenia</string>
<string name="settings_auto_resume_track_timeout">Automatyczne wznawianie rejestracji trasy</string>
<string name="settings_auto_resume_track_timeout_summary">Maksymalny czas w minutach, który upłynął od ostatniego restartu telefonu, a po którym nie zostania wznowiona poprzednia trasa</string>
<string name="pace_label">Tempo</string>
<string name="eclair_required">Dostępne tylko w wersji Android 2.0 lub nowszej</string>
<string name="settings_sensor_type">Typ Czujnika</string>
<string name="settings_sensor_type_summary">Typ czujnika do podłączenia.</string>
<string name="settings_sensor_id">Id Czujnika</string>
<string name="settings_sensor_id_summary">Identyfikator wysyłany do czujników.</string>
<string name="sensor_id_malformed">Identyfikator musi być liczbą całkowitą.</string>
<string name="bluetooth_sensor_title">Czujnik bluetooth</string>
<string name="bluetooth_sensor_summary">Czujnik bluetooth do podłączenia.</string>
<string name="settings_modular_stats_display_title">Widok Statystyk Modularnych</string>
<string name="settings_modular_stats_display_summary_on">Ekran statystyk jest podzielony w zależności od trybu.</string>
<string name="settings_modular_stats_display_summary_off">Wyświetl wszystkie statystyki na jednym ekranie.</string>
<string name="settings_backup">Kopia zapasowa (BETA)</string>
<string name="settings_sd_backup">Kopia zapasowa na kartę SD</string>
<string name="settings_backup_to_sd">Utwórz kopię</string>
<string name="settings_backup_to_sd_summary">Tworzy kopię zapasową wszystkich tras i ustawień i zapisuje je na karcie SD</string>
<string name="settings_restore_from_sd">Odtwórz z kopii</string>
<string name="settings_restore_from_sd_summary">Odtwarza wszystkie trasy i ustawienia z kopii zapasowej z karty SD</string>
<string name="settings_no_backup_while_recording">Nie można tworzyć/odtwarzać kopii zapasowej podczas rejestrowania trasy.</string>
<!-- Backups -->
<string name="restore_overwrites_warning">Odtworzenie z kopii zapasowej z karty SD zastąpi wszystkie bieżące trasy i ustawienia. Jesteś tego pewien?</string>
<string name="select_backup_to_restore">Wybierz kopię do odtworzenia</string>
<string name="no_backups">Nie znaleziono kopii zapasowych.</string>
<string name="backup_write_progress_message">Zapisywanie wszystkich tras i ustawień na kartę SD. To może trochę potrwać...</string>
<string name="backup_import_progress_message">Odczytywanie wszystkich tras i ustawień z karty SD. To może trochę potrwać...</string>
<!-- MyTracksSky -->
<string name="mytracks_sky">Satelity GPS</string>
<!-- My Maps -->
<string name="select_map_message">Wybierz mapę z twojego konta:</string>
<string name="create_new_map">Utwórz nową mapę...</string>
<string name="map_list_title">Wyślij do Moich Map Google</string>
<string name="maps_list_loading">Wczytywanie map...</string>
<string name="maps_list_failed">Nie można wczytać twoich map!</string>
<string name="open_map">Wybierz tę mapę</string>
<string name="edit_map_details">Edytuj szczegóły mapy</string>
<string name="share_map">Podziel się mapą z przyjaciółmi</string>
<string name="share_track">Podziel się trasą z przyjaciółmi</string>
<string name="delete_map">Usuń mapę z twojego konta</string>
<string name="map_title">Tytuł mapy:</string>
<string name="map_description">Opis mapy:</string>
<string name="map_searchable_edit">Pokazuj tę pamę i jej zawartość w wynikach wyszukiwania</string>
<string name="map_not_searchable_edit">Nie pokazuj tej mapy i jej zawartości w wynikach wyszukiwania. Dostęp do mapy będzie ciągle możliwy poprzez odnośnik</string>
<string name="map_searchable">Ta mapa i jej zawartość będzie dostępna w wynikach wyszukiwania</string>
<string name="map_not_searchable">Mapa i jej zawartość nie będzie dostępna w wynikach wyszukiwania, ale będzie dostępna przez bezpośredni odnośnik</string>
<string name="close">Zamknij</string>
<string name="edit">Edytuj</string>
<string name="cancel">Anuluj</string>
<string name="save">Zapisz</string>
<!-- Sharing -->
<string name="share_map_subject">Chciałbym się z Tobą podzielić mapą</string>
<string name="share_map_body_format">Myślę, że może Cię zainteresować ta mapa:
%s</string>
<string name="send_track_subject">Chciałbym się z Tobą podzielić trasą</string>
<string name="send_track_body_format">Myślę, że może Cię zainteresować ta trasa.
Trasę możesz obejrzeć w Google Earth lub innym programie do obsługi map.</string>
<!-- Visibility -->
<string name="public_map">Publiczna</string>
<string name="unlisted_map">Niepubliczna</string>
<string name="mapslist_no_maps_available">Nie masz jeszcze żadnych map.</string>
<!-- Statistics View -->
<!-- Average Moving -->
<string name="average_moving_speed_label">Średnia prędkość poruszania</string>
<string name="average_moving_pace_label">Średnie tempo poruszania</string>
<!-- Average -->
<string name="average_speed_label">Średnia prędkość</string>
<string name="average_pace_label">Średnie tempo</string>
<string name="elevation_chart_name">Wykres wysokości</string>
<string name="elevation_gain_label">Wzrost wysokości</string>
<string name="elevation_label">Wysokość</string>
<string name="latitude_label">Szerokość</string>
<string name="longitude_label">Długość</string>
<!-- Min -->
<string name="min_elevation_label">Min. wysokość</string>
<string name="min_grade_label">Min. nachylenie</string>
<string name="min_pace_label">Min. tempo</string>
<!-- Max -->
<string name="max_elevation_label">Maks. wysokość</string>
<string name="max_grade_label">Maks. nachylenie</string>
<string name="max_speed_label">Maks. prędkość</string>
<string name="loading_track">Wczytywanie trasy...</string>
<!-- Chart Settings -->
<string name="chart_settings">Ustawienia wykresów</string>
<!-- X Axis -->
<string name="x_axis">Oś X</string>
<string name="by_distance">Według odległości</string>
<string name="by_time">Według czasu</string>
<!-- Y Axis -->
<string name="y_axis">Oś Y</string>
<!-- Units -->
<!-- Time -->
<string name="hour">godzina</string>
<string name="hours">godziny</string>
<string name="hr">godz.</string>
<string name="min">min</string>
<string name="minute">minuta</string>
<string name="minutes">minuty</string>
<string name="second">sekunda</string>
<string name="seconds">sekundy</string>
<!-- Distance -->
<string name="meter">m</string>
<string name="kilometer">km</string>
<string name="kilometers_long">kilometerów</string>
<string name="feet">st.</string>
<string name="mile">mi</string>
<string name="miles_long">mil</string>
<!-- Speed and Pace -->
<string name="kilometer_per_hour">km/h</string>
<string name="kilometer_per_hour_long">kilometry na godzinę</string>
<string name="min_per_kilometer">min/km</string>
<string name="per_kilometer">na kilometr</string>
<string name="mile_per_hour">mi/h</string>
<string name="mile_per_hour_long">mile na godzinę</string>
<string name="min_per_mile">min/mi</string>
<string name="per_mile">na milę</string>
<!-- Stats Labels -->
<string name="moving_time_label">Czas w ruchu</string>
<string name="total_time_label">Całkowity czas</string>
<string name="time_label">Czas</string>
<string name="sd_unavail">Karta SD niedostępna</string>
<string name="speed">Prędkość</string>
<string name="start_trip">Musisz rozpocząć podróż.</string>
<string name="statistics">Statystyki</string>
<string name="total_distance_label">Całkowita odległość</string>
<string name="distance_label">Odległość</string>
<string name="unknown">-</string>
<string name="waiting">Oczekiwanie...</string>
<!-- End User License Agreement -->
<string name="eula_title">Licencja użytkownika końcowego</string>
<string name="accept">Zaakceptuj</string>
<string name="decline">Odrzuć</string>
<!-- About -->
<string name="about">O</string>
<string name="help">Pomoc</string>
<string name="about_team">Zespół:</string>
<string name="about_version">Wersja:</string>
<string name="about_email">Wsparcie</string>
<string name="about_support">Wsparcie:</string>
<!-- Send to Google -->
<string name="sendtogoogle_title">Wyślij do...</string>
<string name="sendtogoogle_google_mymaps">Moje Mapy Google</string>
<string name="sendtogoogle_google_docs">Dokumenty Google</string>
<string name="sendtogoogle_send_now">Wyślij teraz</string>
<string name="sendtogoogle_create_new_map">Utwórz nową mapę</string>
<string name="sendtogoogle_pick_existing_map">Wybierz istniejąca mapę</string>
<string name="sendtogoogle_send_stats">Wyślij statystyki trasy</string>
<string name="sendtogoogle_send_stats_and_points">Wyślij statystyki i punkty</string>
<!-- Getting the account to upload to. -->
<string name="no_account_found_title">Znaleziono nowe konto</string>
<string name="no_account_found">Nie znaleziono konta do przesyłu.</string>
<string name="choose_account_title">Wybierz konto</string>
<!-- SD card -->
<string name="io_no_external_storage_found">Nie znaleziono zewnętrznej pamięci. Upewniej się, że karta SD jest w telefonie i nie jest podłączona do komputera.</string>
<string name="io_write_failed">Nie można zapisać pliku.</string>
<string name="io_read_failed">Nie można odczytać pliku.</string>
<string name="io_create_dir_failed">Nie można utworzyć katalogu na karcie SD.</string>
<string name="io_write_finished">Zakończono zapisywanie na karcie SD.</string>
<string name="io_read_finished">Zakończono odczytywanie z karty SD.</string>
<!-- VersionChecker -->
<string name="version_check_title">Sprawdzanie aktualizacji</string>
<string name="version_check_upgrade_button">Aktualizuj</string>
<string name="version_check_continue_button">Kontynuuj bez aktualizacji</string>
<string name="upgrade_required">Twoja wersja %1$s może nie działać poprawnie i nie jest już wspierana. Jeśli chcesz nadal używać %1$s zaktualizuj program.</string>
<string name="upgrade_recommended">Twoja wersja %1$s może nie działać poprawnie. MOżesz zaktualizować program lub używać obecnej wersji na własne ryzyko. Niektóre funkcje, takie jak synchronizacja z serwerem nie będą działać dopóki nie zaktualizujesz programu.</string>
<string name="upgrade_available">Dostępna jest nowa wersja %1$s is available. Możesz nadal używać obecnej wersji bez problemów, nie będziesz miał jednak dostępu do najnowszych funkcji.</string>
<!-- Welcome -->
<string name="welcome">Witamy</string>
<string name="welcome_read_later">Ok</string>
<string name="welcome_about">O My Tracks</string>
<!-- Announcement -->
<string name="announce_no_distance">My Tracks brak odległości</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 w %4$s w tempie %5$s %6$s</string>
<!-- Check Units -->
<string name="check_units_title">Jednostki miary</string>
<string name="check_units_message">Domyślnie używane są jednostki metryczne. Chcesz to zmienić?</string>
<!-- Signal Strength -->
<string name="error_rate"> Poziom błędów: %1$d</string>
<string name="signal_to_noise_ratio"> Stosunek sygnału do szumu: %1$d</string>
<string name="ecio"> Ec/lo: %1$.1f dB</string>
<string name="gsm_strength">Siła GSM: %1$d</string>
<string name="cdma_strength">Siła CDMA: %1$d dBm</string>
<string name="evdo_strength">Siła EVDO: %1$d dBm</string>
</resources>
-1
View File
@@ -157,7 +157,6 @@ limitations under the License.
<string name="settings_sharing">Compartilhamento</string>
<string name="settings_announcement_frequency">Tempo entre anúncios</string>
<string name="settings_announcement_frequency_summary">Tempo mínimo entre dois anúncios de voz</string>
<string name="settings_announcement_not_available_summary">Anúncios de voz não estão disponíveis em sua versão do Android</string>
<string name="settings_min_recording_distance">Dist. mín. entre pontos</string>
<string name="settings_min_recording_distance_summary">Menor distância entre duas gravações da sua posição</string>
<string name="settings_max_recording_distance">Dist. máx. entre pontos</string>
+278
View File
@@ -0,0 +1,278 @@
<?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>Av</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
<item>60 min</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 (rekommenderad)</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 (rekommenderad)</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 (rekommenderad)</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 (rekommenderad)</item>
<item>1640 ft</item>
<item>0.6 miles</item>
<item>3.1 miles</item>
</string-array>
<!-- Minimum recording interval -->
<string-array name="min_recording_interval_options">
<item>batterioptimerad (anpassningsbar)</item>
<item>hög precision (anpassningsbar)</item>
<item>högsta (rekommenderad)</item>
<item>2 s</item>
<item>3 s</item>
<item>4 s</item>
<item>5 s</item>
<item>10 s</item>
<item>20 s</item>
<item>30 s</item>
<item>1 min</item>
<item>2 min</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>30 min</item>
</string-array>
<!-- Minimum required accuracy -->
<string-array name="min_required_accuracy_options">
<item>10 m (bra GPS-signal)</item>
<item>20 m</item>
<item>50 m</item>
<item>100 m</item>
<item>200 m (rekommenderad)</item>
<item>500 m</item>
<item>1000 m</item>
<item>2000 m</item>
<item>5000 m (dålig GPS-signal)</item>
</string-array>
<string-array name="min_required_accuracy_options_ft">
<item>33 ft (bra GPS-signal)</item>
<item>66 ft</item>
<item>165 ft</item>
<item>328 ft</item>
<item>656 ft (rekommenderad)</item>
<item>1640 ft</item>
<item>0.6 miles</item>
<item>1.2 miles</item>
<item>3.1 miles (dålig GPS-signal)</item>
</string-array>
<!-- Split frequency -->
<string-array name="split_frequency_options">
<item>Ingen delning</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 min</item>
<item>30 min</item>
<item>15 min</item>
<item>10 min</item>
<item>5 min</item>
<item>2 min</item>
<item>1 min</item>
</string-array>
<string-array name="split_frequency_options_ft">
<item>Ingen delning</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 min</item>
<item>30 min</item>
<item>15 min</item>
<item>10 min</item>
<item>5 min</item>
<item>2 min</item>
<item>1 min</item>
</string-array>
<!-- Other non-preference arrays -->
<array name="activity_types">
<item>okänd</item>
<item>blandat</item>
<item>annat</item>
<item>löpning</item>
<item>väglöpning</item>
<item>banlöpning</item>
<item>spårlöpning</item>
<item>gång</item>
<item>gångsport</item>
<item>vandring</item>
<item>spårvandring</item>
<item>vildmarksvandring</item>
<item>klättring</item>
<item>cykling</item>
<item>vägcyckling</item>
<item>mountainbikeåkning</item>
<item>bancyckling</item>
<item>skridskoåkning</item>
<item>inlineåkning</item>
<item>skateboardåkning</item>
<item>snöskovandring</item>
<item>skidåkning</item>
<item>snowboardåkning</item>
<item>kälke</item>
<item>issegling</item>
<item>segling</item>
<item>paddling</item>
<item>kayakpaddling</item>
<item>rodd</item>
<item>motorbåtsåkning</item>
<item>färja</item>
<item>RC båt</item>
<item>surfning</item>
<item>vindsurfning</item>
<item>kite surfing</item>
<item>vattenskidåkning</item>
<item>wakeboardåkning</item>
<item>flygplan</item>
<item>sjöflygplan</item>
<item>kommersiellt flygplan</item>
<item>helikopter</item>
<item>varmlyftsballong</item>
<item>luftskepp</item>
<item>RC flygplan</item>
<item>RC helicopter</item>
<item>falskärmshoppning</item>
<item>segelflygning</item>
<item>hängflygning</item>
<item>skärmflygning</item>
<item>ridning</item>
<item>hästridning</item>
<item>åsneridning</item>
<item>köra bil</item>
<item>köra buss</item>
<item>tåg</item>
<item>motorcykel</item>
<item>motorcross</item>
<item>fyrhjuling</item>
<item>landsegling</item>
<item>frisbee</item>
<item>ultimate frisbee</item>
</array>
<array name="waypoint_types">
<item>bankomat</item>
<item>bank</item>
<item>bar</item>
<item>barbeque</item>
<item>byggnad</item>
<item>bushållplats</item>
<item>café</item>
<item>lägereld</item>
<item>kampingklatts</item>
<item>kyrka</item>
<item>kaffestuga</item>
<item>närbutik</item>
<item>driksvatten</item>
<item>fallande stenar</item>
<item>färja</item>
<item>äldplats</item>
<item>brandstation</item>
<item>fiske</item>
<item>bensinstation</item>
<item>golfbana</item>
<item>mataffär</item>
<item>hårfrissör</item>
<item>hamn</item>
<item>vandringsled</item>
<item>hotel</item>
<item>hus</item>
<item>information</item>
<item>korsning</item>
<item>postlåda</item>
<item>marknad</item>
<item>mötesplats</item>
<item>opera</item>
<item>parkering</item>
<item>postkontor</item>
<item>grop</item>
<item>polisstation</item>
<item>järnvägskorssning</item>
<item>restaurant</item>
<item>shopping</item>
<item>sevärdhet</item>
<item>affär</item>
<item>simbassäng</item>
<item>telefon</item>
<item>teater</item>
<item>toalett</item>
<item>turistinformation</item>
<item>tunnel</item>
<item>träd</item>
<item>vatenfall</item>
<item>wattenfontän</item>
<item>etappmål</item>
</array>
</resources>
+394
View File
@@ -0,0 +1,394 @@
<?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">Spela in spår</string>
<string name="stop_recording">Stoppa inspelning</string>
<string name="switch_to_sat">Satellitläge</string>
<string name="switch_to_map">Kartläge</string>
<string name="mylocation">Min plats</string>
<string name="list_tracks">Spår</string>
<string name="list_markers">Markörer</string>
<string name="delete_all_tracks">Ta bort alla spår</string>
<string name="settings">Inställningar</string>
<string name="elevation">Höjd</string>
<string name="choose_track_message">Välj ett spår att visa</string>
<string name="current_track">Nuvarande spår</string>
<string name="current_segment">Senaste segmentet</string>
<!-- Error messages -->
<string name="error_no_location_manager">Ingen platshanterare hittades på denna enhet!</string>
<string name="error_no_gps_location_provider">Ingen GPS-positioneringstjänst hittades på denna enhet.</string>
<string name="error_sending_to_mymap">Det uppstod ett fel i sändningen av ditt spår till Google Mina Kartor. Försök igen senare.</string>
<string name="error_sending_to_docs">Det uppstod ett fel i sändningen av ditt spår till Google Dokument. Försök igen senare.</string>
<string name="error_unable_to_start_recording">Kan inte starta inspelningen. Försök igen.</string>
<string name="error_unable_to_stop_recording">Kan inte stoppa inspelningen. Försök igen.</string>
<string name="error_unable_to_read_file">Kan inte läsa från fil.</string>
<string name="error_invalid_gpx_format">Filen har inte ett giltigt GPX-format.</string>
<string name="error_generic">Ett fel uppstod. Försök igen.</string>
<string name="error_track_does_not_exist">Spåret finns inte.</string>
<string name="error_out_of_memory">Den här filen är för stor (måste vara mindre än 500 KB). Avbryter.</string>
<string name="error_unable_to_insert_marker">Kan inte skapa markör. Ingen position tillgänglig.</string>
<!-- Status messages -->
<string name="wait_for_fix">Väntar på bättre GPS-position. Se till att himlen är synlig.</string>
<string name="status_now_recording">Spelar nu in ett nytt spår.</string>
<string name="status_stopped_recording">Stoppade inspelning av ditt spår.</string>
<string name="status_new_mymap_has_been_created">En ny Google Mina Kartor har skapats på maps.google.com.</string>
<string name="status_tracks_have_been_uploaded">Ditt spår har skickats till maps.google.com.</string>
<string name="status_tracks_have_been_uploaded_to_new_doc">Ditt spår har skickats till ett nytt kalkylblad på docs.google.com.</string>
<string name="status_tracks_have_been_uploaded_to_docs">Ditt spår har skickats till docs.google.com.</string>
<string name="status_mymap_info">Det kan ibland ta några minuter innan nya spår visas.</string>
<string name="status_enable_gps">Klicka här för att gå till Säkerhet och Platsinställningar och kryssa i \'aktivera gps-satteliter\'. Tryck på tillbakaknappen för att återgå.</string>
<string name="status_approximate_location">OBS: En ungefärlig position som avgörs från mobilmaster och WIFI-nät visas.</string>
<string name="status_no_location">En position kan för närvarande inte fastställas. Aktivera mer platskällor (se Säkerhet och Platsinställningar) och se till att himlen är synlig. Försök igen senare.</string>
<string name="status_waypoint_inserted">En positionsmarkör har lagts in.</string>
<string name="status_statistics_inserted">En statistikmarkör har lagts in.</string>
<!-- Progress dialog -->
<string name="progress_title">Var god vänta...</string>
<string name="progress_message">Skickar ditt spår till Google.</string>
<string name="progress_message_reading_track">Läser spår...</string>
<string name="progress_message_preparing_track">Förbereder spår...</string>
<string name="progress_message_authenticating_mymaps">Autentiserar till Google Kartor...</string>
<string name="progress_message_authenticating_docs">Autentiserar till Google Dokument...</string>
<string name="progress_message_sending_docs">Skickar spår till Google Dokument...</string>
<string name="progress_message_sending_mymaps">Skickar spår till Google Kartor...</string>
<string name="progress_message_creating_map">Skapar en ny Min Karta...</string>
<string name="import_progress_message">Importerar ett GPS-spår. Detta kan ta en stund.</string>
<string name="write_progress_message">Skriver till SD-kortet. Detta kan ta en stund.</string>
<string name="progress_message_updating_track">Uppdaterar spårstatistik...</string>
<!-- Used by DeleteAllDataHandler -->
<string name="are_you_sure_question">Är du säker?</string>
<string name="all_data_will_be_permanently_deleted">Alla spår och markörer kommer att tas bort från telefonen.</string>
<string name="track_will_be_permanently_deleted">Spåret och dess markörer kommer att permanent tas bort från telefonen.</string>
<string name="marker_will_be_permanently_deleted">Markören kommer att permanent tas bort från telefonen.</string>
<!-- Text and links when uploading to Google My Maps -->
<string name="new_map_description">Skapad av My Tracks på Android.</string>
<string name="my_map_link">Skapad av &lt;a href=\'http://mytracks.appspot.com\'&gt;My Tracks&lt;/a&gt; på Android.</string>
<string name="new_map_title">Ny karta</string>
<!-- Formatting of track and marker descriptions -->
<string name="points">Punkter</string>
<string name="recorded_date">Inspelad</string>
<string name="category">Aktivitetstyp</string>
<string name="waypoint_category">Markörtyp</string>
<string name="part">(Del %d)</string>
<!-- Used by the track recording service -->
<string name="recording_your_track">Spelar in ditt spår...</string>
<string name="new_track">Spår %d</string>
<!-- Common -->
<string name="no">Nej</string>
<string name="yes">Ja</string>
<string name="ok">OK</string>
<string name="error">Fel</string>
<string name="success">Lyckades</string>
<string name="waypoint">Positionsmärke</string>
<!-- Placemark labels for start and end of track -->
<string name="start">(Start)</string>
<string name="end">(Slut)</string>
<!-- TrackList -->
<string name="tracklist_show_track">Visa</string>
<string name="tracklist_send_to_mymaps">Skicka till Google Mina Kartor</string>
<string name="tracklist_send_to_new_mymap">Skicka till ny Google Mina Kartor</string>
<string name="tracklist_send_to_docs">Skicka till Google Dokument</string>
<string name="tracklist_send_to_google">Skicka till Google...</string>
<string name="tracklist_delete_track">Ta bort från telefonen...</string>
<string name="tracklist_edit_track">Ändra detaljer</string>
<string name="tracklist_write_to_sd">Skriv till SD-kort...</string>
<string name="tracklist_save_as_gpx">Spara en GPX-fil</string>
<string name="tracklist_save_as_kml">Spara en KML-fil</string>
<string name="tracklist_save_as_csv">Spara en CSV-fil</string>
<string name="tracklist_btn_send_all_to_mymaps">Skicka till Google Mina Kartor</string>
<string name="tracklist_btn_delete_all">Ta bort alla spår</string>
<!-- Export All -->
<string name="tracklist_btn_export_all">Exportera alla spår</string>
<string name="export_done">Exporteringen slutfördes</string>
<string name="tracklist_pick_one">Välj ett spår. Tryck &amp; håll för fler alternativ.</string>
<string name="tracklist_all_tracks">Alla spår:</string>
<string name="tracklist_this_track">Det här spåret</string>
<string name="tracklist_share_track">Dela med vänner...</string>
<string name="tracklist_share_link">En länk till Google Mina Kartor</string>
<string name="tracklist_share_gpx_file">En GPX-fil</string>
<string name="tracklist_share_kml_file">En KML-fil</string>
<string name="tracklist_share_csv_file">En CSV-fil</string>
<string name="tracklist_clear_map">Rensa karta</string>
<!-- Waypoints list -->
<string name="waypointslist_btn_delete_all">Ta bort alla markörer</string>
<string name="waypointslist_this_waypoint">Den här markören</string>
<string name="waypointslist_delete_waypoint">Ta bort</string>
<string name="waypointslist_edit_waypoint">Ändra detaljer</string>
<string name="waypointslist_show_waypoint">Visa på karta</string>
<string name="waypointslist_btn_insert_waypoint">Infoga positionspunkt</string>
<string name="waypointslist_btn_insert_statistics">Infoga statistik</string>
<string name="waypointslist_pick_one">Välj en markör. Tryck &amp; håll för fler alternativ.</string>
<!-- MyTracks Details -->
<string name="trackdetails_title">Spårdetaljer</string>
<string name="trackdetails_name">Spårnamn</string>
<string name="trackdetails_description">Spårbeskrivning</string>
<string name="trackdetails_category">Aktivitetstyp (t ex vandring, cyckling, löpning, klättring...)</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Markördetaljer</string>
<string name="waypointdetails_name">Markörnamn</string>
<string name="waypointdetails_description">Markörbeskrivning</string>
<string name="waypointdetails_category">Markörtyp (e.g. byggnad, grop, sevärdhet...)</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">My Tracks inställningar</string>
<string name="settings_display">Visning</string>
<string name="settings_recording">Inspelning</string>
<string name="settings_sharing">Delning</string>
<string name="settings_announcement_frequency">Meddelandefrekvens</string>
<string name="settings_announcement_frequency_summary">Minimumtid mellan två röstmeddelanden</string>
<string name="settings_min_recording_distance">Min distans mellan två punkter</string>
<string name="settings_min_recording_distance_summary">Minsta distans mellan två inspelningar av din position</string>
<string name="settings_max_recording_distance">Max distans mellan punter</string>
<string name="settings_max_recording_distance_summary">Om två punkter är längre bort än det här, kommer spåret att delas</string>
<string name="settings_min_recording_interval">Min tid mellan punkter</string>
<string name="settings_min_recording_interval_summary">Minsta tidsintervall mellan två inspelningar av din position</string>
<string name="settings_min_required_accuracy">Minsta precision</string>
<string name="settings_min_required_accuracy_summary">Inspelningen kommer att pausas om din position inte är minst så här precis</string>
<string name="settings_metric_units">Använd metersystemet</string>
<string name="settings_metric_units_summary_on">Alla distanser kommer att visas i metriska enheter</string>
<string name="settings_metric_units_summary_off">Alla distanser kommer att visas i brittiska enheter</string>
<string name="settings_map_public">Gör kartor offentliga</string>
<string name="settings_map_public_summary_on">Uppladdade kartor kommer att vara synliga för alla på maps.google.com</string>
<string name="settings_map_public_summary_off">Uppladdade kartor kommer inte att visas offentligt på maps.google.com</string>
<string name="settings_report_speed_or_pace">Visa hastighet</string>
<string name="settings_report_speed_or_pace_summary_on">Hastighet kommer att visas</string>
<string name="settings_report_speed_or_pace_summary_off">Tempo kommer att visas</string>
<string name="settings_share_url_only">Dela bara länk</string>
<string name="settings_share_url_only_summary_on">Bara länken till kartan kommer att skickas vid delning</string>
<string name="settings_share_url_only_summary_off">Innehållet i spåret kommer också att inkluderas vid delning</string>
<string name="settings_signal_sampling_frequency">Signalprovsfrekvens</string>
<string name="settings_signal_sampling_frequency_summary">Tid mellan två inspelningar av telefonens signalstyrka</string>
<string name="settings_split_frequency">Delningsfrekvens</string>
<string name="settings_split_frequency_summary">Maximala tiden eller distansen varefter en ny delningsmarkör kommer infogas</string>
<string name="pace_label">Tempo</string>
<!-- MyTracksSky -->
<string name="mytracks_sky">GPS-satelliter</string>
<!-- My Maps -->
<string name="select_map_message">Välj en karta från ditt konto:</string>
<string name="create_new_map">Skapa en ny karta...</string>
<string name="map_list_title">Skicka till Google Mina Kartor</string>
<string name="maps_list_loading">Laddar kartor...</string>
<string name="maps_list_failed">Dina kartor kunde inte laddas just nu!</string>
<string name="open_map">Välj den här kartan</string>
<string name="edit_map_details">Ändra kartdetaljer</string>
<string name="share_map">Dela kartan med vänner</string>
<string name="share_track">Dela spår med vänner</string>
<string name="delete_map">Ta bort kartan från ditt konto</string>
<string name="map_title">Karttitel:</string>
<string name="map_description">Kartbeskrivning:</string>
<string name="map_searchable_edit">Gör så att kartan och dess innehåll visas i sökresultat</string>
<string name="map_not_searchable_edit">Låt inte kartan och dess innehåll visas i sökresultat, men de kan ändå ses av andra genom en direktlänk</string>
<string name="map_searchable">Kartan och dess innehåll kommer att visas i sökresultat</string>
<string name="map_not_searchable">Varken kartan eller dess innehåll visas i sökresultat, men de kan ändå ses av andra genom en direktlänk</string>
<string name="close">Stäng</string>
<string name="edit">Ändra</string>
<string name="cancel">Avbryt</string>
<string name="save">Spara</string>
<!-- Sharing -->
<string name="share_map_subject">Jag vill dela en karta med dig</string>
<string name="share_map_body_format">Jag tror att du skulle vara intresserad av den här kartan:
%s</string>
<string name="send_track_subject">Jag vill dela ett spår med dig</string>
<string name="send_track_body_format">Jag tror att du skulle vara intresserad av det här spåret.
För att se det här spåret använd Google Earth eller något annat kartprogram.</string>
<!-- Visibility -->
<string name="public_map">Publik</string>
<string name="unlisted_map">Olistat</string>
<string name="mapslist_no_maps_available">Du har inga kartor än.</string>
<!-- Statistics View -->
<!-- Average Moving -->
<string name="average_moving_speed_label">Genomsnittlig rörlig hastighet</string>
<string name="average_moving_pace_label">Genomsnittlig rörligt tempo</string>
<!-- Average -->
<string name="average_speed_label">Genomsnittlig hastighet</string>
<string name="average_pace_label">Genomsnittligt tempo</string>
<string name="elevation_chart_name">Höjddiagram</string>
<string name="elevation_gain_label">Höjdvinst</string>
<string name="elevation_label">Höjd</string>
<string name="latitude_label">Latitud</string>
<string name="longitude_label">Longitud</string>
<!-- Min -->
<string name="min_elevation_label">Minimihöjd</string>
<string name="min_grade_label">Minimilutning</string>
<string name="min_pace_label">Minimitempo</string>
<!-- Max -->
<string name="max_elevation_label">Maxhöjd</string>
<string name="max_grade_label">Maxlutning</string>
<string name="max_speed_label">Maxhastighet</string>
<string name="loading_track">Laddar spår...</string>
<!-- Chart Settings -->
<string name="chart_settings">Diagraminställningar</string>
<!-- X Axis -->
<string name="x_axis">X-axel</string>
<string name="by_distance">Efter distans</string>
<string name="by_time">Efter tid</string>
<!-- Y Axis -->
<string name="y_axis">Y-axel</string>
<!-- Units -->
<!-- Time -->
<string name="hour">timme</string>
<string name="hours">timmar</string>
<string name="hr">tim</string>
<string name="min">min</string>
<string name="minute">minut</string>
<string name="minutes">minuter</string>
<string name="second">sekund</string>
<string name="seconds">sekunder</string>
<!-- Distance -->
<string name="meter">m</string>
<string name="kilometer">km</string>
<string name="kilometers_long">kilometer</string>
<string name="feet">ft</string>
<string name="mile">mi</string>
<string name="miles_long">miles</string>
<!-- Speed and Pace -->
<string name="kilometer_per_hour">km/h</string>
<string name="kilometer_per_hour_long">kilometer i timmen</string>
<string name="min_per_kilometer">min/km</string>
<string name="per_kilometer">per kilometer</string>
<string name="mile_per_hour">mi/h</string>
<string name="mile_per_hour_long">miles i timmen</string>
<string name="min_per_mile">min/mi</string>
<string name="per_mile">per mile</string>
<!-- Stats Labels -->
<string name="moving_time_label">Rörlig tid</string>
<string name="total_time_label">Total tid</string>
<string name="time_label">Tid</string>
<string name="sd_unavail">SD otillgängligt</string>
<string name="speed">Hastighet</string>
<string name="start_trip">Du måste starta en tur.</string>
<string name="statistics">Statistik</string>
<string name="total_distance_label">Total distans</string>
<string name="distance_label">Distans</string>
<string name="unknown">-</string>
<string name="waiting">Väntar...</string>
<!-- End User License Agreement -->
<string name="eula_title">Licensavtal för slutanvändare</string>
<string name="accept">Acceptera</string>
<string name="decline">Avböj</string>
<!-- About -->
<string name="about">Om</string>
<string name="help">Hjälp</string>
<string name="about_team">Team:</string>
<string name="about_version">Version:</string>
<string name="about_email">Support</string>
<string name="about_support">Support:</string>
<!-- Send to Google -->
<string name="sendtogoogle_title">Skicka till...</string>
<string name="sendtogoogle_google_mymaps">Google Mina Kartor</string>
<string name="sendtogoogle_google_docs">Google Dokument</string>
<string name="sendtogoogle_send_now">Skicka nu</string>
<string name="sendtogoogle_create_new_map">Skapa en ny karta</string>
<string name="sendtogoogle_pick_existing_map">Välj en befintlig karta</string>
<string name="sendtogoogle_send_stats">Skicka spårstatistik</string>
<string name="sendtogoogle_send_stats_and_points">Skicka statistik och punkter</string>
<!-- Getting the account to upload to. -->
<string name="no_account_found_title">Inget konto hittades</string>
<string name="no_account_found">Inget konto hittades att ladda upp spåret till.</string>
<string name="choose_account_title">Välj konto</string>
<!-- SD card -->
<string name="io_no_external_storage_found">Ingen extern lagring hittades. Se till att ett SD-kort sitter i och att telefonen inte är monterad till en dator.</string>
<string name="io_write_failed">Kan inte skriva till filen.</string>
<string name="io_create_dir_failed">Kunde inte skapa katalog på SD-kort.</string>
<string name="io_write_finished">Slutförde skrivningen av spåret till SD-kort.</string>
<!-- VersionChecker -->
<string name="version_check_title">Uppgraderingskoll</string>
<string name="version_check_upgrade_button">Uppgradera nu</string>
<string name="version_check_continue_button">Fortsätt utan att uppgradera</string>
<string name="upgrade_required">Din version av %1$s kanske inte fungerar korrekt längre, och stöds inte längre. Uppgradera för att fortsätta använda %1$s.</string>
<string name="upgrade_recommended">Din version av %1$s kanske inte fungerar korrekt längre. Du kan uppgradera den nu, eller fortsätta att använda den på egen risk. Vissa funktioner såsom synkronisering kanske inte fungerar förrän du uppgraderar.</string>
<string name="upgrade_available">En ny version av %1$s finns tillgänglig. Du kan fortsätta använda din nuvarande version utan problem, men du kommer inte få de senaste funktionerna förrän du uppgraderar.</string>
<!-- Welcome -->
<string name="welcome">Välkommen</string>
<string name="welcome_read_later">Ok</string>
<string name="welcome_about">Om My Tracks</string>
<!-- Announcement -->
<string name="announce_no_distance">My Tracks ingen distans</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 på %4$s i %5$s %6$s</string>
<!-- Check Units -->
<string name="check_units_title">Mätenheter</string>
<string name="check_units_message">Som standard är enheterna metriska. Vill du ändra den inställningen?</string>
<!-- Signal Strength -->
<string name="error_rate"> Felfrekvens: %1$d</string>
<string name="signal_to_noise_ratio"> Signal / brus-förhållande: %1$d</string>
<string name="ecio"> Ec/lo: %1$.1f dB</string>
<string name="gsm_strength">GSM-styrka: %1$d</string>
<string name="cdma_strength">CDMA-styrka: %1$d dBm</string>
<string name="evdo_strength">EVDO-styrka: %1$d dBm</string>
</resources>
+286
View File
@@ -0,0 +1,286 @@
<?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 米</item>
<item>2 米</item>
<item>3 米</item>
<item>4 米</item>
<item>5 米 (推荐)</item>
<item>10 米</item>
<item>100 米</item>
</string-array>
<string-array name="min_recording_distance_options_ft">
<item>3 英尺</item>
<item>6 英尺</item>
<item>10 英尺</item>
<item>13 英尺</item>
<item>16 英尺 (推荐)</item>
<item>33 英尺</item>
<item>328 英尺</item>
</string-array>
<!-- Maximum recording distance -->
<string-array name="max_recording_distance_options">
<item>50 米</item>
<item>100 米</item>
<item>200 米 (推荐)</item>
<item>500 米</item>
<item>1000 米</item>
<item>5000 米</item>
</string-array>
<string-array name="max_recording_distance_options_ft">
<item>164 英尺</item>
<item>328 英尺</item>
<item>656 英尺 (推荐)</item>
<item>1640 英尺</item>
<item>0.6 英里</item>
<item>3.1 英里</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 米 (良好的 GPS 信号)</item>
<item>20 米</item>
<item>50 米</item>
<item>100 米</item>
<item>200 米 (推荐)</item>
<item>500 米</item>
<item>1000 米</item>
<item>2000 米</item>
<item>5000 米 (糟糕的 GPS 信号)</item>
</string-array>
<string-array name="min_required_accuracy_options_ft">
<item>33 英尺 (良好的 GPS 信号)</item>
<item>66 英尺</item>
<item>165 英尺</item>
<item>328 英尺</item>
<item>656 英尺 (推荐)</item>
<item>1640 英尺</item>
<item>0.6 英里</item>
<item>1.2 英里</item>
<item>3.1 英里 (糟糕的 GPS 信号)</item>
</string-array>
<!-- Split frequency -->
<string-array name="split_frequency_options">
<item>不分割</item>
<item>100 千米</item>
<item>50 千米</item>
<item>25 千米</item>
<item>10 千米</item>
<item>5 千米</item>
<item>1 千米</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 英里</item>
<item>50 英里</item>
<item>25 英里</item>
<item>10 英里</item>
<item>5 英里</item>
<item>1 英里</item>
<item>60 分钟</item>
<item>30 分钟</item>
<item>15 分钟</item>
<item>10 分钟</item>
<item>5 分钟</item>
<item>2 分钟</item>
<item>1 分钟</item>
</string-array>
<!-- Auto-resume track timeout -->
<string-array name="auto_resume_track_timeout_options">
<item>从不</item>
<item>5 分钟</item>
<item>10 分钟</item>
<item>15 分钟</item>
<item>总是</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>提款机</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>
+486
View File
@@ -0,0 +1,486 @@
<?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>
<string name="aggregated_stats">累积统计</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 My Map 时发生错误. 请稍后重试.</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">文件太大 (必须小于 500 KB). 中止操作.</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">已在 maps.google.com 创建新 Google My Map.</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">请点击这里进入\'安全和位置设置\'并勾选上\'启用 GPS 卫星\'. 然后用后退键返回.</string>
<string name="status_approximate_location">注意: 当前显示的位置是根据无线信号和无线网络计算的近似位置.</string>
<string name="status_no_location">目前无法定位. 请启用更多的位置来源 (在\'安全和位置设置\'中) 并确认天空是可见的. 再重试.</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 Maps 中...</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 Maps 中...</string>
<string name="progress_message_creating_map">创建新的 My 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">(第 %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">确定</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 My Map</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">保存为 CSV 文件</string>
<string name="tracklist_save_as_tcx">保存为 TCX 文件</string>
<string name="tracklist_btn_send_all_to_mymaps">发送到 Google My Maps</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 My Map</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_share_tcx_file">TCX 文件</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_not_available_summary">你使用的 Android 版本无法支持</string>
<string name="settings_announcement_frequency">提示频度</string>
<string name="settings_announcement_frequency_summary">语音提示的最小时间间隔</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="settings_auto_resume_track_timeout">自动恢复追踪等待时间</string>
<string name="settings_auto_resume_track_timeout_summary">手机重启后自动继续上次轨迹记录的最大间隔时间</string>
<string name="pace_label">节奏</string>
<string name="eclair_required">只在 Android 2.0 及以上版本支持</string>
<string name="settings_sensor_type">感应器类型</string>
<string name="settings_sensor_type_summary">要连接到的感应器类型</string>
<string name="settings_sensor_id">感应器ID</string>
<string name="settings_sensor_id_summary">发送到感应器的ID.</string>
<string name="sensor_id_malformed">ID必须是一个整数值.</string>
<string name="bluetooth_sensor_title">蓝牙感应器</string>
<string name="bluetooth_sensor_summary">要连接到的蓝牙感应器</string>
<string name="settings_backup">备份 (测试版)</string>
<string name="settings_sd_backup">SD 存储卡备份</string>
<string name="settings_backup_to_sd">立即备份</string>
<string name="settings_backup_to_sd_summary">将你所有轨迹和设置的完整备份写入到 SD 存储卡</string>
<string name="settings_restore_from_sd">立即恢复</string>
<string name="settings_restore_from_sd_summary">从 SD 存储卡恢复所有轨迹和设置</string>
<string name="settings_no_backup_while_recording">在记录轨迹时无法处理备份数据.</string>
<!-- Backups -->
<string name="restore_overwrites_warning">从 SD 存储卡恢复备份数据将覆盖掉你当前的所有轨迹和设置. 确定要继续吗?</string>
<string name="select_backup_to_restore">请选择要恢复的备份文件</string>
<string name="no_backups">没有找到备份文件.</string>
<string name="backup_write_progress_message">将所有轨迹和设置写入 SD 存储卡中. 这需要一些时间...</string>
<string name="backup_import_progress_message">从 SD 存储卡读取轨迹和设置中. 这需要一些时间...</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 My Maps</string>
<string name="maps_list_loading">载入地图中...</string>
<string name="maps_list_failed">现在无法载入你的地图!</string>
<string name="open_map">选择此地图</string>
<string name="edit_map_details">编辑地图详细信息</string>
<string name="share_map">与朋友分享地图</string>
<string name="share_track">与朋友分享轨迹</string>
<string name="delete_map">删除你帐号下的此地图</string>
<string name="map_title">地图标题:</string>
<string name="map_description">地图描述:</string>
<string name="map_searchable_edit">允许此地图及其内容出现在搜索结果中</string>
<string name="map_not_searchable_edit">不允许地图及其内容出现在搜索结果中, 但它的内容依然可以通过网址直接访问</string>
<string name="map_searchable">此地图及其内容会出现在搜索结果中</string>
<string name="map_not_searchable">此地图及其内容不会出现在搜索结果中, 但它的内容依然可以通过网址直接访问</string>
<string name="close">关闭</string>
<string name="edit">编辑</string>
<string name="cancel">取消</string>
<string name="save">保存</string>
<!-- Sharing -->
<string name="share_map_subject">我想与你分享一张地图</string>
<string name="share_map_body_format">我想你可能会对这个地图感兴趣:
%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">横轴</string>
<string name="by_distance">按距离显示</string>
<string name="by_time">按时间显示</string>
<!-- Y Axis -->
<string name="y_axis">纵轴</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="kilometers_long">千米</string>
<string name="miles_long">英里</string>
<!-- Speed and Pace -->
<string name="kilometer_per_hour_long">千米每小时</string>
<string name="per_kilometer">每千米</string>
<string name="mile_per_hour_long">英里每小时</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="power">电源</string>
<string name="cadence">节奏</string>
<string name="heart_rate">心率</string>
<string name="max_heart_rate">最大心率</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_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 卡并且你的手机没有将挂载 SD 卡到电脑.</string>
<string name="io_write_failed">无法写入文件.</string>
<string name="io_create_dir_failed">无法在 SD 存储卡上创建目录.</string>
<string name="io_write_finished">轨迹已导出到 SD 卡.</string>
<string name="io_read_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 版本可能无法继续正常工作, 技术支持也将结束. 请升级以便继续使用 %1$s.</string>
<string name="upgrade_recommended">你所使用的 %1$s 版本可能无法继续正常工作. 你可以现在就更新它, 或者自负风险继续使用. 一些如服务器同步之类的功能在更新前可能不能使用.</string>
<string name="upgrade_available">%1$s 的新版本已经发布. 你可以继续使用现在的版本, 但在更新前无法使用到最新的功能.</string>
<!-- Welcome -->
<string name="welcome">欢迎</string>
<string name="welcome_read_later">确定</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"> 信噪比: %1$d</string>
<string name="ecio"> Ec/lo: %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>
<!-- Fake preference keys - never saved, but show options in the preferences screen -->
<string name="method_bluetooth">蓝牙</string>
<string name="bluetooth_options">蓝牙设置</string>
<string name="bluetooth_pairing">设备关联</string>
<string name="bluetooth_pairing_summary">打开 Android 的蓝牙设置关联设备</string>
<string name="sensor_options">感应器选项</string>
<!-- Bluetooth Sensor -->
<string name="bluetooth_connected">连接到 %s</string>
<string name="bluetooth_connecting">蓝牙感应器连接中 ...</string>
<string name="bluetooth_not_connected">未连接蓝牙感应器</string>
<!-- Sensor State -->
<string name="sensor_data_time">感应器数据时间</string>
<string name="power_meter_enabled">电源计量器已启用</string>
<string name="heart_rate_monitor_enabled">心率监视器已启用</string>
</resources>
+19
View File
@@ -236,6 +236,24 @@ limitations under the License.
<item>1</item>
</string-array>
<!-- Auto-resume track timeout -->
<string-array name="auto_resume_track_timeout_options">
<item>Never</item>
<item>5 min</item>
<item>10 min</item>
<item>15 min</item>
<item>Always</item>
</string-array>
<!-- DO NOT TRANSLATE -->
<string-array name="auto_resume_track_timeout_values">
<item>0</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>-1</item>
</string-array>
<!-- Other non-preference arrays -->
<array name="activity_types">
@@ -370,5 +388,6 @@ limitations under the License.
<item>GPX</item>
<item>KML</item>
<item>CSV</item>
<item>TCX</item>
</array>
</resources>
+51
View File
@@ -0,0 +1,51 @@
<?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>
<!-- Keys for application preferences - DO NOT TRANSLATE -->
<string name="announcement_frequency_key">announcementFrequency</string>
<string name="auto_resume_track_timeout_key">autoResumeTrackTimeout</string>
<string name="bluetooth_device_key">bluetoothDevice</string>
<string name="bluetooth_pairing_key">bluetoothPairing</string>
<string name="bluetooth_sensor_key">bluetoothSensor</string>
<string name="default_map_public_key">defaultMapPublic</string>
<string name="max_recording_distance_key">maxRecordingDistance</string>
<string name="metric_units_key">metricUnits</string>
<string name="min_recording_distance_key">minRecordingDistance</string>
<string name="min_recording_interval_key">minRecordingInterval</string>
<string name="min_required_accuracy_key">minRequiredAccuracy</string>
<string name="modular_stats_display_key">modularStatsDisplay</string>
<string name="pick_existing_map_key">pickExistingMap</string>
<string name="report_speed_key">reportSpeed</string>
<string name="send_stats_and_points_key">sendStatsAndPoints</string>
<string name="send_to_docs_key">sendToDocs</string>
<string name="send_to_my_maps_key">sendToMyMaps</string>
<string name="sensor_id_key">sensorId</string>
<string name="sensor_type_key">sensorType</string>
<string name="share_url_only_key">shareUrlOnly</string>
<string name="signal_sampling_frequency_key">signalSamplingFrequency</string>
<string name="split_frequency_key">splitFrequency</string>
<string name="timestamp_track_name_key">timestampTrackName</string>
<!-- Preference keys which keep transient state (not user preferences) - DO NOT TRANSLATE -->
<string name="auto_resume_track_current_retry_key">autoResumeTrackCurrentRetry</string>
<string name="recording_track_key">recordingTrack</string>
<string name="selected_track_key">selectedTrack</string>
<!-- Keys for non-persisted preferences - DO NOT TRANSLATE -->
<string name="backup_to_sd_key">backupToSd</string>
<string name="restore_from_sd_key">restoreFromSd</string>
</resources>
+29
View File
@@ -0,0 +1,29 @@
<?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>
<array name="left_icons">
<item>@drawable/left_arrow_chart_stateful</item>
<item>@drawable/left_arrow_track_stateful</item>
<item>@drawable/left_arrow_stats_stateful</item>
</array>
<array name="right_icons">
<item>@drawable/right_arrow_stats_stateful</item>
<item>@drawable/right_arrow_chart_stateful</item>
<item>@drawable/right_arrow_track_stateful</item>
</array>
</resources>
+81 -5
View File
@@ -32,6 +32,7 @@ limitations under the License.
<string name="choose_track_message">Please select the track to view</string>
<string name="current_track">Current track</string>
<string name="current_segment">Last segment</string>
<string name="aggregated_stats">Aggregated Statistics</string>
<!-- Error messages -->
<string name="error_no_location_manager">No location manager was found on this device!</string>
@@ -122,6 +123,7 @@ limitations under the License.
<string name="tracklist_save_as_gpx">Save a GPX file</string>
<string name="tracklist_save_as_kml">Save a KML file</string>
<string name="tracklist_save_as_csv">Save a CSV file</string>
<string name="tracklist_save_as_tcx">Save a TCX file</string>
<string name="tracklist_btn_send_all_to_mymaps">Send to Google My Maps</string>
<string name="tracklist_btn_delete_all">Delete all tracks</string>
<!-- Export All -->
@@ -135,6 +137,7 @@ limitations under the License.
<string name="tracklist_share_gpx_file">A GPX file</string>
<string name="tracklist_share_kml_file">A KML file</string>
<string name="tracklist_share_csv_file">A CSV file</string>
<string name="tracklist_share_tcx_file">A TCX file</string>
<string name="tracklist_clear_map">Clear map</string>
<!-- Waypoints list -->
@@ -165,9 +168,10 @@ limitations under the License.
<string name="settings_recording">Recording</string>
<string name="settings_sharing">Sharing</string>
<string name="settings_not_available_summary">Not available in your version of Android</string>
<string name="settings_announcement_frequency">Announcement frequency</string>
<string name="settings_announcement_frequency_summary">Minimum time between two voice announcements</string>
<string name="settings_announcement_not_available_summary">Text-to-speech announcements are not available in your version of Android</string>
<string name="settings_min_recording_distance">Min distance between points</string>
<string name="settings_min_recording_distance_summary">Smallest distance between two recordings of your position</string>
@@ -197,13 +201,48 @@ limitations under the License.
<string name="settings_share_url_only_summary_on">Only the URL of the map will be sent when sharing</string>
<string name="settings_share_url_only_summary_off">The contents of the track will also be included when sharing</string>
<string name="settings_signal_sampling_frequency">Signal Sampling frequency</string>
<string name="settings_signal_sampling_frequency">Signal sampling frequency</string>
<string name="settings_signal_sampling_frequency_summary">Time between two recordings of the phone\'s signal strength</string>
<string name="settings_split_frequency">Split Frequency</string>
<string name="settings_split_frequency">Split frequency</string>
<string name="settings_split_frequency_summary">Maximum time or distance after which a new split marker will be inserted</string>
<string name="settings_auto_resume_track_timeout">Auto-resume track timeout</string>
<string name="settings_auto_resume_track_timeout_summary">Maximum time in minutes after which a previously recording track will not be resumed on phone reboot</string>
<string name="settings_timestamp_track_name">Timestamp as track name</string>
<string name="settings_timestamp_track_name_summary_on">The default track name will be the date and time at the start of the track</string>
<string name="settings_timestamp_track_name_summary_off">Track names will have numbers which increment for each new track.</string>
<string name="pace_label">Pace</string>
<string name="eclair_required">Only supported in Android 2.0 or above</string>
<string name="settings_sensor_type">Sensor Type</string>
<string name="settings_sensor_type_summary">The type of sensor to connect to.</string>
<string name="settings_sensor_id">Sensor Id</string>
<string name="settings_sensor_id_summary">The Id sent to sensors.</string>
<string name="sensor_id_malformed">The Id must be an integer value.</string>
<string name="bluetooth_sensor_title">Bluetooth sensor</string>
<string name="bluetooth_sensor_summary">The bluetooth sensor to connect to.</string>
<string name="settings_modular_stats_display_title">Modular Stats Display</string>
<string name="settings_modular_stats_display_summary_on">The stats screen is split in various modes.</string>
<string name="settings_modular_stats_display_summary_off">Display all stats on one screen</string>
<string name="settings_backup">Backup (BETA)</string>
<string name="settings_sd_backup">SD Card Backup</string>
<string name="settings_backup_to_sd">Backup now</string>
<string name="settings_backup_to_sd_summary">Write a full copy of all your tracks and settings to the SD card</string>
<string name="settings_restore_from_sd">Restore now</string>
<string name="settings_restore_from_sd_summary">Restore all tracks and settings from the SD card</string>
<string name="settings_no_backup_while_recording">Cannot manipulate backups while recording a track.</string>
<!-- Backups -->
<string name="restore_overwrites_warning">Restoring a backup from the SD card will OVERWRITE all your current tracks and settings. Are you sure?</string>
<string name="select_backup_to_restore">Select the backup to restore</string>
<string name="no_backups">No backups found.</string>
<string name="backup_write_progress_message">Writing all tracks and settings to the SD card. This may take a while...</string>
<string name="backup_import_progress_message">Reading all tracks and settings from the SD card. This may take a while...</string>
<!-- MyTracksSky -->
<string name="mytracks_sky">GPS Satellites</string>
@@ -291,6 +330,7 @@ limitations under the License.
<string name="minutes">minutes</string>
<string name="second">second</string>
<string name="seconds">seconds</string>
<string name="milliseconds">ms</string>
<!-- Distance -->
<string name="meter">m</string>
@@ -310,6 +350,12 @@ limitations under the License.
<string name="min_per_mile">min/mi</string>
<string name="per_mile">per mile</string>
<!-- Power -->
<string name="watts">W</string>
<string name="revolutions_per_minute">rpm</string>
<string name="bpm">bpm</string>
<!-- Stats Labels -->
<string name="moving_time_label">Moving Time</string>
<string name="total_time_label">Total Time</string>
@@ -320,6 +366,10 @@ limitations under the License.
<string name="statistics">Statistics</string>
<string name="total_distance_label">Total Distance</string>
<string name="distance_label">Distance</string>
<string name="power">Power</string>
<string name="cadence">Cadence</string>
<string name="heart_rate">Heart Rate</string>
<string name="max_heart_rate">Max Heart Rate</string>
<string name="unknown">-</string>
<string name="waiting">Waiting...</string>
@@ -337,7 +387,6 @@ limitations under the License.
<string name="about_support">Support:</string>
<!-- DO NOT TRANSLATE -->
<string name="version">1.0.14</string>
<string name="about_googlegroups">mytracks@googlegroups.com</string>
<string name="about_weburl">mytracks.appspot.com</string>
<string name="about_copyright_message">Copyright 2009 Google Inc.</string>
@@ -364,8 +413,10 @@ limitations under the License.
<!-- SD card -->
<string name="io_no_external_storage_found">No external storage was found. Make sure that a SD card is inserted and that your phone is not mounted to a computer.</string>
<string name="io_write_failed">Unable to write to file.</string>
<string name="io_read_failed">Unable to read from file.</string>
<string name="io_create_dir_failed">Unable to create directory on SD card.</string>
<string name="io_write_finished">Finished writing the track to SD card.</string>
<string name="io_write_finished">Finished writing to SD card.</string>
<string name="io_read_finished">Finished reading from SD card.</string>
<!-- VersionChecker -->
<string name="version_check_title">Upgrade Check</string>
@@ -403,4 +454,29 @@ limitations under the License.
<string name="gsm_strength">GSM Strength: %1$d</string>
<string name="cdma_strength">CDMA Strength: %1$d dBm</string>
<string name="evdo_strength">EVDO Strength: %1$d dBm</string>
<!-- Fake preference keys - never saved, but show options in the preferences screen -->
<string name="method_bluetooth">Bluetooth</string>
<string name="bluetooth_options">Bluetooth options</string>
<string name="bluetooth_pairing">Device pairing</string>
<string name="bluetooth_pairing_summary">Open Android\'s bluetooth settings to pair a device</string>
<string name="sensor_bluetooth_options_key">bluetoothSensorsEnabled</string>
<string name="sensor_options">Sensor Options</string>
<!-- Sensor Types => DO NOT TRANSLATE -->
<string name="none">NONE</string>
<string name="ant_sensor_type">ANT</string>
<string name="srm_sensor_type">SRM</string>
<string name="athlosoft_sensor_type">ATHLOSOFT</string>
<!-- Bluetooth Sensor -->
<string name="bluetooth_connected">Connected to %s</string>
<string name="bluetooth_connecting">Bluetooth sensor connecting ...</string>
<string name="bluetooth_not_connected">Bluetooth sensor not connected</string>
<!-- Sensor State -->
<string name="sensor_data_time">Sensor Data Time</string>
<string name="power_meter_enabled">Power meter enabled</string>
<string name="heart_rate_monitor_enabled">Heart rate monitor enabled</string>
</resources>
+49 -17
View File
@@ -18,26 +18,26 @@ limitations under the License.
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Display settings -->
<PreferenceCategory android:title="@string/settings_display">
<PreferenceScreen android:title="@string/settings_display">
<CheckBoxPreference
android:key="metricUnits"
android:key="@string/metric_units_key"
android:title="@string/settings_metric_units"
android:summaryOn="@string/settings_metric_units_summary_on"
android:summaryOff="@string/settings_metric_units_summary_off"
android:defaultValue="true" />
<CheckBoxPreference
android:key="reportSpeed"
android:key="@string/report_speed_key"
android:title="@string/settings_report_speed_or_pace"
android:summaryOn="@string/settings_report_speed_or_pace_summary_on"
android:summaryOff="@string/settings_report_speed_or_pace_summary_off"
android:defaultValue="true" />
</PreferenceCategory>
</PreferenceScreen>
<!-- Recording settings -->
<PreferenceCategory android:title="@string/settings_recording">
<PreferenceScreen android:title="@string/settings_recording">
<com.google.android.apps.mytracks.IntegerListPreference
android:key="announcementFrequency"
android:key="@string/announcement_frequency_key"
android:title="@string/settings_announcement_frequency"
android:summary="@string/settings_announcement_frequency_summary"
android:entries="@array/task_frequency_options"
@@ -45,7 +45,7 @@ limitations under the License.
android:defaultValue="-1" />
<com.google.android.apps.mytracks.IntegerListPreference
android:key="splitFrequency"
android:key="@string/split_frequency_key"
android:title="@string/settings_split_frequency"
android:summary="@string/settings_split_frequency_summary"
android:entries="@array/split_frequency_options"
@@ -53,7 +53,7 @@ limitations under the License.
android:defaultValue="0" />
<com.google.android.apps.mytracks.IntegerListPreference
android:key="minRecordingInterval"
android:key="@string/min_recording_interval_key"
android:title="@string/settings_min_recording_interval"
android:summary="@string/settings_min_recording_interval_summary"
android:entries="@array/min_recording_interval_options"
@@ -61,7 +61,7 @@ limitations under the License.
android:defaultValue="0" />
<com.google.android.apps.mytracks.IntegerListPreference
android:key="minRecordingDistance"
android:key="@string/min_recording_distance_key"
android:title="@string/settings_min_recording_distance"
android:summary="@string/settings_min_recording_distance_summary"
android:entries="@array/min_recording_distance_options"
@@ -69,7 +69,7 @@ limitations under the License.
android:defaultValue="5" />
<com.google.android.apps.mytracks.IntegerListPreference
android:key="maxRecordingDistance"
android:key="@string/max_recording_distance_key"
android:title="@string/settings_max_recording_distance"
android:summary="@string/settings_max_recording_distance_summary"
android:entries="@array/max_recording_distance_options"
@@ -77,7 +77,7 @@ limitations under the License.
android:defaultValue="200" />
<com.google.android.apps.mytracks.IntegerListPreference
android:key="minRequiredAccuracy"
android:key="@string/min_required_accuracy_key"
android:title="@string/settings_min_required_accuracy"
android:summary="@string/settings_min_required_accuracy_summary"
android:entries="@array/min_required_accuracy_options"
@@ -85,28 +85,60 @@ limitations under the License.
android:defaultValue="200" />
<com.google.android.apps.mytracks.IntegerListPreference
android:key="signalSamplingFrequency"
android:key="@string/signal_sampling_frequency_key"
android:title="@string/settings_signal_sampling_frequency"
android:summary="@string/settings_signal_sampling_frequency_summary"
android:entries="@array/task_frequency_options"
android:entryValues="@array/task_frequency_values"
android:defaultValue="-1" />
</PreferenceCategory>
<com.google.android.apps.mytracks.IntegerListPreference
android:key="@string/auto_resume_track_timeout_key"
android:title="@string/settings_auto_resume_track_timeout"
android:summary="@string/settings_auto_resume_track_timeout_summary"
android:entries="@array/auto_resume_track_timeout_options"
android:entryValues="@array/auto_resume_track_timeout_values"
android:defaultValue="10" />
<CheckBoxPreference
android:key="@string/timestamp_track_name_key"
android:title="@string/settings_timestamp_track_name"
android:summaryOn="@string/settings_timestamp_track_name_summary_on"
android:summaryOff="@string/settings_timestamp_track_name_summary_off"
android:defaultValue="true" />
</PreferenceScreen>
<!-- Sharing settings -->
<PreferenceCategory android:title="@string/settings_sharing">
<PreferenceScreen android:title="@string/settings_sharing">
<CheckBoxPreference
android:key="defaultMapPublic"
android:key="@string/default_map_public_key"
android:title="@string/settings_map_public"
android:summaryOn="@string/settings_map_public_summary_on"
android:summaryOff="@string/settings_map_public_summary_off"
android:defaultValue="true" />
<CheckBoxPreference
android:key="shareUrlOnly"
android:key="@string/share_url_only_key"
android:title="@string/settings_share_url_only"
android:summaryOn="@string/settings_share_url_only_summary_on"
android:summaryOff="@string/settings_share_url_only_summary_off"
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>
<!-- Backup settings -->
<PreferenceScreen android:title="@string/settings_backup">
<PreferenceCategory android:title="@string/settings_sd_backup">
<Preference
android:key="@string/backup_to_sd_key"
android:title="@string/settings_backup_to_sd"
android:summary="@string/settings_backup_to_sd_summary"
android:persistent="false" />
<Preference
android:key="@string/restore_from_sd_key"
android:title="@string/settings_restore_from_sd"
android:summary="@string/settings_restore_from_sd_summary"
android:persistent="false" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceScreen>
@@ -25,5 +25,8 @@ public class MyMapsConstants {
"http://maps.google.com/maps/ms";
public static final String MAPSHOP_SERVICE = MapsClient.SERVICE;
/**
* Private constructor to prevent instantiation.
*/
private MyMapsConstants() { }
}
@@ -114,7 +114,7 @@ public class MyMapsGDataWrapper {
} catch (HttpException e) {
Log.e(MyTracksConstants.TAG, "HttpException", e);
errorMessage = e.getMessage();
if (errorMessage.contains("401")) {
if (errorMessage != null && errorMessage.contains("401")) {
errorType = ERROR_AUTH;
} else {
errorType = ERROR_CONNECTION;
@@ -122,7 +122,7 @@ public class MyMapsGDataWrapper {
} catch (IOException e) {
Log.e(MyTracksConstants.TAG, "Exception", e);
errorMessage = e.getMessage();
if (errorMessage.contains("503")) {
if (errorMessage != null && errorMessage.contains("503")) {
errorType = ERROR_INTERNAL;
} else {
errorType = ERROR_CONNECTION;
@@ -1,184 +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.apps.mymaps.MyMapsGDataWrapper.QueryFunction;
import com.google.android.apps.mytracks.MyTracksConstants;
import com.google.android.maps.mytracks.R;
import com.google.wireless.gdata.GDataException;
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.GDataParser;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.TabActivity;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import java.io.IOException;
public class VersionChecker {
/** The User id for the account storing MyTracks versions. */
private static final String VERSION_USER_ID = "116927647071651051525";
private static final String VERSION_CLIENT = "AndroidMyTracks";
private static final String VERSION_STATUS_LATEST = "latest";
private static final String VERSION_STATUS_OKAY = "okay";
private static final String VERSION_STATUS_OLD = "old";
private static final String VERSION_STATUS_BAD = "bad";
private final TabActivity parentActivity;
private Dialog dialog;
private boolean canceledAvailable;
private boolean canceledRecommended;
public VersionChecker(TabActivity parent) {
parentActivity = parent;
canceledAvailable = false;
canceledRecommended = false;
startMapsQueryThread();
}
private void startMapsQueryThread() {
new Thread() {
@Override
public void run() {
MyMapsGDataWrapper mapsGDataWrapper =
new MyMapsGDataWrapper(parentActivity);
mapsGDataWrapper.runQuery(new QueryFunction() {
@Override
public void query(MapsClient client) throws IOException, Exception {
doMapsQuery(client);
}
});
mapsGDataWrapper.cleanUp();
}
}.start();
}
private void doMapsQuery(MapsClient client)
throws GDataException, IOException, HttpException {
final boolean newVersion; // Returned new version available
final boolean newVersionRecommended; // Returned new version recommended
final boolean newVersionRequired; // Returned new version required
String currentVersion = parentActivity.getString(R.string.version);
GDataParser parser = client.getParserForFeed(
Entry.class,
MapsClient.getVersionFeed(
VERSION_USER_ID,
VERSION_CLIENT,
currentVersion),
"");
Entry en = parser.parseStandaloneEntry();
if (currentVersion.equals(en.getTitle())) {
if (VERSION_STATUS_LATEST.equals(en.getContent())) {
newVersion = false;
newVersionRecommended = false;
newVersionRequired = false;
} else if (VERSION_STATUS_OKAY.equals(en.getContent())) {
newVersion = true;
newVersionRecommended = false;
newVersionRequired = false;
} else if (VERSION_STATUS_OLD.equals(en.getContent())) {
newVersion = true;
newVersionRecommended = true;
newVersionRequired = false;
} else if (VERSION_STATUS_BAD.equals(en.getContent())) {
newVersion = true;
newVersionRecommended = true;
newVersionRequired = true;
} else {
Log.e(MyTracksConstants.TAG,
"Got unknown version status: " + en.getContent());
return;
}
} else {
Log.e(MyTracksConstants.TAG,
"Current version '" + currentVersion + "' doesn't match entry: "
+ en.getTitle());
return;
}
if (!newVersion) {
Log.d(MyTracksConstants.TAG, "No new version available.");
return;
}
final AlertDialog.Builder dialogBuilder =
new AlertDialog.Builder(parentActivity);
dialogBuilder.setTitle(R.string.version_check_title);
dialogBuilder.setPositiveButton(R.string.version_check_upgrade_button,
new OnClickListener() {
public void onClick(DialogInterface di, int number) {
Intent upgradeIntent = new Intent();
upgradeIntent.setAction(Intent.ACTION_VIEW);
upgradeIntent.setData(Uri.parse(String.format("market://search?q=%s",
parentActivity.getResources().getString(
R.string.app_name).toString())));
parentActivity.startActivity(upgradeIntent);
parentActivity.finish();
}
});
if (newVersionRequired) {
dialogBuilder.setCancelable(false);
} else {
final boolean recommended = newVersionRecommended;
dialogBuilder.setNegativeButton(R.string.version_check_continue_button,
new OnClickListener() {
public void onClick(DialogInterface di, int number) {
dialog.dismiss();
canceledAvailable = true;
if (recommended) {
canceledRecommended = true;
}
}
});
}
int messageResource;
if (newVersionRequired) {
messageResource = R.string.upgrade_required;
} else if (newVersionRecommended) {
messageResource = R.string.upgrade_recommended;
if (canceledRecommended) {
return;
}
} else {
messageResource = R.string.upgrade_available;
if (canceledAvailable) {
return;
}
}
String message = parentActivity.getResources().getString(messageResource);
dialogBuilder.setMessage(String.format(message,
parentActivity.getResources().getString(R.string.app_name)));
parentActivity.getTabHost().post(new Runnable() {
public void run() {
dialog = dialogBuilder.create();
dialog.show();
}
});
}
}
@@ -0,0 +1,115 @@
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Window;
import android.widget.ScrollView;
import android.widget.TextView;
import java.util.List;
/**
* Activity for viewing the combined statistics for all the recorded tracks.
*
* Other features to add - menu items to change setings.
*
* @author Fergus Nelson
*/
public class AggregatedStatsActivity extends Activity implements
OnSharedPreferenceChangeListener {
private final StatsUtilities utils;
private MyTracksProviderUtils tracksProvider;
private boolean metricUnits = true;
public AggregatedStatsActivity() {
this.utils = new StatsUtilities(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Log.d(MyTracksConstants.TAG, "StatsActivity: onSharedPreferences changed "
+ key);
if (key != null) {
if (key.equals(R.string.metric_units_key)) {
metricUnits = sharedPreferences.getBoolean(
getString(R.string.metric_units_key), true);
utils.setMetricUnits(metricUnits);
utils.updateUnits();
loadAggregatedStats();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.tracksProvider = MyTracksProviderUtils.Factory.get(this);
// We don't need a window title bar:
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.stats);
ScrollView sv = ((ScrollView) findViewById(R.id.scrolly));
sv.setScrollBarStyle(ScrollView.SCROLLBARS_OUTSIDE_INSET);
SharedPreferences preferences = getSharedPreferences(
MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), true);
preferences.registerOnSharedPreferenceChangeListener(this);
}
utils.setMetricUnits(metricUnits);
utils.updateUnits();
utils.setSpeedLabel(R.id.speed_label, R.string.speed, R.string.pace_label);
utils.setSpeedLabels();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
if (metrics.heightPixels > 600) {
((TextView) findViewById(R.id.speed_register)).setTextSize(80.0f);
}
loadAggregatedStats();
}
/**
* 1. Reads tracks from the db
* 2. Merges the trip stats from the tracks
* 3. Updates the view
*/
private void loadAggregatedStats() {
List<Track> tracks = retrieveTracks();
TripStatistics rollingStats = null;
if (!tracks.isEmpty()) {
rollingStats = new TripStatistics(tracks.iterator().next()
.getStatistics());
for (int i = 1; i < tracks.size(); i++) {
rollingStats.merge(tracks.get(i).getStatistics());
}
}
updateView(rollingStats);
}
private List<Track> retrieveTracks() {
return tracksProvider.getAllTracks();
}
private void updateView(TripStatistics aggStats) {
if (aggStats != null) {
utils.setAllStats(aggStats);
}
}
}
@@ -1,12 +1,12 @@
/*
* 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
@@ -21,8 +21,10 @@ import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackPointsColumns;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.stats.DoubleBuffer;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.apps.mytracks.util.MyTracksUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
@@ -49,7 +51,7 @@ import java.util.ArrayList;
/**
* An activity that displays a chart from the track point provider.
*
*
* @author Sandor Dornbush
*/
public class ChartActivity extends Activity implements
@@ -175,11 +177,15 @@ public class ChartActivity extends Activity implements
final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
ArrayList<double[]> data = new ArrayList<double[]>();
// Need two locations so we can keep track of the last location.
Location location = new Location("");
do {
lastSeenLocationId = cursor.getLong(idColumnIdx);
Location location = providerUtils.createLocation(cursor);
if (location != null && MyTracksUtils.isValidLocation(location)) {
data.add(getDataPoint(location, track));
providerUtils.fillLocation(cursor, location);
if (MyTracksUtils.isValidLocation(location)) {
double[] point = new double[3];
location = getDataPoint(location, track, point);
data.add(point);
}
} while (cursor.moveToPrevious());
cv.addDataPoints(data);
@@ -204,23 +210,27 @@ public class ChartActivity extends Activity implements
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key != null) {
if (key.equals(MyTracksSettings.SELECTED_TRACK)) {
if (key.equals(getString(R.string.selected_track_key))) {
selectedTrackId =
sharedPreferences.getLong(MyTracksSettings.SELECTED_TRACK, -1);
sharedPreferences.getLong(getString(R.string.selected_track_key),
-1);
readProfileAsync();
} else if (key.equals(MyTracksSettings.METRIC_UNITS)) {
} else if (key.equals(getString(R.string.metric_units_key))) {
metricUnits =
sharedPreferences.getBoolean(MyTracksSettings.METRIC_UNITS, true);
sharedPreferences.getBoolean(getString(R.string.metric_units_key),
true);
cv.setMetricUnits(metricUnits);
readProfileAsync();
} else if (key.equals(MyTracksSettings.REPORT_SPEED)) {
} else if (key.equals(getString(R.string.report_speed_key))) {
reportSpeed =
sharedPreferences.getBoolean(MyTracksSettings.REPORT_SPEED, true);
sharedPreferences.getBoolean(getString(R.string.report_speed_key),
true);
cv.setReportSpeed(reportSpeed, this);
readProfileAsync();
} else if (key.equals(MyTracksSettings.RECORDING_TRACK)) {
} else if (key.equals(getString(R.string.recording_track_key))) {
recordingTrackId =
sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
sharedPreferences.getLong(getString(R.string.recording_track_key),
-1);
runOnUiThread(updateChart);
}
}
@@ -233,6 +243,11 @@ public class ChartActivity extends Activity implements
MyTracks.getInstance().setChartActivity(this);
providerUtils = MyTracksProviderUtils.Factory.get(this);
// The volume we want to control is the Text-To-Speech volume
int volumeStream =
new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream();
setVolumeControlStream(volumeStream);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mytracks_elevation);
ViewGroup layout = (ViewGroup) findViewById(R.id.elevation_chart);
@@ -246,12 +261,14 @@ public class ChartActivity extends Activity implements
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
selectedTrackId =
preferences.getLong(MyTracksSettings.SELECTED_TRACK, -1);
preferences.getLong(getString(R.string.selected_track_key), -1);
recordingTrackId =
preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true);
preferences.getLong(getString(R.string.recording_track_key), -1);
metricUnits = preferences.getBoolean(getString(R.string.metric_units_key),
true);
cv.setMetricUnits(metricUnits);
reportSpeed = preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true);
reportSpeed = preferences.getBoolean(getString(R.string.report_speed_key),
true);
cv.setReportSpeed(reportSpeed, this);
preferences.registerOnSharedPreferenceChangeListener(this);
}
@@ -281,13 +298,13 @@ public class ChartActivity extends Activity implements
public void onChange(boolean selfChange) {
Log.d(MyTracksConstants.TAG, "ChartActivity: ContentObserver.onChange");
// Check for any new locations and append them to the currently
// recording track:
if (!MyTracks.getInstance().isRecording()) {
// recording track.
if (recordingTrackId < 0) {
// No track is being recorded. We should not be here.
return;
}
if (selectedTrackId != recordingTrackId) {
// no track, or one other than the recording track is selected, don't
// No track, or one other than the recording track is selected, don't
// bother.
return;
}
@@ -438,7 +455,8 @@ public class ChartActivity extends Activity implements
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MyTracksConstants.MENU_CHART_SETTINGS:
MyTracks.getInstance().showDialogSafely(MyTracks.DIALOG_CHART_SETTINGS);
MyTracks.getInstance().getDialogManager().showDialogSafely(
DialogManager.DIALOG_CHART_SETTINGS);
return true;
}
return super.onOptionsItemSelected(item);
@@ -450,12 +468,15 @@ public class ChartActivity extends Activity implements
* data[0] = the time or distance
* data[1] = the elevation
* data[2] = the speed
*
* @param location a location
* @return the data point
*
* This must be called in order for each point.
*
* @param location the location to get data for (this method takes ownership of that location)
* @param track the track to get data from
* @param result the resulting point to fill out
* @return the previous location, now available for reuse
*/
public double[] getDataPoint(Location location, Track track) {
double[] result = new double[3];
private Location getDataPoint(Location location, Track track, double[] result) {
switch (mode) {
case BY_DISTANCE:
result[0] = profileLength;
@@ -488,10 +509,10 @@ public class ChartActivity extends Activity implements
if (Math.abs(location.getSpeed() - 128) > 1) {
speedBuffer.setNext(location.getSpeed());
}
} else if (TripStatistics.isValidSpeed(
} else if (TripStatisticsBuilder.isValidSpeed(
location.getTime(), location.getSpeed(), lastLocation.getTime(),
lastLocation.getSpeed(), speedBuffer)
&& (location.getSpeed() <= track.getMaxSpeed())) {
&& (location.getSpeed() <= track.getStatistics().getMaxSpeed())) {
speedBuffer.setNext(location.getSpeed());
}
result[2] = speedBuffer.getAverage() * 3.6;
@@ -502,8 +523,16 @@ public class ChartActivity extends Activity implements
// Format as hours per unit
result[2] = (60.0 / result[2]);
}
Location oldLastLocation = lastLocation;
lastLocation = location;
return result;
if (oldLastLocation == null) {
// No previous location, but return a blank one for reuse
return new Location("");
}
return oldLastLocation;
}
/**
@@ -546,6 +575,8 @@ public class ChartActivity extends Activity implements
try {
final ArrayList<double[]> theData = new ArrayList<double[]>();
int points = 0;
// Need two locations so we can keep track of the last location.
Location location = new Location("");
while (lastLocationRead < track.getStopId()) {
cursor = providerUtils.getLocationsCursor(
selectedTrackId, lastLocationRead, bufferSize, false);
@@ -557,11 +588,12 @@ public class ChartActivity extends Activity implements
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
while (cursor.moveToNext()) {
points++;
Location location = providerUtils.createLocation(cursor);
providerUtils.fillLocation(cursor, location);
if (MyTracksUtils.isValidLocation(location)) {
lastLocationRead = lastSeenLocationId =
cursor.getLong(idColumnIdx);
double[] point = getDataPoint(location, track);
double[] point = new double[3];
location = getDataPoint(location, track, point);
if (points % chartSamplingFrequency == 0) {
theData.add(point);
}
@@ -66,6 +66,10 @@ public class ChartSettingsDialog extends Dialog {
}
public void setup(ChartActivity chart) {
if (chart == null) {
return;
}
RadioGroup rd = (RadioGroup) findViewById(R.id.chart_settings_x);
rd.check(chart.getMode() == Mode.BY_DISTANCE
? R.id.chart_settings_by_distance
@@ -25,7 +25,7 @@ import android.graphics.Path;
import java.text.DecimalFormat;
/**
* This class encasulates meta data about one series of values for a chart.
* This class encapsulates meta data about one series of values for a chart.
*
* @author Sandor Dornbush
*/
@@ -511,8 +511,8 @@ public class ChartView extends View {
// Dimensions have changed (for example due to orientation change):
w = c.getWidth();
h = c.getHeight();
effectiveWidth = w - leftBorder - RIGHT_BORDER;
effectiveHeight = h - TOP_BORDER - BOTTOM_BORDER;
effectiveWidth = Math.max(0, w - leftBorder - RIGHT_BORDER);
effectiveHeight = Math.max(0, h - TOP_BORDER - BOTTOM_BORDER);
setupPath();
}
c.save();
@@ -0,0 +1,231 @@
/*
* 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;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager.BadTokenException;
/**
* A class to handle all dialog related events for My Tracks.
*
* @author Sandor Dornbush
*/
public class DialogManager {
public static final int DIALOG_CHART_SETTINGS = 1;
public static final int DIALOG_IMPORT_PROGRESS = 2;
public static final int DIALOG_PROGRESS = 3;
public static final int DIALOG_SEND_TO_GOOGLE = 4;
public static final int DIALOG_SEND_TO_GOOGLE_RESULT = 5;
public static final int DIALOG_WRITE_PROGRESS = 6;
private ProgressDialog progressDialog;
private ProgressDialog importProgressDialog;
private ProgressDialog writeProgressDialog;
private SendToGoogleDialog sendToGoogleDialog;
private AlertDialog sendToGoogleResultDialog;
private ChartSettingsDialog chartSettingsDialog;
private MyTracks activity;
public DialogManager(MyTracks activity) {
this.activity = activity;
}
protected Dialog onCreateDialog(int id, Bundle args) {
switch (id) {
case DIALOG_CHART_SETTINGS:
chartSettingsDialog = new ChartSettingsDialog(activity);
return chartSettingsDialog;
case DIALOG_IMPORT_PROGRESS:
importProgressDialog = new ProgressDialog(activity);
importProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
importProgressDialog.setTitle(
activity.getString(R.string.progress_title));
importProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
importProgressDialog.setMessage(
activity.getString(R.string.import_progress_message));
return importProgressDialog;
case DIALOG_PROGRESS:
progressDialog = new ProgressDialog(activity);
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setTitle(activity.getString(R.string.progress_title));
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("");
progressDialog.setMax(100);
progressDialog.setProgress(10);
return progressDialog;
case DIALOG_SEND_TO_GOOGLE:
sendToGoogleDialog = new SendToGoogleDialog(activity);
return sendToGoogleDialog;
case DIALOG_SEND_TO_GOOGLE_RESULT:
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setTitle("Title");
builder.setMessage("Message");
builder.setPositiveButton(activity.getString(R.string.ok), null);
builder.setNeutralButton(activity.getString(R.string.share_map),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
activity.shareLinkToMyMap(activity.getSendToMyMapsMapId());
dialog.dismiss();
}
});
sendToGoogleResultDialog = builder.create();
return sendToGoogleResultDialog;
case DIALOG_WRITE_PROGRESS:
writeProgressDialog = new ProgressDialog(activity);
writeProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
writeProgressDialog.setTitle(
activity.getString(R.string.progress_title));
writeProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
writeProgressDialog.setMessage(
activity.getString(R.string.write_progress_message));
return writeProgressDialog;
}
return null;
}
protected void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_SEND_TO_GOOGLE:
activity.resetSendToGoogleStatus();
break;
case DIALOG_SEND_TO_GOOGLE_RESULT:
boolean success = activity.getSendToGoogleSuccess();
sendToGoogleResultDialog.setTitle(
success ? R.string.success : R.string.error);
sendToGoogleResultDialog.setIcon(success
? android.R.drawable.ic_dialog_info
: android.R.drawable.ic_dialog_alert);
sendToGoogleResultDialog.setMessage(activity.getMapsResultMessage());
boolean canShare = activity.getSendToMyMapsMapId() != null;
View share =
sendToGoogleResultDialog.findViewById(android.R.id.button3);
if (share != null) {
share.setVisibility(canShare ? View.VISIBLE : View.GONE);
}
break;
case DIALOG_CHART_SETTINGS:
Log.d(MyTracksConstants.TAG, "MyTracks.onPrepare chart dialog");
chartSettingsDialog.setup(activity.getChartActivity());
break;
}
}
public void setProgressMessage(final String message) {
activity.runOnUiThread(new Runnable() {
public void run() {
synchronized (this) {
if (progressDialog != null) {
progressDialog.setMessage(message);
}
}
}
});
}
public void setProgressValue(final int percent) {
activity.runOnUiThread(new Runnable() {
public void run() {
synchronized (this) {
if (progressDialog != null) {
progressDialog.setProgress(percent);
}
}
}
});
}
/**
* @return the sendToGoogleDialog
*/
public SendToGoogleDialog getSendToGoogleDialog() {
return sendToGoogleDialog;
}
/**
* Shows a dialog with the given message.
* Does it on the UI thread.
*
* @param success if true, displays an info icon/title, otherwise an error
* icon/title
* @param message resource string id
*/
public void showMessageDialog(final int message, final boolean success) {
activity.runOnUiThread(new Runnable() {
public void run() {
AlertDialog dialog = null;
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(activity.getString(message));
builder.setNegativeButton(activity.getString(R.string.ok), null);
builder.setIcon(success ? android.R.drawable.ic_dialog_info :
android.R.drawable.ic_dialog_alert);
builder.setTitle(success ? R.string.success : R.string.error);
dialog = builder.create();
dialog.show();
}
});
}
/**
* Just like showDialog, but will catch a BadTokenException that sometimes
* (very rarely) gets thrown. This might happen if the user hits the "back"
* button immediately after sending tracks to google.
*
* @param id the dialog id
*/
public void showDialogSafely(final int id) {
activity.runOnUiThread(new Runnable() {
public void run() {
try {
activity.showDialog(id);
} catch (BadTokenException e) {
Log.w(MyTracksConstants.TAG,
"Could not display dialog with id " + id, e);
} catch (IllegalStateException e) {
Log.w(MyTracksConstants.TAG,
"Could not display dialog with id " + id, e);
}
}
});
}
/**
* Dismisses the progress dialog if it is showing. Executed on the UI thread.
*/
public void dismissDialogSafely(final int id) {
activity.runOnUiThread(new Runnable() {
public void run() {
try {
activity.dismissDialog(id);
} catch (IllegalArgumentException e) {
// This will be thrown if this dialog was not shown before.
}
}
});
}
}
@@ -17,10 +17,12 @@ package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.io.TrackWriter;
import com.google.android.apps.mytracks.io.TrackWriterFactory;
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
@@ -45,7 +47,7 @@ public class ExportAllTracks {
public static final int KML_OPTION_INDEX = 1;
public static final int CSV_OPTION_INDEX = 2;
private final Context context;
private final Activity activity;
private WakeLock wakeLock;
private ProgressDialog progress;
@@ -69,11 +71,11 @@ public class ExportAllTracks {
}
};
public ExportAllTracks(Context c) {
this.context = c;
public ExportAllTracks(Activity activity) {
this.activity = activity;
Log.i(MyTracksConstants.TAG, "ExportAllTracks: Starting");
AlertDialog.Builder builder = new AlertDialog.Builder(c);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setSingleChoiceItems(R.array.export_formats, 0, itemClick);
builder.setPositiveButton(R.string.ok, positiveClick);
builder.setNegativeButton(R.string.cancel, null);
@@ -106,10 +108,11 @@ public class ExportAllTracks {
*/
private void aquireLocksAndExport() {
SharedPreferences prefs =
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
activity.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
long recordingTrackId = -1;
if (prefs != null) {
recordingTrackId = prefs.getLong(MyTracksSettings.RECORDING_TRACK, -1);
recordingTrackId =
prefs.getLong(activity.getString(R.string.recording_track_key), -1);
}
if (recordingTrackId != -1) {
acquireWakeLock();
@@ -125,12 +128,12 @@ public class ExportAllTracks {
Log.i(MyTracksConstants.TAG, "ExportAllTracks: Releasing wake lock.");
}
Log.i(MyTracksConstants.TAG, "ExportAllTracks: Done");
Toast.makeText(context, R.string.export_done, Toast.LENGTH_SHORT).show();
Toast.makeText(activity, R.string.export_done, Toast.LENGTH_SHORT).show();
}
private void makeProgressDialog(final int trackCount) {
String exportMsg = context.getString(R.string.tracklist_btn_export_all);
progress = new ProgressDialog(context);
String exportMsg = activity.getString(R.string.tracklist_btn_export_all);
progress = new ProgressDialog(activity);
progress.setIcon(android.R.drawable.ic_dialog_info);
progress.setTitle(exportMsg);
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
@@ -148,22 +151,25 @@ public class ExportAllTracks {
Cursor cursor = null;
try {
MyTracksProviderUtils providerUtils =
MyTracksProviderUtils.Factory.get(context);
MyTracksProviderUtils.Factory.get(activity);
cursor = providerUtils.getTracksCursor("");
if (cursor == null) {
return;
}
final int trackCount = cursor.getCount();
Log.i(MyTracksConstants.TAG,
"ExportAllTracks: Exporting: " + cursor.getCount() + " tracks.");
int idxTrackId = cursor.getColumnIndexOrThrow(TracksColumns._ID);
MyTracks.getInstance().runOnUiThread(new Runnable() {
activity.runOnUiThread(new Runnable() {
public void run() {
makeProgressDialog(trackCount);
}});
}
});
for (int i = 0; cursor.moveToNext(); i++) {
final int status = i;
MyTracks.getInstance().runOnUiThread(new Runnable() {
activity.runOnUiThread(new Runnable() {
public void run() {
synchronized (this) {
if (progress == null) {
@@ -176,16 +182,27 @@ public class ExportAllTracks {
long id = cursor.getLong(idxTrackId);
Log.i(MyTracksConstants.TAG, "ExportAllTracks: exporting: " + id);
TrackWriterFactory.newWriter(context, providerUtils, id, format)
.writeTrack();
TrackWriter writer =
TrackWriterFactory.newWriter(activity, providerUtils, id, format);
writer.writeTrack();
if (!writer.wasSuccess()) {
// Abort the whole export on the first error.
int error = writer.getErrorMessage();
Toast.makeText(activity, error, Toast.LENGTH_LONG).show();
return;
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
if (progress != null) {
progress.dismiss();
if (progress != null) {
synchronized (this) {
progress.dismiss();
progress = null;
}
}
}
}
@@ -197,7 +214,7 @@ public class ExportAllTracks {
Log.i(MyTracksConstants.TAG, "ExportAllTracks: Aquiring wake lock.");
try {
PowerManager pm =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
(PowerManager) activity.getSystemService(Context.POWER_SERVICE);
if (pm == null) {
Log.e(MyTracksConstants.TAG,
"ExportAllTracks: Power manager not found!");
@@ -0,0 +1,98 @@
/*
* 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;
import com.google.android.maps.mytracks.R;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
/**
* Manage the application menus.
*
* @author Sandor Dornbush
*/
class MenuManager {
private final MyTracks activity;
public MenuManager(MyTracks activity) {
this.activity = activity;
}
public boolean onCreateOptionsMenu(Menu menu) {
activity.getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void onPrepareOptionsMenu(Menu menu, boolean hasRecorded,
boolean isRecording, boolean hasSelectedTrack) {
menu.findItem(R.id.menu_list_tracks).setEnabled(hasRecorded);
menu.findItem(R.id.menu_list_markers)
.setEnabled(hasRecorded && hasSelectedTrack);
menu.findItem(R.id.menu_start_recording)
.setEnabled(!isRecording)
.setVisible(!isRecording);
menu.findItem(R.id.menu_stop_recording)
.setEnabled(isRecording)
.setVisible(isRecording);
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_start_recording: {
activity.startRecording();
return true;
}
case R.id.menu_stop_recording: {
activity.stopRecording();
return true;
}
case R.id.menu_list_tracks: {
Intent startIntent = new Intent(activity, MyTracksList.class);
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_TRACK);
return true;
}
case R.id.menu_list_markers: {
Intent startIntent = new Intent(activity, MyTracksWaypointsList.class);
startIntent.putExtra("trackid", activity.getSelectedTrackId());
activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_WAYPOINT);
return true;
}
case R.id.menu_settings: {
Intent startIntent = new Intent(activity, MyTracksSettings.class);
activity.startActivity(startIntent);
return true;
}
case R.id.menu_aggregated_stats: {
Intent startIntent = new Intent(activity, AggregatedStatsActivity.class);
activity.startActivity(startIntent);
return true;
}
case R.id.menu_help: {
Intent startIntent = new Intent(activity, WelcomeActivity.class);
activity.startActivity(startIntent);
return true;
}
case MyTracksConstants.MENU_CLEAR_MAP: {
activity.setSelectedTrackId(-1);
return true;
}
}
return false;
}
}
File diff suppressed because it is too large Load Diff
@@ -32,6 +32,12 @@ public abstract class MyTracksConstants {
*/
public static final String GPS_PROVIDER = "gps";
/**
* Name of the top-level directory inside the SD card where our files will
* be read from/written to.
*/
public static final String SDCARD_TOP_DIR = "MyTracks";
/*
* onActivityResult request codes:
*/
@@ -54,29 +60,25 @@ public abstract class MyTracksConstants {
public static final int SHARE_GPX_FILE = 15;
public static final int SHARE_KML_FILE = 16;
public static final int SHARE_CSV_FILE = 17;
public static final int EDIT_DETAILS = 18;
public static final int SAVE_GPX_FILE = 19;
public static final int SAVE_KML_FILE = 20;
public static final int SAVE_CSV_FILE = 21;
public static final int CLEAR_MAP = 22;
public static final int SHOW_WAYPOINT = 23;
public static final int EDIT_WAYPOINT = 24;
public static final int WELCOME = 25;
public static final int SHARE_TCX_FILE = 18;
public static final int EDIT_DETAILS = 19;
public static final int SAVE_GPX_FILE = 20;
public static final int SAVE_KML_FILE = 21;
public static final int SAVE_CSV_FILE = 22;
public static final int SAVE_TCX_FILE = 23;
public static final int CLEAR_MAP = 24;
public static final int SHOW_WAYPOINT = 25;
public static final int EDIT_WAYPOINT = 26;
public static final int WELCOME = 27;
/*
* Menu ids:
*/
public static final int MENU_START_RECORDING = 1;
public static final int MENU_STOP_RECORDING = 2;
public static final int MENU_LIST_TRACKS = 3;
public static final int MENU_LIST_MARKERS = 4;
public static final int MENU_SETTINGS = 5;
public static final int MENU_MY_LOCATION = 6;
public static final int MENU_TOGGLE_LAYERS = 7;
public static final int MENU_CHART_SETTINGS = 8;
public static final int MENU_HELP = 9;
public static final int MENU_CURRENT_SEGMENT = 10;
public static final int MENU_MY_LOCATION = 1;
public static final int MENU_TOGGLE_LAYERS = 2;
public static final int MENU_CHART_SETTINGS = 3;
public static final int MENU_CURRENT_SEGMENT = 4;
/*
* Context menu ids:
@@ -91,11 +93,13 @@ public abstract class MyTracksConstants {
public static final int MENU_SHARE_GPX_FILE = 201;
public static final int MENU_SHARE_KML_FILE = 202;
public static final int MENU_SHARE_CSV_FILE = 203;
public static final int MENU_WRITE_TO_SD_CARD = 204;
public static final int MENU_SAVE_GPX_FILE = 205;
public static final int MENU_SAVE_KML_FILE = 206;
public static final int MENU_SAVE_CSV_FILE = 207;
public static final int MENU_CLEAR_MAP = 208;
public static final int MENU_SHARE_TCX_FILE = 204;
public static final int MENU_WRITE_TO_SD_CARD = 205;
public static final int MENU_SAVE_GPX_FILE = 206;
public static final int MENU_SAVE_KML_FILE = 207;
public static final int MENU_SAVE_CSV_FILE = 208;
public static final int MENU_SAVE_TCX_FILE = 209;
public static final int MENU_CLEAR_MAP = 210;
/**
* The number of distance readings to smooth to get a stable signal.
@@ -123,6 +127,12 @@ public abstract class MyTracksConstants {
*/
public static final int MAX_DISPLAYED_TRACK_POINTS = 10000;
/**
* Target number of track points displayed by the map overlay.
* We may display more than this number of points.
*/
public static final int TARGET_DISPLAYED_TRACK_POINTS = 5000;
/**
* Maximum number of track points ever loaded at once from the provider into
* memory.
@@ -162,6 +172,13 @@ public abstract class MyTracksConstants {
* The type of account that we can use for gdata uploads.
*/
public static final String ACCOUNT_TYPE = "com.google";
/**
* The name of extra intent property to indicate whether we want to resume
* a previously recorded track.
*/
public static final String RESUME_TRACK_EXTRA_NAME =
"com.google.android.apps.mytracks.RESUME_TRACK";
public static int getActionFromMenuId(int menuId) {
switch (menuId) {
@@ -179,12 +196,16 @@ public abstract class MyTracksConstants {
return MyTracksConstants.SHARE_GPX_FILE;
case MyTracksConstants.MENU_SHARE_CSV_FILE:
return MyTracksConstants.SHARE_CSV_FILE;
case MyTracksConstants.MENU_SHARE_TCX_FILE:
return MyTracksConstants.SHARE_TCX_FILE;
case MyTracksConstants.MENU_SAVE_GPX_FILE:
return MyTracksConstants.SAVE_GPX_FILE;
case MyTracksConstants.MENU_SAVE_KML_FILE:
return MyTracksConstants.SAVE_KML_FILE;
case MyTracksConstants.MENU_SAVE_CSV_FILE:
return MyTracksConstants.SAVE_CSV_FILE;
case MyTracksConstants.MENU_SAVE_TCX_FILE:
return MyTracksConstants.SAVE_TCX_FILE;
case MyTracksConstants.MENU_CLEAR_MAP:
return MyTracksConstants.CLEAR_MAP;
default:
+1 -1
View File
@@ -62,7 +62,7 @@ public class MyTracksDeleteAllTracks extends Handler {
SharedPreferences prefs =
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong(MyTracksSettings.SELECTED_TRACK, -1);
editor.putLong(context.getString(R.string.selected_track_key), -1);
editor.commit();
if (done != null) {
Handler h = new Handler();
@@ -90,6 +90,8 @@ public class MyTracksList extends ListActivity
R.string.tracklist_share_kml_file);
share.add(0, MyTracksConstants.MENU_SHARE_CSV_FILE, 0,
R.string.tracklist_share_csv_file);
share.add(0, MyTracksConstants.MENU_SHARE_TCX_FILE, 0,
R.string.tracklist_share_tcx_file);
SubMenu save = menu.addSubMenu(0,
MyTracksConstants.MENU_WRITE_TO_SD_CARD, 0,
R.string.tracklist_write_to_sd);
@@ -99,6 +101,8 @@ public class MyTracksList extends ListActivity
R.string.tracklist_save_as_kml);
save.add(0, MyTracksConstants.MENU_SAVE_CSV_FILE, 0,
R.string.tracklist_save_as_csv);
save.add(0, MyTracksConstants.MENU_SAVE_TCX_FILE, 0,
R.string.tracklist_save_as_tcx);
menu.add(0, MyTracksConstants.MENU_DELETE, 0,
R.string.tracklist_delete_track);
}
@@ -111,14 +115,16 @@ public class MyTracksList extends ListActivity
if (key == null) {
return;
}
if (key.equals(MyTracksSettings.METRIC_UNITS)) {
metricUnits = sharedPreferences.getBoolean(MyTracksSettings.METRIC_UNITS, true);
if (key.equals(getString(R.string.metric_units_key))) {
metricUnits = sharedPreferences.getBoolean(
getString(R.string.metric_units_key), true);
if (tracksCursor != null && !tracksCursor.isClosed()) {
tracksCursor.requery();
}
}
if (key.equals(MyTracksSettings.RECORDING_TRACK)) {
recordingTrackId = sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
if (key.equals(getString(R.string.recording_track_key))) {
recordingTrackId = sharedPreferences.getLong(
getString(R.string.recording_track_key), -1);
}
}
@@ -193,9 +199,10 @@ public class MyTracksList extends ListActivity
SharedPreferences preferences =
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
preferences.registerOnSharedPreferenceChangeListener(this);
metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true);
metricUnits =
preferences.getBoolean(getString(R.string.metric_units_key), true);
recordingTrackId =
preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
preferences.getLong(getString(R.string.recording_track_key), -1);
tracksCursor = getContentResolver().query(
TracksColumns.CONTENT_URI, null, null, null, "_id DESC");
+316 -203
View File
@@ -21,6 +21,9 @@ import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackPointsColumns;
import com.google.android.apps.mytracks.content.Waypoint;
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.MyTracksUtils;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
@@ -48,14 +51,14 @@ import android.os.HandlerThread;
import android.provider.Settings;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.view.Window;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -68,8 +71,9 @@ import android.widget.Toast;
* @author Leif Hendrik Wilden
*/
public class MyTracksMap extends MapActivity
implements LocationListener, SensorEventListener, View.OnTouchListener,
View.OnClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
implements View.OnTouchListener, View.OnClickListener,
SharedPreferences.OnSharedPreferenceChangeListener {
private static final int TRACKPOINT_BUFFER_SIZE = 1024;
// Saved instance state keys:
// ---------------------------
@@ -80,9 +84,9 @@ public class MyTracksMap extends MapActivity
public static final String KEY_HAVE_GOOD_FIX = "haveGoodFix";
/**
* The currently selected track (or null if nothing selected).
* The ID of the currently selected track (or -1 if nothing selected).
*/
private Track selectedTrack;
private long selectedTrackId = -1;
/**
* The id of the currently recording track.
@@ -95,20 +99,26 @@ public class MyTracksMap extends MapActivity
*/
private boolean keepMyLocationVisible;
/**
* Id of the first location that was seen when reading tracks from the
* provider.
*/
private long firstSeenLocationId = -1;
/**
* Id of the last location that was seen when reading tracks from the
* provider. This is used to determine which locations are new compared to the
* last time the mapOverlay was updated.
*/
private long lastSeenLocationId = -1;
/**
* Magnetic variation.
*/
private double variation;
/**
* From the shared preferences:
* From the shared preferences.
*/
private int minRequiredAccuracy =
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY;
@@ -125,56 +135,32 @@ public class MyTracksMap extends MapActivity
private Location currentLocation;
/**
* A thread with a looper. Post to updateTrackHandler to execute Runnables on
* this thread.
* A thread with a looper. Post to updateTrackHandler to execute
* {@link Runnable}s on this thread.
*/
private final HandlerThread updateTrackThread =
new HandlerThread("updateTrackThread");
private HandlerThread updateTrackThread;
/** Handler for updateTrackThread */
/**
* Handler for updateTrackThread.
*/
private Handler updateTrackHandler;
private MyTracksProviderUtils providerUtils;
private SharedPreferences sharedPreferences;
/**
* A runnable that updates the track from the provider (looking for points
* added after "lastSeenLocationId".
* added after "lastSeenLocationId").
*/
private final Runnable updateTrackRunnable = new Runnable() {
@Override
public void run() {
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
Cursor cursor = null;
try {
cursor = providerUtils.getLocationsCursor(
recordingTrackId,
lastSeenLocationId + 1,
MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS
- selectedTrack.getLocations().size(),
true);
if (cursor != null) {
if (cursor.moveToLast()) {
final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
do {
lastSeenLocationId = cursor.getLong(idColumnIdx);
Location location = providerUtils.createLocation(cursor);
if (location != null) {
selectedTrack.addLocation(location);
}
} while (cursor.moveToPrevious());
}
}
} catch (RuntimeException e) {
Log.w(MyTracksConstants.TAG, "Caught an unexpected exception: ", e);
} finally {
if (cursor != null) {
cursor.close();
}
mapView.postInvalidate();
}
readAllNewTrackPoints();
}
};
@@ -184,12 +170,14 @@ public class MyTracksMap extends MapActivity
private Runnable restoreTrackRunnable = new Runnable() {
@Override
public void run() {
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
lastSeenLocationId = providerUtils.getTrackPoints(selectedTrack,
MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS);
mapView.postInvalidate();
mapOverlay.clearPoints();
firstSeenLocationId = -1;
lastSeenLocationId = -1;
readAllNewTrackPoints();
}
};
@@ -199,7 +187,7 @@ public class MyTracksMap extends MapActivity
private final Runnable restoreWaypointsRunnable = new Runnable() {
@Override
public void run() {
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
@@ -209,15 +197,12 @@ public class MyTracksMap extends MapActivity
// We will silently drop extra waypoints to make the app responsive.
// TODO: Try to only load the waypoints in the view port.
cursor = providerUtils.getWaypointsCursor(
selectedTrack.getId(), 0,
selectedTrackId, 0,
MyTracksConstants.MAX_DISPLAYED_WAYPOINTS_POINTS);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
Waypoint wpt = providerUtils.createWaypoint(cursor);
mapOverlay.addWaypoint(wpt);
} while (cursor.moveToNext());
}
if (cursor != null && cursor.moveToFirst()) {
do {
mapOverlay.addWaypoint(providerUtils.createWaypoint(cursor));
} while (cursor.moveToNext());
}
} catch (RuntimeException e) {
Log.w(MyTracksConstants.TAG, "Caught an unexpected exception.", e);
@@ -231,19 +216,18 @@ public class MyTracksMap extends MapActivity
};
/**
* A runnable intended to be posted to the updateTrackThread after the
* selected track changes. It will post to the ui thread to update the screen
* elements and move the map to show the selected track.
* A runnable intended to be posted to the {@code #updateTrackThread} after
* the selected track changes. It will post to the UI thread to update
* the screen elements and move the map to show the selected track.
*/
private final Runnable setSelectedTrackRunnable = new Runnable() {
@Override
public void run() {
uiHandler.post(new Runnable() {
public void run() {
showTrack(selectedTrack);
mapOverlay.setSelectedTrack(selectedTrack);
mapOverlay.setShowEndMarker(selectedTrack == null ||
selectedTrack.getId() != recordingTrackId);
showTrack(selectedTrackId);
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
mapOverlay.setShowEndMarker(!isRecordingSelected());
mapView.invalidate();
busyPane.setVisibility(View.GONE);
updateOptionsButton();
@@ -301,6 +285,11 @@ public class MyTracksMap extends MapActivity
Log.d(MyTracksConstants.TAG, "MyTracksMap.onCreate");
super.onCreate(bundle);
// The volume we want to control is the Text-To-Speech volume
int volumeStream =
new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream();
setVolumeControlStream(volumeStream);
providerUtils = MyTracksProviderUtils.Factory.get(this);
// We don't need a window title bar:
@@ -334,6 +323,7 @@ public class MyTracksMap extends MapActivity
locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
updateTrackThread = new HandlerThread("updateTrackThread");
updateTrackThread.start();
updateTrackHandler = new Handler(updateTrackThread.getLooper());
@@ -343,14 +333,7 @@ public class MyTracksMap extends MapActivity
@Override
public void onChange(boolean selfChange) {
Log.d(MyTracksConstants.TAG, "MyTracksMap: ContentObserver.onChange");
// Check for any new locations and append them to the currently
// recording track:
if (!MyTracks.getInstance().isRecording()) {
// No track is being recorded. We should not be here.
return;
}
if (selectedTrack == null
|| selectedTrack.getId() != recordingTrackId) {
if (!isRecordingSelected()) {
// No track, or one other than the recording track is selected,
// don't bother.
return;
@@ -366,7 +349,7 @@ public class MyTracksMap extends MapActivity
public void onChange(boolean selfChange) {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: ContentObserver.onChange waypoints");
if (selectedTrack == null) {
if (!isATrackSelected()) {
return;
}
updateTrackHandler.post(restoreWaypointsRunnable);
@@ -374,38 +357,49 @@ public class MyTracksMap extends MapActivity
}
};
// Read shared preferences and register change listener:
SharedPreferences preferences =
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
minRequiredAccuracy = preferences.getInt(
MyTracksSettings.MIN_REQUIRED_ACCURACY,
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY);
recordingTrackId =
preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
long selectedTrackId =
preferences.getLong(MyTracksSettings.SELECTED_TRACK, -1);
if (selectedTrackId >= 0) {
setSelectedTrack(selectedTrackId);
}
// Read shared preferences and register change listener.
sharedPreferences = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (sharedPreferences != null) {
reloadSharedPreferences(sharedPreferences, null);
updateOptionsButton();
preferences.registerOnSharedPreferenceChangeListener(this);
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
}
}
@SuppressWarnings("deprecation")
protected void setupZoomControls() {
mapView.setBuiltInZoomControls(true);
}
@Override
protected void onDestroy() {
// Final cleanup before activity is destroyed.
// May not be called at all in some situations.
Log.d(MyTracksConstants.TAG, "MyTracksMap.onDestroy");
if (updateTrackThread != null) {
ApiFeatures.getInstance().getApiPlatformAdapter().stopHandlerThread(
updateTrackThread);
}
if (sharedPreferences != null) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
}
super.onDestroy();
}
/**
* Returns whether there's a track currently selected for display.
*/
private boolean isATrackSelected() {
return selectedTrackId >= 0;
}
/**
* Returns whether we're currently recording the same track that's selected
* for display.
*/
private boolean isRecordingSelected() {
return isATrackSelected() && selectedTrackId == recordingTrackId;
}
protected void setupZoomControls() {
mapView.setBuiltInZoomControls(true);
}
@Override
protected void onStart() {
// Called after onCreate or onStop.
@@ -448,6 +442,9 @@ public class MyTracksMap extends MapActivity
Log.d(MyTracksConstants.TAG, "MyTracksMap.onResume");
super.onResume();
// Reload all preferences as they might have changed since last run.
reloadSharedPreferences(sharedPreferences, null);
// Make sure any updates that might have happened are propagated to the
// Map overlay:
observer.onChange(false);
@@ -468,10 +465,9 @@ public class MyTracksMap extends MapActivity
// While this activity was paused the user may have deleted the selected
// track. In that case the map overlay needs to be cleared:
Track track = mapOverlay.getSelectedTrack();
if (track != null && !providerUtils.trackExists(track.getId())) {
if (isATrackSelected() && !providerUtils.trackExists(selectedTrackId)) {
// The recording track must have been deleted meanwhile.
mapOverlay.setSelectedTrack(null);
mapOverlay.setTrackDrawingEnabled(false);
mapView.invalidate();
}
}
@@ -533,10 +529,10 @@ public class MyTracksMap extends MapActivity
+ gpsProvider.getName());
}
locationManager.requestLocationUpdates(gpsProvider.getName(),
0 /*minTime*/, 0 /*minDist*/, this);
0 /*minTime*/, 0 /*minDist*/, locationListener);
try {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, this);
1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, locationListener);
} catch (RuntimeException e) {
// If anything at all goes wrong with getting a cell location do not
// abort. Cell location is not essential to this app.
@@ -554,7 +550,7 @@ public class MyTracksMap extends MapActivity
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now registering sensor listeners.");
sensorManager.registerListener(
this, compass, SensorManager.SENSOR_DELAY_UI);
sensorListener, compass, SensorManager.SENSOR_DELAY_UI);
}
/**
@@ -564,12 +560,12 @@ public class MyTracksMap extends MapActivity
if (locationManager != null) {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now unregistering location listeners.");
locationManager.removeUpdates(this);
locationManager.removeUpdates(locationListener);
}
if (sensorManager != null) {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Now unregistering sensor listeners.");
sensorManager.unregisterListener(this);
sensorManager.unregisterListener(sensorListener);
}
}
@@ -598,7 +594,7 @@ public class MyTracksMap extends MapActivity
*/
private void updateOptionsButton() {
optionsBtn.setVisibility(
selectedTrack != null ? View.VISIBLE : View.INVISIBLE);
isATrackSelected() ? View.VISIBLE : View.INVISIBLE);
}
/**
@@ -651,20 +647,31 @@ public class MyTracksMap extends MapActivity
/**
* Zooms and pans the map so that the given track is visible.
*
* @param track a given track
* @param trackId a given track ID
*/
public void showTrack(Track track) {
if (track == null || mapView == null || track.getNumberOfPoints() < 2) {
public void showTrack(long trackId) {
if (mapView == null) {
return;
}
int latSpanE6 = track.getTop() - track.getBottom();
int lonSpanE6 = track.getRight() - track.getLeft();
if (latSpanE6 > 0 && latSpanE6 < 180E6 && lonSpanE6 > 0
&& lonSpanE6 < 180E6) {
Track track = providerUtils.getTrack(trackId);
if (track == null || track.getNumberOfPoints() < 2) {
return;
}
TripStatistics stats = track.getStatistics();
int bottom = stats.getBottom();
int left = stats.getLeft();
int latSpanE6 = stats.getTop() - bottom;
int lonSpanE6 = stats.getRight() - left;
if (latSpanE6 > 0
&& latSpanE6 < 180E6
&& lonSpanE6 > 0
&& lonSpanE6 < 360E6) {
keepMyLocationVisible = false;
GeoPoint center = new GeoPoint(
track.getBottom() + latSpanE6 / 2,
track.getLeft() + lonSpanE6 / 2);
bottom + latSpanE6 / 2,
left + lonSpanE6 / 2);
if (MyTracksUtils.isValidGeoPoint(center)) {
mapView.getController().setCenter(center);
mapView.getController().zoomToSpan(latSpanE6, lonSpanE6);
@@ -694,24 +701,33 @@ public class MyTracksMap extends MapActivity
* @param trackId a given track id
*/
public void setSelectedTrack(final long trackId) {
if (selectedTrack != null && selectedTrack.getId() == trackId) {
Log.d(MyTracksConstants.TAG, "MyTracksMap.setSelectedTrack: "
+ "selectedTrackId = " + selectedTrackId + ", trackId = " + trackId);
if (selectedTrackId == trackId) {
// Selected track did not change, nothing to do.
mapOverlay.setSelectedTrack(selectedTrack);
mapView.invalidate();
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
updateOptionsButton();
mapView.invalidate();
return;
}
if (trackId < 0) {
// Remove selection.
selectedTrack = null;
mapOverlay.setSelectedTrack(null);
selectedTrackId = -1;
mapOverlay.setTrackDrawingEnabled(false);
mapOverlay.clearWaypoints();
updateOptionsButton();
mapView.invalidate();
return;
}
busyPane.setVisibility(View.VISIBLE);
selectedTrack = providerUtils.getTrack(trackId);
selectedTrackId = trackId;
loadSelectedTrack();
}
private void loadSelectedTrack() {
updateTrackHandler.post(restoreTrackRunnable);
updateTrackHandler.post(restoreWaypointsRunnable);
updateTrackHandler.post(setSelectedTrackRunnable);
@@ -767,8 +783,7 @@ public class MyTracksMap extends MapActivity
menu.setHeaderTitle(R.string.tracklist_this_track);
menu.add(0, MyTracksConstants.MENU_EDIT, 0,
R.string.tracklist_edit_track);
if (!MyTracks.getInstance().isRecording() || (selectedTrack != null
&& selectedTrack.getId() != recordingTrackId)) {
if (!isRecordingSelected()) {
menu.add(0, MyTracksConstants.MENU_SEND_TO_GOOGLE, 0,
R.string.tracklist_send_to_google);
SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0,
@@ -781,6 +796,8 @@ public class MyTracksMap extends MapActivity
R.string.tracklist_share_kml_file);
share.add(0, MyTracksConstants.MENU_SHARE_CSV_FILE, 0,
R.string.tracklist_share_csv_file);
share.add(0, MyTracksConstants.MENU_SHARE_TCX_FILE, 0,
R.string.tracklist_share_tcx_file);
SubMenu save = menu.addSubMenu(0,
MyTracksConstants.MENU_WRITE_TO_SD_CARD, 0,
R.string.tracklist_write_to_sd);
@@ -790,6 +807,8 @@ public class MyTracksMap extends MapActivity
R.string.tracklist_save_as_kml);
save.add(0, MyTracksConstants.MENU_SAVE_CSV_FILE, 0,
R.string.tracklist_save_as_csv);
save.add(0, MyTracksConstants.MENU_SAVE_TCX_FILE, 0,
R.string.tracklist_save_as_tcx);
menu.add(0, MyTracksConstants.MENU_CLEAR_MAP, 0,
R.string.tracklist_clear_map);
menu.add(0, MyTracksConstants.MENU_DELETE, 0,
@@ -801,7 +820,7 @@ public class MyTracksMap extends MapActivity
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
if (!super.onMenuItemSelected(featureId, item)) {
if (selectedTrack != null) {
if (isATrackSelected()) {
MyTracks.getInstance().onActivityResult(
MyTracksConstants.getActionFromMenuId(item.getItemId()), RESULT_OK,
new Intent());
@@ -862,8 +881,7 @@ public class MyTracksMap extends MapActivity
public void onClick(View v) {
if (v == messagePane) {
launchMyLocationSettings();
}
if (v == optionsBtn) {
} else if (v == optionsBtn) {
optionsBtn.performLongClick();
}
}
@@ -883,96 +901,191 @@ public class MyTracksMap extends MapActivity
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
public void onSharedPreferenceChanged(
final SharedPreferences sharedPreferences, final String key) {
Log.d(MyTracksConstants.TAG,
"MyTracksMap.onSharedPreferenceChanged: " + key);
if (key != null) {
if (key.equals(MyTracksSettings.MIN_REQUIRED_ACCURACY)) {
minRequiredAccuracy = sharedPreferences.getInt(
MyTracksSettings.MIN_REQUIRED_ACCURACY,
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY);
} else if (key.equals(MyTracksSettings.SELECTED_TRACK)) {
long selectedTrackId =
sharedPreferences.getLong(MyTracksSettings.SELECTED_TRACK, -1);
setSelectedTrack(selectedTrackId);
} else if (key.equals(MyTracksSettings.RECORDING_TRACK)) {
recordingTrackId =
sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
if (selectedTrack != null) {
mapOverlay.setShowEndMarker(
selectedTrack.getId() != recordingTrackId);
mapView.postInvalidate();
uiHandler.post(new Runnable() {
@Override
public void run() {
reloadSharedPreferences(sharedPreferences, key);
}
});
}
}
private final LocationListener locationListener = new LocationListener() {
@Override
public void onProviderEnabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.wait_for_fix);
}
}
@Override
public void onProviderDisabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.status_enable_gps);
messagePane.setVisibility(View.VISIBLE);
messagePane.setOnClickListener(MyTracksMap.this);
screen.requestLayout();
}
}
@Override
public void onLocationChanged(Location location) {
if (location.getProvider().equals(MyTracksConstants.GPS_PROVIDER)) {
// Recalculate the variation if there was a jump in location > 1km:
if (currentLocation == null ||
location.distanceTo(currentLocation) > 1000) {
setVariation(location);
}
currentLocation = location;
boolean haveGoodFixNow =
currentLocation.getAccuracy() < minRequiredAccuracy;
if (haveGoodFixNow != haveGoodFix) {
haveGoodFix = haveGoodFixNow;
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE);
screen.requestLayout();
}
showCurrentLocation();
} else {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Network location update received.");
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
switch (status) {
case LocationProvider.OUT_OF_SERVICE:
case LocationProvider.TEMPORARILY_UNAVAILABLE:
haveGoodFix = false;
messagePane.setVisibility(View.VISIBLE);
screen.requestLayout();
break;
}
}
}
}
};
@Override
public void onProviderEnabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.wait_for_fix);
}
}
@Override
public void onProviderDisabled(String provider) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
messageText.setText(R.string.status_enable_gps);
messagePane.setVisibility(View.VISIBLE);
messagePane.setOnClickListener(this);
screen.requestLayout();
}
}
@Override
public void onLocationChanged(Location location) {
if (location.getProvider().equals(MyTracksConstants.GPS_PROVIDER)) {
// Recalculate the variation if there was a jump in location > 1km:
if (currentLocation == null
|| location.distanceTo(currentLocation) > 1000) {
setVariation(location);
private final SensorEventListener sensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent se) {
synchronized (this) {
float magneticHeading = se.values[0];
double heading = magneticHeading + variation;
if (mapOverlay.setHeading((float) heading)) {
mapView.invalidate();
}
}
currentLocation = location;
boolean haveGoodFixNow =
currentLocation.getAccuracy() < minRequiredAccuracy;
if (haveGoodFixNow != haveGoodFix) {
haveGoodFix = haveGoodFixNow;
messagePane.setVisibility(haveGoodFix ? View.GONE : View.VISIBLE);
screen.requestLayout();
}
showCurrentLocation();
} else {
Log.d(MyTracksConstants.TAG,
"MyTracksMap: Network location update received.");
}
@Override
public void onAccuracyChanged(Sensor s, int accuracy) {
// do nothing
}
};
private void reloadSharedPreferences(SharedPreferences sharedPreferences,
String key) {
if (key == null ||
key.equals(getString(R.string.min_required_accuracy_key))) {
minRequiredAccuracy = sharedPreferences.getInt(
getString(R.string.min_required_accuracy_key),
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY);
}
if (key == null || key.equals(getString(R.string.recording_track_key))) {
recordingTrackId = sharedPreferences.getLong(
getString(R.string.recording_track_key), -1);
}
if (key == null || key.equals(getString(R.string.selected_track_key))) {
setSelectedTrack(sharedPreferences.getLong(
getString(R.string.selected_track_key), -1));
}
// Show end marker if the track has been selected and is not recording.
// Note: This check must be *after* a call to setSelectedTrack(...) above.
if (isATrackSelected()) {
mapOverlay.setShowEndMarker(!isRecordingSelected());
mapView.postInvalidate();
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (provider.equals(MyTracksConstants.GPS_PROVIDER)) {
switch (status) {
case LocationProvider.OUT_OF_SERVICE:
case LocationProvider.TEMPORARILY_UNAVAILABLE:
haveGoodFix = false;
messagePane.setVisibility(View.VISIBLE);
screen.requestLayout();
private void readAllNewTrackPoints() {
int numPoints = mapOverlay.getNumLocations();
if (numPoints >= MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS) {
// We're about to exceed the maximum allowed number of points, so reload
// the whole track with fewer points (the sampling frequency will be
// lower).
loadSelectedTrack();
return;
}
// Keep a copy of selectedTrackId, because it can change asynchronously.
long currentSelectedTrackId = selectedTrackId;
long lastStoredLocationId =
providerUtils.getLastLocationId(currentSelectedTrackId);
int samplingFrequency = -1;
Location location = new Location("");
while (currentSelectedTrackId == selectedTrackId) {
Cursor cursor = null;
try {
cursor = providerUtils.getLocationsCursor(currentSelectedTrackId,
lastSeenLocationId + 1, TRACKPOINT_BUFFER_SIZE, false);
if (cursor == null || !cursor.moveToFirst()) {
// No (more) data
break;
}
final int idColumnIdx = cursor.getColumnIndexOrThrow(
TrackPointsColumns._ID);
do {
long locationId = cursor.getLong(idColumnIdx);
lastSeenLocationId = locationId;
if (firstSeenLocationId == -1) {
// This was our first point, keep its ID
firstSeenLocationId = locationId;
}
if (samplingFrequency == -1) {
// Now we already have at least one point, calculate the sampling
// frequency
long numTotalPoints = lastStoredLocationId - firstSeenLocationId;
samplingFrequency = (int) (1 + numTotalPoints
/ MyTracksConstants.TARGET_DISPLAYED_TRACK_POINTS);
// TODO: This shouldn't happen after adding currentSelectedTrackId,
// but just to be safe until we have 100% confidence.
if (samplingFrequency <= 0) {
Log.w(MyTracksConstants.TAG,
"readAllNewTrackPoints: samplingFreq <= 0, numTotalPoints = "
+ numTotalPoints + ", trackId = " + currentSelectedTrackId);
samplingFrequency = 1;
}
}
providerUtils.fillLocation(cursor, location);
// Include a point if it fits one of the following criteria:
// - Has the mod for the sampling frequency (includes first point).
// - Is the last point and we are not recording this track.
// - The point is a segment split
if (numPoints % samplingFrequency == 0 ||
(!isRecordingSelected() && locationId == lastStoredLocationId) ||
!MyTracksUtils.isValidLocation(location)) {
// No need to allocate a new location (we can safely reuse the existing).
mapOverlay.addLocation(location);
}
numPoints++;
} while (cursor.moveToNext() &&
currentSelectedTrackId == selectedTrackId);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
@Override
public void onSensorChanged(SensorEvent se) {
synchronized (this) {
float magneticHeading = se.values[0];
double heading = magneticHeading + variation;
if (mapOverlay.setHeading((float) heading)) {
mapView.invalidate();
}
}
}
@Override
public void onAccuracyChanged(Sensor s, int accuracy) {
// do nothing
mapView.postInvalidate();
}
}
+284 -215
View File
@@ -1,12 +1,12 @@
/*
* 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
@@ -15,17 +15,17 @@
*/
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.util.MyTracksUtils;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
@@ -35,6 +35,9 @@ import android.graphics.drawable.Drawable;
import android.location.Location;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* A map overlay that displays a "MyLocation" arrow, an error circle, the
@@ -44,7 +47,7 @@ import java.util.ArrayList;
*/
public class MyTracksOverlay extends Overlay {
private final Drawable arrow[] = new Drawable[18];
private final Drawable[] arrows;
private final int arrowWidth, arrowHeight;
private final Drawable statsMarker;
private final Drawable waypointMarker;
@@ -54,86 +57,152 @@ public class MyTracksOverlay extends Overlay {
private final Paint selectedTrackPaint;
private final Paint errorCirclePaint;
private final Context context;
private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
private final List<Waypoint> waypoints;
private final List<CachedLocation> points;
private final BlockingQueue<CachedLocation> pendingPoints;
private Track selectedTrack;
private boolean trackDrawingEnabled;
private int lastHeading = 0;
private Location myLocation;
private boolean showEndMarker = true;
// TODO: Remove it completely after completing performance tests.
private boolean alwaysVisible = true;
private GeoPoint lastReferencePoint;
private Rect lastViewRect;
private Path lastPath;
/**
* Represents a pre-processed {@code Location} to speed up drawing.
* This class is more like a data object and doesn't provide accessors.
*/
private static class CachedLocation {
public final boolean valid;
public final GeoPoint geoPoint;
public CachedLocation(Location location) {
this.valid = MyTracksUtils.isValidLocation(location);
this.geoPoint = valid ? MyTracksUtils.getGeoPoint(location) : null;
}
};
public MyTracksOverlay(Context context) {
this.context = context;
this.waypoints = new ArrayList<Waypoint>();
this.points = new ArrayList<CachedLocation>(1024);
this.pendingPoints = new ArrayBlockingQueue<CachedLocation>(
MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS, true);
// TODO: Can we use a FrameAnimation or similar here rather
// than individual resources for each arrow direction?
arrow[0] = context.getResources().getDrawable(R.drawable.arrow_0);
arrow[1] = context.getResources().getDrawable(R.drawable.arrow_20);
arrow[2] = context.getResources().getDrawable(R.drawable.arrow_40);
arrow[3] = context.getResources().getDrawable(R.drawable.arrow_60);
arrow[4] = context.getResources().getDrawable(R.drawable.arrow_80);
arrow[5] = context.getResources().getDrawable(R.drawable.arrow_100);
arrow[6] = context.getResources().getDrawable(R.drawable.arrow_120);
arrow[7] = context.getResources().getDrawable(R.drawable.arrow_140);
arrow[8] = context.getResources().getDrawable(R.drawable.arrow_160);
arrow[9] = context.getResources().getDrawable(R.drawable.arrow_180);
arrow[10] = context.getResources().getDrawable(R.drawable.arrow_200);
arrow[11] = context.getResources().getDrawable(R.drawable.arrow_220);
arrow[12] = context.getResources().getDrawable(R.drawable.arrow_240);
arrow[13] = context.getResources().getDrawable(R.drawable.arrow_260);
arrow[14] = context.getResources().getDrawable(R.drawable.arrow_280);
arrow[15] = context.getResources().getDrawable(R.drawable.arrow_300);
arrow[16] = context.getResources().getDrawable(R.drawable.arrow_320);
arrow[17] = context.getResources().getDrawable(R.drawable.arrow_340);
arrowWidth = arrow[lastHeading].getIntrinsicWidth();
arrowHeight = arrow[lastHeading].getIntrinsicHeight();
for (int i = 0; i <= 17; i++) {
arrow[i].setBounds(0, 0, arrowWidth, arrowHeight);
final Resources resources = context.getResources();
arrows = new Drawable[] {
resources.getDrawable(R.drawable.arrow_0),
resources.getDrawable(R.drawable.arrow_20),
resources.getDrawable(R.drawable.arrow_40),
resources.getDrawable(R.drawable.arrow_60),
resources.getDrawable(R.drawable.arrow_80),
resources.getDrawable(R.drawable.arrow_100),
resources.getDrawable(R.drawable.arrow_120),
resources.getDrawable(R.drawable.arrow_140),
resources.getDrawable(R.drawable.arrow_160),
resources.getDrawable(R.drawable.arrow_180),
resources.getDrawable(R.drawable.arrow_200),
resources.getDrawable(R.drawable.arrow_220),
resources.getDrawable(R.drawable.arrow_240),
resources.getDrawable(R.drawable.arrow_260),
resources.getDrawable(R.drawable.arrow_280),
resources.getDrawable(R.drawable.arrow_300),
resources.getDrawable(R.drawable.arrow_320),
resources.getDrawable(R.drawable.arrow_340)
};
arrowWidth = arrows[lastHeading].getIntrinsicWidth();
arrowHeight = arrows[lastHeading].getIntrinsicHeight();
for (Drawable arrow : arrows) {
arrow.setBounds(0, 0, arrowWidth, arrowHeight);
}
statsMarker = context.getResources().getDrawable(R.drawable.ylw_pushpin);
statsMarker = resources.getDrawable(R.drawable.ylw_pushpin);
markerWidth = statsMarker.getIntrinsicWidth();
markerHeight = statsMarker.getIntrinsicHeight();
statsMarker.setBounds(0, 0, markerWidth, markerHeight);
startMarker = context.getResources().getDrawable(R.drawable.green_dot);
startMarker = resources.getDrawable(R.drawable.green_dot);
startMarker.setBounds(0, 0, markerWidth, markerHeight);
endMarker = context.getResources().getDrawable(R.drawable.red_dot);
endMarker = resources.getDrawable(R.drawable.red_dot);
endMarker.setBounds(0, 0, markerWidth, markerHeight);
waypointMarker =
context.getResources().getDrawable(R.drawable.blue_pushpin);
waypointMarker = resources.getDrawable(R.drawable.blue_pushpin);
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
selectedTrackPaint = new Paint();
selectedTrackPaint.setColor(context.getResources().getColor(R.color.red));
selectedTrackPaint.setColor(resources.getColor(R.color.red));
selectedTrackPaint.setStrokeWidth(3);
selectedTrackPaint.setStyle(Paint.Style.STROKE);
selectedTrackPaint.setAntiAlias(true);
errorCirclePaint = new Paint();
errorCirclePaint.setColor(context.getResources().getColor(R.color.blue));
errorCirclePaint.setColor(resources.getColor(R.color.blue));
errorCirclePaint.setStyle(Paint.Style.STROKE);
errorCirclePaint.setStrokeWidth(3);
errorCirclePaint.setAlpha(127);
errorCirclePaint.setAntiAlias(true);
}
public void setSelectedTrack(Track track) {
selectedTrack = track;
}
public Track getSelectedTrack() {
return selectedTrack;
/**
* Add a location to the map overlay.
*
* NOTE: This method doesn't take ownership of the given location, so it is
* safe to reuse the same location while calling this method.
*
* @param l the location to add.
*/
public void addLocation(Location l) {
// Queue up in the pending queue until it's merged with {@code #points}.
pendingPoints.offer(new CachedLocation(l));
}
public void addWaypoint(Waypoint wpt) {
waypoints.add(wpt);
// Note: We don't cache waypoints, because it's not worth the effort.
if (wpt != null && wpt.getLocation() != null) {
synchronized (waypoints) {
waypoints.add(wpt);
}
}
}
public int getNumLocations() {
synchronized (points) {
return points.size() + pendingPoints.size();
}
}
// Visible for testing.
int getNumWaypoints() {
synchronized (waypoints) {
return waypoints.size();
}
}
public void clearPoints() {
synchronized (points) {
points.clear();
pendingPoints.clear();
lastPath = null;
lastViewRect = null;
}
}
public void clearWaypoints() {
waypoints.clear();
synchronized (waypoints) {
waypoints.clear();
}
}
public void setTrackDrawingEnabled(boolean trackDrawingEnabled) {
this.trackDrawingEnabled = trackDrawingEnabled;
}
public void setShowEndMarker(boolean showEndMarker) {
@@ -142,181 +211,179 @@ public class MyTracksOverlay extends Overlay {
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if (shadow) {
return;
}
// Draw the selected track:
drawTrack(canvas, mapView, selectedTrack, true);
// It's safe to keep projection within a single draw operation.
final Projection projection = getMapProjection(mapView);
// Get the current viewing window.
if (trackDrawingEnabled) {
Rect viewRect = getMapViewRect(mapView);
// Draw the selected track:
drawTrack(canvas, projection, viewRect);
// Draw the waypoints:
ArrayList<Waypoint> currentWaypoints = waypoints;
for (int i = 1; i < currentWaypoints.size(); i++) {
Location loc = currentWaypoints.get(i).getLocation();
if (loc == null) {
continue;
}
GeoPoint geoPoint = MyTracksUtils.getGeoPoint(loc);
Point pt = new Point();
mapView.getProjection().toPixels(geoPoint, pt);
canvas.save();
canvas.translate(pt.x - (markerWidth / 2) + 3, pt.y - (markerHeight));
if (currentWaypoints.get(i).getType() == Waypoint.TYPE_STATISTICS) {
statsMarker.draw(canvas);
} else {
waypointMarker.draw(canvas);
}
canvas.restore();
// Draw the waypoints:
drawWaypoints(canvas, projection);
}
// Draw the arrow icon:
if (myLocation == null) {
return;
}
GeoPoint geoPoint = new GeoPoint(
(int) (myLocation.getLatitude() * 1E6),
(int) (myLocation.getLongitude() * 1E6));
Point pt = new Point();
mapView.getProjection().toPixels(geoPoint, pt);
canvas.save();
canvas.translate(pt.x - (arrowWidth / 2), pt.y - (arrowHeight / 2));
arrow[lastHeading].draw(canvas);
canvas.restore();
// Draw the error circle:
float radius =
mapView.getProjection().metersToEquatorPixels(myLocation.getAccuracy());
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
// Draw the current location
drawMyLocation(canvas, projection);
}
public void drawTrack(Canvas canvas, MapView mapView, Track track,
boolean selected) {
if (track == null) {
return;
}
ArrayList<Location> points = track.getLocations();
if (points.size() < 2) {
return;
}
// Get the current viewing window:
// Visible for testing.
Projection getMapProjection(MapView mapView) {
return mapView.getProjection();
}
// Visible for testing.
Rect getMapViewRect(MapView mapView) {
int w = mapView.getLongitudeSpan();
int h = mapView.getLatitudeSpan();
int cx = mapView.getMapCenter().getLongitudeE6();
int cy = mapView.getMapCenter().getLatitudeE6();
Rect rect = new Rect(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2);
return new Rect(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2);
}
// Visible for testing.
Path newPath() {
return new Path();
}
// Visible for testing.
Path getLastPath() {
return lastPath;
}
Point pt = new Point();
GeoPoint geoPoint;
Location loc;
Location lastValidLocation = null;
Path path;
boolean wasInside;
// Do as much allocation and preparation outside the loop over track
// points:
wasInside = false;
int locLon = 0, locLat = 0, minLon, maxLon, minLat, maxLat;
int lastLocLon = (int) (points.get(0).getLongitude() * 1E6);
int lastLocLat = (int) (points.get(0).getLatitude() * 1E6);
// Line decimation for dummies:
// Skip di additional points, where di depends on zoom level:
int di = 0;
int dl = 17 - mapView.getZoomLevel();
if (dl > 0) {
di += (dl * dl);
}
// Loop over track points:
path = new Path();
for (int i = 1; i < points.size(); i++) {
loc = points.get(i);
if (loc == null) {
continue;
}
locLon = (int) (loc.getLongitude() * 1E6);
locLat = (int) (loc.getLatitude() * 1E6);
// Skip to every n-th point (depends on zoom level, see above):
for (int j = 0; j < di && i < points.size() - 1; j++) {
// TODO Check the thread synchronization.
// There is no reason that points.get(i + 1) should be null but users
// have reported it causing null pointer exceptions.
if ((locLat > 90E6) ||
(lastLocLat > 90E6) ||
(points.get(i + 1) == null) ||
(points.get(i + 1).getLatitude() > 90)) {
break;
}
i++;
loc = points.get(i);
locLon = (int) (loc.getLongitude() * 1E6);
locLat = (int) (loc.getLatitude() * 1E6);
}
// Draw a line segment if it's inside the viewing window:
if (locLat < 90E6 && lastLocLat < 90E6) {
lastValidLocation = loc;
minLon = Math.min(locLon, lastLocLon);
maxLon = Math.max(locLon, lastLocLon);
minLat = Math.min(locLat, lastLocLat);
maxLat = Math.max(locLat, lastLocLat);
if (rect.intersects(minLon, minLat, maxLon, maxLat)) {
if (!wasInside) {
geoPoint = new GeoPoint(lastLocLat, lastLocLon);
mapView.getProjection().toPixels(geoPoint, pt);
path.moveTo(pt.x, pt.y);
}
geoPoint = new GeoPoint(locLat, locLon);
mapView.getProjection().toPixels(geoPoint, pt);
path.lineTo(pt.x, pt.y);
wasInside = rect.contains(locLon, locLat);
}
} else {
wasInside = false;
}
lastLocLon = locLon;
lastLocLat = locLat;
}
canvas.drawPath(path, selectedTrackPaint);
// Draw the "End" marker:
Location currentLastValidLocation = lastValidLocation;
if (showEndMarker && currentLastValidLocation != null) {
canvas.save();
geoPoint = MyTracksUtils.getGeoPoint(currentLastValidLocation);
mapView.getProjection().toPixels(geoPoint, pt);
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
endMarker.draw(canvas);
canvas.restore();
}
// Draw the "Start" marker:
for (int i = 0; i < points.size(); i++) {
loc = points.get(i);
if (loc.getLatitude() < 90) {
locLon = (int) (loc.getLongitude() * 1E6);
locLat = (int) (loc.getLatitude() * 1E6);
geoPoint = new GeoPoint(locLat, locLon);
mapView.getProjection().toPixels(geoPoint, pt);
canvas.save();
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
startMarker.draw(canvas);
canvas.restore();
break;
private void drawWaypoints(Canvas canvas, Projection projection) {
synchronized (waypoints) {;
for (Waypoint wpt : waypoints) {
Location loc = wpt.getLocation();
drawElement(canvas, projection, MyTracksUtils.getGeoPoint(loc),
wpt.getType() == Waypoint.TYPE_STATISTICS ? statsMarker
: waypointMarker, -(markerWidth / 2) + 3, -markerHeight);
}
}
}
@Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow,
long when) {
draw(canvas, mapView, shadow);
return false;
private void drawMyLocation(Canvas canvas, Projection projection) {
// Draw the arrow icon.
if (myLocation == null) {
return;
}
Point pt = drawElement(canvas, projection,
MyTracksUtils.getGeoPoint(myLocation), arrows[lastHeading],
-(arrowWidth / 2) + 3, -(arrowHeight / 2));
// Draw the error circle.
float radius = projection.metersToEquatorPixels(myLocation.getAccuracy());
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
}
private void drawTrack(Canvas canvas, Projection projection, Rect viewRect) {
Path path;
synchronized (points) {
// Merge the pending points with the list of cached locations.
final GeoPoint referencePoint = projection.fromPixels(0, 0);
int newPoints = pendingPoints.drainTo(points);
boolean newProjection = !viewRect.equals(lastViewRect) ||
!referencePoint.equals(lastReferencePoint);
if (newPoints == 0 && lastPath != null && !newProjection) {
// No need to recreate path (same points and viewing area).
path = lastPath;
} else {
int numPoints = points.size();
if (numPoints < 2) {
// Not enough points to draw a path.
path = null;
} else if (lastPath != null && !newProjection) {
// Incremental update of the path, without repositioning the view.
path = lastPath;
updatePath(projection, viewRect, path, numPoints - newPoints);
} else {
// The view has changed so we have to start from scratch.
path = newPath();
path.incReserve(numPoints);
updatePath(projection, viewRect, path, 0);
}
lastPath = path;
}
lastReferencePoint = referencePoint;
lastViewRect = viewRect;
}
if (path != null) {
canvas.drawPath(path, selectedTrackPaint);
}
// Draw the "End" marker.
if (showEndMarker) {
for (int i = points.size() - 1; i >= 0; --i) {
if (points.get(i).valid) {
drawElement(canvas, projection, points.get(i).geoPoint, endMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
// Draw the "Start" marker.
for (int i = 0; i < points.size(); ++i) {
if (points.get(i).valid) {
drawElement(canvas, projection, points.get(i).geoPoint, startMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
private void updatePath(Projection projection, Rect viewRect, Path path,
int startLocationIdx) {
// Whether to start a new segment on new valid and visible point.
boolean newSegment =
startLocationIdx > 0 ? !points.get(startLocationIdx - 1).valid : true;
boolean lastVisible = !newSegment;
final Point pt = new Point();
// Loop over track points.
for (int i = startLocationIdx; i < points.size(); ++i) {
CachedLocation loc = points.get(i);
// Check if valid, if not then indicate a new segment.
if (!loc.valid) {
newSegment = true;
continue;
}
final GeoPoint geoPoint = loc.geoPoint;
// Check if this breaks the existing segment.
boolean visible = alwaysVisible || viewRect.contains(
geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
if (!visible && !lastVisible) {
// This is a point outside view not connected to a visible one.
newSegment = true;
}
lastVisible = visible;
// Either move to beginning of a new segment or continue the old one.
projection.toPixels(geoPoint, pt);
if (newSegment) {
path.moveTo(pt.x, pt.y);
newSegment = false;
} else {
path.lineTo(pt.x, pt.y);
}
}
}
// Visible for testing.
Point drawElement(Canvas canvas, Projection projection, GeoPoint geoPoint,
Drawable element, int offsetX, int offsetY) {
Point pt = new Point();
projection.toPixels(geoPoint, pt);
canvas.save();
canvas.translate(pt.x + offsetX, pt.y + offsetY);
element.draw(canvas);
canvas.restore();
return pt;
}
/**
@@ -359,20 +426,22 @@ public class MyTracksOverlay extends Overlay {
final Location tapLocation = MyTracksUtils.getLocation(p);
double dmin = Double.MAX_VALUE;
Waypoint waypoint = null;
for (int i = 0; i < waypoints.size(); i++) {
final Location waypointLocation = waypoints.get(i).getLocation();
if (waypointLocation == null) {
continue;
}
final double d = waypointLocation.distanceTo(tapLocation);
if (d < dmin) {
dmin = d;
waypoint = waypoints.get(i);
synchronized (waypoints) {
for (int i = 0; i < waypoints.size(); i++) {
final Location waypointLocation = waypoints.get(i).getLocation();
if (waypointLocation == null) {
continue;
}
final double d = waypointLocation.distanceTo(tapLocation);
if (d < dmin) {
dmin = d;
waypoint = waypoints.get(i);
}
}
}
if (waypoint != null
&& dmin < 15000000 / Math.pow(2, mapView.getZoomLevel())) {
if (waypoint != null &&
dmin < 15000000 / Math.pow(2, mapView.getZoomLevel())) {
Intent intent = new Intent(context, MyTracksWaypointDetails.class);
intent.putExtra("waypointid", waypoint.getId());
context.startActivity(intent);
@@ -0,0 +1,57 @@
/*
* 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;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static com.google.android.apps.mytracks.MyTracksConstants.RESUME_TRACK_EXTRA_NAME;
import static com.google.android.apps.mytracks.MyTracksConstants.TAG;
import com.google.android.apps.mytracks.services.TrackRecordingService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
* This class handles MyTracks related broadcast messages.
*
* One example of a broadcast message that this class is interested in,
* is notification about the phone boot. We may want to resume a previously
* started tracking session if the phone crashed (hopefully not), or the user
* decided to swap the battery or some external event occurred which forced
* a phone reboot.
*
* This class simply delegates to {@link TrackRecordingService} to make a
* decision whether to continue with the previous track (if any), or just
* abandon it.
*
* @author Bartlomiej Niechwiej
*/
public class MyTracksReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "MyTracksReceiver.onReceive: " + intent.getAction());
if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Intent startIntent = new Intent(context, TrackRecordingService.class);
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
context.startService(startIntent);
} else {
Log.w(TAG, "MyTracksReceiver: unsupported action");
}
}
}
@@ -1,12 +1,12 @@
/*
* 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
@@ -15,17 +15,21 @@
*/
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.services.SafeStatusAnnouncerTask;
import com.google.android.apps.mytracks.io.backup.BackupActivityHelper;
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.maps.mytracks.R;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.util.Log;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
/**
* An activity that let's the user see and edit the settings.
@@ -37,29 +41,10 @@ public class MyTracksSettings extends PreferenceActivity {
public static final String SETTINGS_NAME = "MyTracksSettings";
public static final String ANNOUNCEMENT_FREQUENCY = "announcementFrequency";
public static final String DEFAULT_MAP_PUBLIC = "defaultMapPublic";
public static final String MAX_RECORDING_DISTANCE = "maxRecordingDistance";
public static final String METRIC_UNITS = "metricUnits";
public static final String MIN_RECORDING_DISTANCE = "minRecordingDistance";
public static final String MIN_RECORDING_INTERVAL = "minRecordingInterval";
public static final String MIN_REQUIRED_ACCURACY = "minRequiredAccuracy";
public static final String PICK_EXISTING_MAP = "pickExistingMap";
public static final String RECORDING_TRACK = "recordingTrack";
public static final String REPORT_SPEED = "reportSpeed";
public static final String SELECTED_TRACK = "selectedTrack";
public static final String SEND_STATS_AND_POINTS = "sendStatsAndPoints";
public static final String SEND_TO_DOCS = "sendToDocs";
public static final String SEND_TO_MYMAPS = "sendToMyMaps";
public static final String SHARE_URL_ONLY = "shareUrlOnly";
public static final String SIGNAL_SAMPLING_FREQUENCY =
"signalSamplingFrequency";
public static final String SPLIT_FREQUENCY = "splitFrequency";
public static final String WRITE_TO_SD_CARD = "writeToSdCard";
/*
* Default values - keep in sync with those in preferences.xml.
*/
public static final int DEFAULT_AUTO_RESUME_TRACK_TIMEOUT = 10; // In min.
public static final int DEFAULT_ANNOUNCEMENT_FREQUENCY = -1;
public static final int DEFAULT_MAX_RECORDING_DISTANCE = 200;
public static final int DEFAULT_MIN_RECORDING_DISTANCE = 5;
@@ -67,36 +52,38 @@ public class MyTracksSettings extends PreferenceActivity {
public static final int DEFAULT_MIN_REQUIRED_ACCURACY = 200;
public static final int DEFAULT_SPLIT_FREQUENCY = 0;
private static boolean mTTSAvailable;
/* establish whether the tts class is available to us */
static {
try {
SafeStatusAnnouncerTask.checkAvailable();
mTTSAvailable = true;
} catch (Throwable t) {
Log.d(MyTracksConstants.TAG, "TTS not available.", t);
mTTSAvailable = false;
}
}
private BackupPreferencesListener backupListener;
private SharedPreferences preferences;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// The volume we want to control is the Text-To-Speech volume
ApiFeatures apiFeatures = ApiFeatures.getInstance();
int volumeStream =
new StatusAnnouncerFactory(apiFeatures).getVolumeStream();
setVolumeControlStream(volumeStream);
// Tell it where to read/write preferences
PreferenceManager preferenceManager = getPreferenceManager();
preferenceManager.setSharedPreferencesName(SETTINGS_NAME);
preferenceManager.setSharedPreferencesMode(0);
// Set up automatic preferences backup
backupListener = BackupPreferencesListener.create(this, apiFeatures);
preferences = preferenceManager.getSharedPreferences();
preferences.registerOnSharedPreferenceChangeListener(backupListener);
// Load the preferences to be displayed
addPreferencesFromResource(R.xml.preferences);
// Hook up switching of displayed list entries between metric and imperial
// units
CheckBoxPreference metricUnitsPreference =
(CheckBoxPreference) findPreference(METRIC_UNITS);
(CheckBoxPreference) findPreference(
getString(R.string.metric_units_key));
metricUnitsPreference.setOnPreferenceChangeListener(
new OnPreferenceChangeListener() {
@Override
@@ -110,16 +97,70 @@ public class MyTracksSettings extends PreferenceActivity {
updatePreferenceUnits(metricUnitsPreference.isChecked());
// Disable TTS announcement preference if not available
if (!mTTSAvailable) {
if (!apiFeatures.hasTextToSpeech()) {
IntegerListPreference announcementFrequency =
(IntegerListPreference) findPreference(ANNOUNCEMENT_FREQUENCY);
(IntegerListPreference) findPreference(
getString(R.string.announcement_frequency_key));
announcementFrequency.setEnabled(false);
announcementFrequency.setValue("-1");
announcementFrequency.setSummary(
R.string.settings_announcement_not_available_summary);
R.string.settings_not_available_summary);
}
}
@Override
protected void onResume() {
super.onResume();
Preference backupNowPreference =
findPreference(getString(R.string.backup_to_sd_key));
Preference restoreNowPreference =
findPreference(getString(R.string.restore_from_sd_key));
// If recording, disable backup/restore
// (we don't want to get to inconsistent states)
boolean recording =
preferences.getLong(getString(R.string.recording_track_key), -1) != -1;
backupNowPreference.setEnabled(!recording);
restoreNowPreference.setEnabled(!recording);
backupNowPreference.setSummary(
recording ? R.string.settings_no_backup_while_recording
: R.string.settings_backup_to_sd_summary);
restoreNowPreference.setSummary(
recording ? R.string.settings_no_backup_while_recording
: R.string.settings_restore_from_sd_summary);
// Add actions to the backup preferences
backupNowPreference.setOnPreferenceClickListener(
new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
BackupActivityHelper backupHelper =
new BackupActivityHelper(MyTracksSettings.this);
backupHelper.writeBackup();
return true;
}
});
restoreNowPreference.setOnPreferenceClickListener(
new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
BackupActivityHelper backupHelper =
new BackupActivityHelper(MyTracksSettings.this);
backupHelper.restoreBackup();
return true;
}
});
}
@Override
protected void onDestroy() {
getPreferenceManager().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(backupListener);
super.onDestroy();
}
/**
* Updates all the preferences which give options with distance units to use
* the proper unit the user has selected.
@@ -128,14 +169,18 @@ public class MyTracksSettings extends PreferenceActivity {
*/
private void updatePreferenceUnits(boolean isMetric) {
final ListPreference minRecordingDistance =
(ListPreference) findPreference(MIN_RECORDING_DISTANCE);
(ListPreference) findPreference(
getString(R.string.min_recording_distance_key));
final ListPreference maxRecordingDistance =
(ListPreference) findPreference(MAX_RECORDING_DISTANCE);
(ListPreference) findPreference(
getString(R.string.max_recording_distance_key));
final ListPreference minRequiredAccuracy =
(ListPreference) findPreference(MIN_REQUIRED_ACCURACY);
(ListPreference) findPreference(
getString(R.string.min_required_accuracy_key));
final ListPreference splitFrequency =
(ListPreference) findPreference(SPLIT_FREQUENCY);
(ListPreference) findPreference(
getString(R.string.split_frequency_key));
minRecordingDistance.setEntries(isMetric
? R.array.min_recording_distance_options
: R.array.min_recording_distance_options_ft);
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
@@ -65,11 +66,11 @@ public class MyTracksWaypointDetails extends Activity
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
boolean useMetric =
preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true);
preferences.getBoolean(getString(R.string.metric_units_key), true);
utils.setMetricUnits(useMetric);
boolean displaySpeed =
preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true);
preferences.getBoolean(getString(R.string.report_speed_key), true);
utils.setReportSpeed(displaySpeed);
utils.updateWaypointUnits();
@@ -132,8 +133,8 @@ public class MyTracksWaypointDetails extends Activity
detailsView.setVisibility(View.GONE);
statsView.setVisibility(View.VISIBLE);
iconId = R.drawable.ylw_pushpin;
utils.setAllStats(waypoint);
utils.setTime(R.id.total_time_register, waypoint.getTotalTime());
TripStatistics waypointStats = waypoint.getStatistics();
utils.setAllStats(waypointStats);
utils.setAltitude(
R.id.elevation_register, waypoint.getLocation().getAltitude());
break;
@@ -139,9 +139,9 @@ public class MyTracksWaypointsList extends ListActivity
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
recordingTrackId =
preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
preferences.getLong(getString(R.string.recording_track_key), -1);
selectedTrackId =
preferences.getLong(MyTracksSettings.SELECTED_TRACK, -1);
preferences.getLong(getString(R.string.selected_track_key), -1);
}
boolean selectedRecording = selectedTrackId > 0
&& selectedTrackId == recordingTrackId;
@@ -168,16 +168,20 @@ public class MyTracksWaypointsList extends ListActivity
switch (v.getId()) {
case R.id.waypointslist_btn_insert_waypoint: {
long id = MyTracks.getInstance().insertWaypointMarker();
Intent intent = new Intent(this, MyTracksWaypointDetails.class);
intent.putExtra("waypointid", id);
startActivity(intent);
if (id >= 0) {
Intent intent = new Intent(this, MyTracksWaypointDetails.class);
intent.putExtra("waypointid", id);
startActivity(intent);
}
break;
}
case R.id.waypointslist_btn_insert_statistics: {
long id = MyTracks.getInstance().insertStatisticsMarker();
Intent intent = new Intent(this, MyTracksWaypointDetails.class);
intent.putExtra("waypointid", id);
startActivity(intent);
if (id >= 0) {
Intent intent = new Intent(this, MyTracksWaypointDetails.class);
intent.putExtra("waypointid", id);
startActivity(intent);
}
break;
}
@@ -15,9 +15,8 @@
*/
package com.google.android.apps.mytracks;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.MotionEvent;
@@ -50,21 +49,18 @@ public class NavControls {
/**
* A touchable image view.
* When touched it changes the navigation control icons accordingly.
*/
public class TouchLayout extends RelativeLayout {
private final ImageView arrow;
private class TouchLayout extends RelativeLayout implements Runnable {
private final boolean isLeft;
private final ImageView icon;
public TouchLayout(Context context, boolean isLeft) {
super(context);
arrow = new ImageView(context);
arrow.setImageDrawable(context.getResources().getDrawable(
isLeft ? R.drawable.btn_arrow_left : R.drawable.btn_arrow_right));
icon = new ImageView(context);
this.isLeft = isLeft;
this.icon = new ImageView(context);
icon.setVisibility(View.GONE);
addView(arrow);
addView(icon);
icon.setPadding((isLeft ? 15 : 10), 27, 15, 0);
}
public void setIcon(Drawable drawable) {
@@ -77,18 +73,11 @@ public class NavControls {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setPressed(true);
hide();
if (this == prevImage) {
if (prevRunnable != null) {
handler.post(prevRunnable);
return true;
}
} else if (this == nextImage) {
if (nextRunnable != null) {
handler.post(nextRunnable);
return true;
}
}
shiftIcons(isLeft);
// Call the user back
handler.post(this);
break;
case MotionEvent.ACTION_UP:
setPressed(false);
@@ -96,55 +85,69 @@ public class NavControls {
}
return super.onTouchEvent(event);
}
@Override
public void run() {
touchRunnable.run();
setPressed(false);
}
}
private final Context context;
private final Runnable prevRunnable;
private final Runnable nextRunnable;
private final Handler handler = new Handler();
private final Runnable dismissControls = new Runnable() {
public void run() {
hide();
}
};
private final TouchLayout prevImage;
private final TouchLayout nextImage;
private final TypedArray leftIcons;
private final TypedArray rightIcons;
private final Runnable touchRunnable;
private boolean isVisible = false;
private boolean hasNext = true;
private boolean hasPrev = true;
private int currentIcons;
public NavControls(Context context, ViewGroup container,
Runnable prevRunnable, Runnable nextRunnable) {
this.context = context;
this.prevRunnable = prevRunnable;
this.nextRunnable = nextRunnable;
TypedArray leftIcons, TypedArray rightIcons,
Runnable touchRunnable) {
this.leftIcons = leftIcons;
this.rightIcons = rightIcons;
this.touchRunnable = touchRunnable;
if (leftIcons.length() != rightIcons.length() || leftIcons.length() < 1) {
throw new IllegalArgumentException("Invalid icons specified");
}
if (touchRunnable == null) {
throw new NullPointerException("Runnable cannot be null");
}
LayoutParams prevParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
prevParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
prevParams.addRule(RelativeLayout.CENTER_VERTICAL);
prevImage = new TouchLayout(context, true);
prevImage.setLayoutParams(prevParams);
prevImage.setVisibility(View.INVISIBLE);
container.addView(prevImage);
LayoutParams nextParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
prevParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
nextParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
prevParams.addRule(RelativeLayout.CENTER_VERTICAL);
nextParams.addRule(RelativeLayout.CENTER_VERTICAL);
nextImage = new TouchLayout(context, false);
prevImage = new TouchLayout(context, true);
nextImage.setLayoutParams(nextParams);
prevImage.setLayoutParams(prevParams);
nextImage.setVisibility(View.INVISIBLE);
prevImage.setVisibility(View.INVISIBLE);
container.addView(prevImage);
container.addView(nextImage);
}
public void setHasNext(boolean hasNext) {
this.hasNext = hasNext;
}
public void setHasPrev(boolean hasPrev) {
this.hasPrev = hasPrev;
prevImage.setIcon(leftIcons.getDrawable(0));
nextImage.setIcon(rightIcons.getDrawable(0));
this.currentIcons = 0;
}
private void keepVisible() {
@@ -156,20 +159,18 @@ public class NavControls {
public void show() {
if (!isVisible) {
if (prevRunnable != null && hasPrev) {
SHOW_PREV_ANIMATION.setDuration(500);
SHOW_PREV_ANIMATION.startNow();
prevImage.setPressed(false);
prevImage.setAnimation(SHOW_PREV_ANIMATION);
prevImage.setVisibility(View.VISIBLE);
}
if (nextRunnable != null && hasNext) {
SHOW_NEXT_ANIMATION.setDuration(500);
SHOW_NEXT_ANIMATION.startNow();
nextImage.setPressed(false);
nextImage.setAnimation(SHOW_NEXT_ANIMATION);
nextImage.setVisibility(View.VISIBLE);
}
SHOW_PREV_ANIMATION.setDuration(500);
SHOW_PREV_ANIMATION.startNow();
prevImage.setPressed(false);
prevImage.setAnimation(SHOW_PREV_ANIMATION);
prevImage.setVisibility(View.VISIBLE);
SHOW_NEXT_ANIMATION.setDuration(500);
SHOW_NEXT_ANIMATION.startNow();
nextImage.setPressed(false);
nextImage.setAnimation(SHOW_NEXT_ANIMATION);
nextImage.setVisibility(View.VISIBLE);
isVisible = true;
keepVisible();
} else {
@@ -188,33 +189,26 @@ public class NavControls {
public void hide() {
isVisible = false;
if (prevRunnable != null) {
if (hasPrev) {
prevImage.setAnimation(HIDE_PREV_ANIMATION);
HIDE_PREV_ANIMATION.setDuration(500);
HIDE_PREV_ANIMATION.startNow();
} else {
prevImage.clearAnimation();
}
prevImage.setVisibility(View.INVISIBLE);
}
if (nextRunnable != null) {
if (hasNext) {
nextImage.setAnimation(HIDE_NEXT_ANIMATION);
HIDE_NEXT_ANIMATION.setDuration(500);
HIDE_NEXT_ANIMATION.startNow();
} else {
nextImage.clearAnimation();
}
nextImage.setVisibility(View.INVISIBLE);
}
prevImage.setAnimation(HIDE_PREV_ANIMATION);
HIDE_PREV_ANIMATION.setDuration(500);
HIDE_PREV_ANIMATION.startNow();
prevImage.setVisibility(View.INVISIBLE);
nextImage.setAnimation(HIDE_NEXT_ANIMATION);
HIDE_NEXT_ANIMATION.setDuration(500);
HIDE_NEXT_ANIMATION.startNow();
nextImage.setVisibility(View.INVISIBLE);
}
public void setLeftIcon(int resourceId) {
prevImage.setIcon(context.getResources().getDrawable(resourceId));
public int getCurrentIcons() {
return currentIcons;
}
public void setRightIcon(int resourceId) {
nextImage.setIcon(context.getResources().getDrawable(resourceId));
private void shiftIcons(boolean isLeft) {
// Increment or decrement by one, with wrap around
currentIcons = (currentIcons + leftIcons.length() +
(isLeft ? -1 : 1)) % leftIcons.length();
prevImage.setIcon(leftIcons.getDrawable(currentIcons));
nextImage.setIcon(rightIcons.getDrawable(currentIcons));
}
}
@@ -23,7 +23,8 @@ package com.google.android.apps.mytracks;
*/
public interface ProgressIndicator {
public void setProgressMessage(String text);
public void setProgressMessage(int resId);
public void clearProgressMessage();
public void setProgressValue(int percent);
@@ -61,7 +61,7 @@ public class SendToGoogleDialog extends Dialog {
Button cancel = (Button) findViewById(R.id.sendtogoogle_cancel);
cancel.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
MyTracks.getInstance().dismissDialog(MyTracks.DIALOG_SEND_TO_GOOGLE);
MyTracks.getInstance().dismissDialog(DialogManager.DIALOG_SEND_TO_GOOGLE);
}
});
@@ -105,17 +105,25 @@ public class SendToGoogleDialog extends Dialog {
getContext().getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (prefs != null) {
sendToMyMapsCheckBox.setChecked(
prefs.getBoolean(MyTracksSettings.SEND_TO_MYMAPS, true));
prefs.getBoolean(
getContext().getString(R.string.send_to_my_maps_key), true));
sendToDocsCheckBox.setChecked(
prefs.getBoolean(MyTracksSettings.SEND_TO_DOCS, true));
prefs.getBoolean(
getContext().getString(R.string.send_to_docs_key), true));
pickMapRadioButton.setChecked(
prefs.getBoolean(MyTracksSettings.PICK_EXISTING_MAP, false));
prefs.getBoolean(
getContext().getString(R.string.pick_existing_map_key), false));
createNewMapRadioButton.setChecked(
!prefs.getBoolean(MyTracksSettings.PICK_EXISTING_MAP, false));
!prefs.getBoolean(
getContext().getString(R.string.pick_existing_map_key), false));
sendStatsAndPointsRadioButton.setChecked(
prefs.getBoolean(MyTracksSettings.SEND_STATS_AND_POINTS, false));
prefs.getBoolean(
getContext().getString(R.string.send_stats_and_points_key),
false));
sendStatsRadioButton.setChecked(
!prefs.getBoolean(MyTracksSettings.SEND_STATS_AND_POINTS, false));
!prefs.getBoolean(
getContext().getString(R.string.send_stats_and_points_key),
false));
}
}
@@ -127,13 +135,16 @@ public class SendToGoogleDialog extends Dialog {
Editor editor = prefs.edit();
if (editor != null) {
editor.putBoolean(
MyTracksSettings.SEND_TO_MYMAPS, sendToMyMapsCheckBox.isChecked());
getContext().getString(R.string.send_to_my_maps_key),
sendToMyMapsCheckBox.isChecked());
editor.putBoolean(
MyTracksSettings.SEND_TO_DOCS, sendToDocsCheckBox.isChecked());
getContext().getString(R.string.send_to_docs_key),
sendToDocsCheckBox.isChecked());
editor.putBoolean(
MyTracksSettings.PICK_EXISTING_MAP, pickMapRadioButton.isChecked());
getContext().getString(R.string.pick_existing_map_key),
pickMapRadioButton.isChecked());
editor.putBoolean(
MyTracksSettings.SEND_STATS_AND_POINTS,
getContext().getString(R.string.send_stats_and_points_key),
sendStatsAndPointsRadioButton.isChecked());
editor.commit();
}
@@ -1,12 +1,12 @@
/*
* 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
@@ -15,8 +15,13 @@
*/
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.MyTracksProviderUtilsImpl;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
@@ -24,7 +29,6 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.ContentObserver;
import android.database.Cursor;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -46,31 +50,12 @@ import android.widget.Toast;
* @author Sandor Dornbush
*/
public class StatsActivity extends Activity
implements OnSharedPreferenceChangeListener, LocationListener {
implements OnSharedPreferenceChangeListener {
/**
* Columns we need from the provider
*/
private static final String[] PROJECTION_COLUMNS = {
TracksColumns._ID,
TracksColumns.STARTTIME,
TracksColumns.TOTALTIME,
TracksColumns.MOVINGTIME,
TracksColumns.TOTALDISTANCE,
TracksColumns.AVGSPEED,
TracksColumns.AVGMOVINGSPEED,
TracksColumns.MAXSPEED,
TracksColumns.MINELEVATION,
TracksColumns.MAXELEVATION,
TracksColumns.ELEVATIONGAIN,
TracksColumns.MINGRADE,
TracksColumns.MAXGRADE };
private final StatsUtilities utils;
private StatsUtilities utils;
private UIUpdateThread thread;
private ContentObserver observer;
private LocationManager locationManager;
/**
* The id of the currently selected track.
@@ -109,6 +94,8 @@ public class StatsActivity extends Activity
*/
private boolean showCurrentSegment = false;
private MyTracksProviderUtils providerUtils;
/**
* A runnable for posting to the UI thread. Will update the total time field.
*/
@@ -146,15 +133,19 @@ public class StatsActivity extends Activity
}
}
public StatsActivity() {
this.utils = new StatsUtilities(this);
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
utils = new StatsUtilities(this);
providerUtils = new MyTracksProviderUtilsImpl(getContentResolver());
// The volume we want to control is the Text-To-Speech volume
int volumeStream =
new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream();
setVolumeControlStream(volumeStream);
// We don't need a window title bar:
requestWindowFeature(Window.FEATURE_NO_TITLE);
@@ -176,21 +167,19 @@ public class StatsActivity extends Activity
SharedPreferences preferences =
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
selectedTrackId = preferences.getLong(MyTracksSettings.SELECTED_TRACK,
-1);
recordingTrackId = preferences.getLong(MyTracksSettings.RECORDING_TRACK,
-1);
metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS,
true);
selectedTrackId = preferences.getLong(
getString(R.string.selected_track_key), -1);
recordingTrackId = preferences.getLong(
getString(R.string.recording_track_key), -1);
metricUnits = preferences.getBoolean(
getString(R.string.metric_units_key), true);
displaySpeed =
preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true);
preferences.getBoolean(getString(R.string.report_speed_key), true);
checkLiveTrack();
restoreStats();
updateLocation(null);
showUnknownLocation();
preferences.registerOnSharedPreferenceChangeListener(this);
}
locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
utils.setMetricUnits(metricUnits);
utils.setReportSpeed(displaySpeed);
utils.updateUnits();
@@ -221,45 +210,57 @@ public class StatsActivity extends Activity
activityOnTop = true;
checkLiveTrack();
restoreStats();
updateLocation(null);
showUnknownLocation();
super.onResume();
}
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
final SharedPreferences sharedPreferences, final String key) {
Log.d(MyTracksConstants.TAG,
"StatsActivity: onSharedPreferences changed " + key);
if (key != null) {
if (key.equals(MyTracksSettings.SELECTED_TRACK)) {
selectedTrackId =
sharedPreferences.getLong(MyTracksSettings.SELECTED_TRACK, -1);
checkLiveTrack();
restoreStats();
updateLocation(null);
} else if (key.equals(MyTracksSettings.RECORDING_TRACK)) {
recordingTrackId =
sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1);
checkLiveTrack();
restoreStats();
updateLocation(null);
} else if (key.equals(MyTracksSettings.METRIC_UNITS)) {
metricUnits =
sharedPreferences.getBoolean(MyTracksSettings.METRIC_UNITS, true);
utils.setMetricUnits(metricUnits);
utils.updateUnits();
restoreStats();
} else if (key.equals(MyTracksSettings.REPORT_SPEED)) {
displaySpeed =
sharedPreferences.getBoolean(MyTracksSettings.REPORT_SPEED, true);
utils.setReportSpeed(displaySpeed);
utils.updateUnits();
utils.setSpeedLabel(
R.id.speed_label, R.string.speed, R.string.pace_label);
Log.w(MyTracksConstants.TAG, "Setting speed labels");
utils.setSpeedLabels();
restoreStats();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
if (key.equals(getString(R.string.selected_track_key))) {
selectedTrackId =
sharedPreferences.getLong(
getString(R.string.selected_track_key),
-1);
checkLiveTrack();
restoreStats();
showUnknownLocation();
} else if (key.equals(getString(R.string.recording_track_key))) {
recordingTrackId =
sharedPreferences.getLong(
getString(R.string.recording_track_key),
-1);
checkLiveTrack();
restoreStats();
showUnknownLocation();
} else if (key.equals(getString(R.string.metric_units_key))) {
metricUnits =
sharedPreferences.getBoolean(
getString(R.string.metric_units_key), true);
utils.setMetricUnits(metricUnits);
utils.updateUnits();
restoreStats();
} else if (key.equals(getString(R.string.report_speed_key))) {
displaySpeed =
sharedPreferences.getBoolean(
getString(R.string.report_speed_key),
true);
utils.setReportSpeed(displaySpeed);
utils.updateUnits();
utils.setSpeedLabel(
R.id.speed_label, R.string.speed, R.string.pace_label);
Log.w(MyTracksConstants.TAG, "Setting speed labels");
utils.setSpeedLabels();
restoreStats();
}
}
});
}
}
@@ -294,29 +295,36 @@ public class StatsActivity extends Activity
return super.onOptionsItemSelected(item);
}
@Override
public void onLocationChanged(Location l) {
if (selectedTrackIsRecording()) {
updateLocation(l);
private final LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location l) {
if (selectedTrackIsRecording()) {
showLocation(l);
}
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderDisabled(String provider) {
// Do nothing
}
@Override
public void onProviderEnabled(String provider) {
// Do nothing
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// Do nothing
}
};
/**
* Registers to receive location updates from the GPS location provider.
*/
private void registerLocationListener() {
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) {
LocationProvider gpsProvider =
locationManager.getProvider(MyTracksConstants.GPS_PROVIDER);
@@ -329,7 +337,7 @@ public class StatsActivity extends Activity
+ gpsProvider.getName());
}
locationManager.requestLocationUpdates(gpsProvider.getName(),
0/*minTime*/, 0/*minDist*/, this);
0/*minTime*/, 0/*minDist*/, locationListener);
}
}
@@ -337,8 +345,10 @@ public class StatsActivity extends Activity
* Unregisters all location listener.
*/
private void unregisterLocationListener() {
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (locationManager != null) {
locationManager.removeUpdates(this);
locationManager.removeUpdates(locationListener);
}
}
@@ -359,60 +369,18 @@ public class StatsActivity extends Activity
return;
}
Cursor cursor = null;
try {
cursor = getContentResolver().query(
showCurrentSegment
? WaypointsColumns.CONTENT_URI
: TracksColumns.CONTENT_URI,
PROJECTION_COLUMNS,
showCurrentSegment
? WaypointsColumns.TRACKID + "=" + selectedTrackId
: "_id=" + selectedTrackId,
null,
"_id LIMIT 1");
if (cursor != null && cursor.moveToFirst()) {
final int idxStartTime =
cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
final int idxTotalTime =
cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
final int idxMovingTime =
cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
final int idxTotalDistance =
cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
final int idxAverageSpeed =
cursor.getColumnIndexOrThrow(TracksColumns.AVGSPEED);
final int idxAverageMovingSpeed =
cursor.getColumnIndexOrThrow(TracksColumns.AVGMOVINGSPEED);
final int idxMaxSpeed =
cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
final int idxMinElevation =
cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION);
final int idxMaxElevation =
cursor.getColumnIndexOrThrow(TracksColumns.MAXELEVATION);
final int idxElevationGain =
cursor.getColumnIndexOrThrow(TracksColumns.ELEVATIONGAIN);
final int idxMinGrade =
cursor.getColumnIndexOrThrow(TracksColumns.MINGRADE);
final int idxMaxGrade =
cursor.getColumnIndexOrThrow(TracksColumns.MAXGRADE);
startTime = cursor.getLong(idxStartTime);
if (!selectedTrackIsRecording()) {
utils.setTime(R.id.total_time_register, cursor.getLong(idxTotalTime));
}
utils.setAllStats(cursor.getLong(idxMovingTime),
cursor.getFloat(idxTotalDistance), cursor.getFloat(idxAverageSpeed),
cursor.getFloat(idxAverageMovingSpeed),
cursor.getFloat(idxMaxSpeed), cursor.getFloat(idxMinElevation),
cursor.getFloat(idxMaxElevation), cursor.getFloat(idxElevationGain),
cursor.getFloat(idxMinGrade), cursor.getFloat(idxMaxGrade));
}
} finally {
if (cursor != null) {
cursor.close();
}
Track track = providerUtils.getTrack(selectedTrackId);
if (track == null || track.getStatistics() == null) {
utils.setAllToUnknown();
return;
}
startTime = track.getStatistics().getStartTime();
if (!selectedTrackIsRecording()) {
utils.setTime(R.id.total_time_register,
track.getStatistics().getTotalTime());
}
utils.setAllStats(track.getStatistics());
}
/**
@@ -458,17 +426,17 @@ public class StatsActivity extends Activity
*
* @param l may be null (will set location fields to unknown)
*/
private void updateLocation(Location l) {
if (l == null) {
utils.setUnknown(R.id.elevation_register);
utils.setUnknown(R.id.latitude_register);
utils.setUnknown(R.id.longitude_register);
utils.setUnknown(R.id.speed_register);
} else {
utils.setAltitude(R.id.elevation_register, l.getAltitude());
utils.setLatLong(R.id.latitude_register, l.getLatitude());
utils.setLatLong(R.id.longitude_register, l.getLongitude());
utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6);
}
private void showLocation(Location l) {
utils.setAltitude(R.id.elevation_register, l.getAltitude());
utils.setLatLong(R.id.latitude_register, l.getLatitude());
utils.setLatLong(R.id.longitude_register, l.getLongitude());
utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6);
}
private void showUnknownLocation() {
utils.setUnknown(R.id.elevation_register);
utils.setUnknown(R.id.latitude_register);
utils.setUnknown(R.id.longitude_register);
utils.setUnknown(R.id.speed_register);
}
}
@@ -15,19 +15,18 @@
*/
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import android.app.Activity;
import android.util.Log;
import android.widget.TextView;
import java.text.DecimalFormat;
import java.text.NumberFormat;
/**
* Various utility functions for views that display statistics information.
*
@@ -219,32 +218,20 @@ public class StatsUtilities {
setGrade(R.id.max_grade_register, maxGrade);
}
public void setAllStats(Waypoint wpt) {
setTime(R.id.moving_time_register, wpt.getMovingTime());
setDistance(R.id.total_distance_register, wpt.getTotalDistance() / 1000);
setSpeed(R.id.average_speed_register, wpt.getAverageSpeed() * 3.6);
public void setAllStats(TripStatistics stats) {
setTime(R.id.moving_time_register, stats.getMovingTime());
setDistance(R.id.total_distance_register, stats.getTotalDistance() / 1000);
setSpeed(R.id.average_speed_register, stats.getAverageSpeed() * 3.6);
setSpeed(R.id.average_moving_speed_register,
wpt.getAverageMovingSpeed() * 3.6);
setSpeed(R.id.max_speed_register, wpt.getMaxSpeed() * 3.6);
setAltitude(R.id.min_elevation_register, wpt.getMinElevation());
setAltitude(R.id.max_elevation_register, wpt.getMaxElevation());
setAltitude(R.id.elevation_gain_register, wpt.getTotalElevationGain());
setGrade(R.id.min_grade_register, wpt.getMinGrade());
setGrade(R.id.max_grade_register, wpt.getMaxGrade());
}
public void setAllStats(Track track) {
setTime(R.id.moving_time_register, track.getMovingTime());
setDistance(R.id.total_distance_register, track.getTotalDistance() / 1000);
setSpeed(R.id.average_speed_register, track.getAverageSpeed() * 3.6);
setSpeed(R.id.average_moving_speed_register,
track.getAverageMovingSpeed() * 3.6);
setSpeed(R.id.max_speed_register, track.getMaxSpeed() * 3.6);
setAltitude(R.id.min_elevation_register, track.getMinElevation());
setAltitude(R.id.max_elevation_register, track.getMaxElevation());
setAltitude(R.id.elevation_gain_register, track.getTotalElevationGain());
setGrade(R.id.min_grade_register, track.getMinGrade());
setGrade(R.id.max_grade_register, track.getMaxGrade());
stats.getAverageMovingSpeed() * 3.6);
setSpeed(R.id.max_speed_register, stats.getMaxSpeed() * 3.6);
setAltitude(R.id.min_elevation_register, stats.getMinElevation());
setAltitude(R.id.max_elevation_register, stats.getMaxElevation());
setAltitude(R.id.elevation_gain_register, stats.getTotalElevationGain());
setGrade(R.id.min_grade_register, stats.getMinGrade());
setGrade(R.id.max_grade_register, stats.getMaxGrade());
setTime(R.id.total_time_register, stats.getTotalTime());
}
public void setSpeedLabel(int id, int speedString, int paceString) {
@@ -19,12 +19,17 @@ import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.AlertDialog;
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;
import android.view.Window;
import android.webkit.WebView;
import android.widget.TextView;
/**
* An activity that displays a welcome screen.
@@ -72,5 +77,16 @@ 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);
}
}
}
@@ -0,0 +1,32 @@
/*
* 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.content;
/**
* Utilities for serializing primitive types.
*
* @author Rodrigo Damazio
*/
public class ContentTypeIds {
public static final byte BOOLEAN_TYPE_ID = 0;
public static final byte LONG_TYPE_ID = 1;
public static final byte INT_TYPE_ID = 2;
public static final byte FLOAT_TYPE_ID = 3;
public static final byte DOUBLE_TYPE_ID = 4;
public static final byte STRING_TYPE_ID = 5;
private ContentTypeIds() { /* Not instantiable */ }
}
@@ -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);
@@ -15,11 +15,14 @@
*/
package com.google.android.apps.mytracks.content;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import java.util.List;
/**
* Utility to access data from the mytracks content provider.
*
@@ -172,7 +175,7 @@ public interface MyTracksProviderUtils {
* @return A cursor over the selected range of locations
*/
Cursor getWaypointsCursor(long trackId, long minWaypointId,
long maxWaypoints);
int maxWaypoints);
/**
* Finds a track by given unique track id.
@@ -183,6 +186,15 @@ public interface MyTracksProviderUtils {
* @return a Track object, or null if not found
*/
Track getTrack(long id);
/**
* Retrieves all tracks without track points. If no tracks exist an empty
* list will be returned. Use {@link #getTrackPoints(Track, int)} to load
* the track points.
*
* @return a list of all the recorded tracks
*/
List<Track> getAllTracks();
/**
* Loads the track points for a given track.
@@ -194,17 +206,6 @@ public interface MyTracksProviderUtils {
*/
long getTrackPoints(Track track, int maxPoints);
/**
* Fetches some number of locations for the given track.
*
* This is designed to be used to stream through large tracks without loading
* all points into memory.
*
* @param track to load locations for
* @param buffer an array of locations to fill
*/
void getTrackPoints(Track track, TrackBuffer buffer);
/**
* Creates a cursor over the tracks provider with a given selection.
* Caller gets to own the returned cursor. Don't forget to close it.
@@ -232,6 +233,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 +252,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.
*
@@ -264,13 +268,39 @@ public interface MyTracksProviderUtils {
*/
void updateTrack(Track track);
/**
* Creates a Track object from a given cursor.
*
* @param cursor a cursor pointing at a db or provider with tracks
* @return a new Track object
*/
Track createTrack(Cursor cursor);
/**
* Creates the ContentValues for a given Track object.
*
* Note: If the track has an id<0 the id column will not be filled.
*
* @param track a given track object
* @return a filled in ContentValues object
*/
ContentValues createContentValues(Track track);
/**
* Creates a location object from a given cursor.
*
* @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);
/**
* Fill a location object with values from a given cursor.
*
* @param cursor a cursor pointing at a db or provider with locations
* @param location a location object to be overwritten
*/
void fillLocation(Cursor cursor, Location location);
/**
* Creates a waypoint object from a given cursor.
@@ -278,7 +308,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},
@@ -315,7 +345,7 @@ public interface MyTracksProviderUtils {
* Creates an instance of {@link MyTracksProviderUtils}.
*/
protected MyTracksProviderUtils newForContext(Context context) {
return new MyTracksProviderUtilsImpl(context);
return new MyTracksProviderUtilsImpl(context.getContentResolver());
}
}
}
@@ -15,13 +15,18 @@
*/
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.stats.TripStatistics;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Helper class providing easy access to locations and tracks in the
* MyTracksProvider. All static members.
@@ -30,10 +35,10 @@ import android.util.Log;
*/
public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
private final Context context;
private final ContentResolver contentResolver;
public MyTracksProviderUtilsImpl(Context context) {
this.context = context;
public MyTracksProviderUtilsImpl(ContentResolver contentResolver) {
this.contentResolver = contentResolver;
}
/**
@@ -72,16 +77,11 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
return values;
}
/**
* Creates the ContentValues for a given Track object.
*
* Note: If the track has an id<0 the id column will not be filled.
*
* @param track a given track object
* @return a filled in ContentValues object
*/
private static ContentValues createContentValues(Track track) {
@Override
public ContentValues createContentValues(Track track) {
ContentValues values = new ContentValues();
TripStatistics stats = track.getStatistics();
// Values id < 0 indicate no id is available:
if (track.getId() >= 0) {
values.put(TracksColumns._ID, track.getId());
@@ -92,29 +92,31 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
values.put(TracksColumns.CATEGORY, track.getCategory());
values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints());
values.put(TracksColumns.STARTID, track.getStartId());
values.put(TracksColumns.STARTTIME, track.getStartTime());
values.put(TracksColumns.STOPTIME, track.getStopTime());
values.put(TracksColumns.STARTTIME, stats.getStartTime());
values.put(TracksColumns.STOPTIME, stats.getStopTime());
values.put(TracksColumns.STOPID, track.getStopId());
values.put(TracksColumns.TOTALDISTANCE, track.getTotalDistance());
values.put(TracksColumns.TOTALTIME, track.getTotalTime());
values.put(TracksColumns.MOVINGTIME, track.getMovingTime());
values.put(TracksColumns.MAXLAT, track.getTop());
values.put(TracksColumns.MINLAT, track.getBottom());
values.put(TracksColumns.MAXLON, track.getRight());
values.put(TracksColumns.MINLON, track.getLeft());
values.put(TracksColumns.AVGSPEED, track.getAverageSpeed());
values.put(TracksColumns.AVGMOVINGSPEED, track.getAverageMovingSpeed());
values.put(TracksColumns.MAXSPEED, track.getMaxSpeed());
values.put(TracksColumns.MINELEVATION, track.getMinElevation());
values.put(TracksColumns.MAXELEVATION, track.getMaxElevation());
values.put(TracksColumns.ELEVATIONGAIN, track.getTotalElevationGain());
values.put(TracksColumns.MINGRADE, track.getMinGrade());
values.put(TracksColumns.MAXGRADE, track.getMaxGrade());
values.put(TracksColumns.TOTALDISTANCE, stats.getTotalDistance());
values.put(TracksColumns.TOTALTIME, stats.getTotalTime());
values.put(TracksColumns.MOVINGTIME, stats.getMovingTime());
values.put(TracksColumns.MAXLAT, stats.getTop());
values.put(TracksColumns.MINLAT, stats.getBottom());
values.put(TracksColumns.MAXLON, stats.getRight());
values.put(TracksColumns.MINLON, stats.getLeft());
values.put(TracksColumns.AVGSPEED, stats.getAverageSpeed());
values.put(TracksColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed());
values.put(TracksColumns.MAXSPEED, stats.getMaxSpeed());
values.put(TracksColumns.MINELEVATION, stats.getMinElevation());
values.put(TracksColumns.MAXELEVATION, stats.getMaxElevation());
values.put(TracksColumns.ELEVATIONGAIN, stats.getTotalElevationGain());
values.put(TracksColumns.MINGRADE, stats.getMinGrade());
values.put(TracksColumns.MAXGRADE, stats.getMaxGrade());
return values;
}
private static ContentValues createContentValues(Waypoint waypoint) {
ContentValues values = new ContentValues();
TripStatistics stats = waypoint.getStatistics();
// Values id < 0 indicate no id is available:
if (waypoint.getId() >= 0) {
values.put(WaypointsColumns._ID, waypoint.getId());
@@ -127,23 +129,21 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
values.put(WaypointsColumns.TYPE, waypoint.getType());
values.put(WaypointsColumns.LENGTH, waypoint.getLength());
values.put(WaypointsColumns.DURATION, waypoint.getDuration());
values.put(WaypointsColumns.STARTTIME, waypoint.getStartTime());
values.put(WaypointsColumns.STARTTIME, stats.getStartTime());
values.put(WaypointsColumns.STARTID, waypoint.getStartId());
values.put(WaypointsColumns.STOPID, waypoint.getStopId());
values.put(WaypointsColumns.TOTALDISTANCE, waypoint.getTotalDistance());
values.put(WaypointsColumns.TOTALTIME, waypoint.getTotalTime());
values.put(WaypointsColumns.MOVINGTIME, waypoint.getMovingTime());
values.put(WaypointsColumns.AVGSPEED, waypoint.getAverageSpeed());
values.put(WaypointsColumns.AVGMOVINGSPEED,
waypoint.getAverageMovingSpeed());
values.put(WaypointsColumns.MAXSPEED, waypoint.getMaxSpeed());
values.put(WaypointsColumns.MINELEVATION, waypoint.getMinElevation());
values.put(WaypointsColumns.MAXELEVATION, waypoint.getMaxElevation());
values.put(WaypointsColumns.ELEVATIONGAIN,
waypoint.getTotalElevationGain());
values.put(WaypointsColumns.MINGRADE, waypoint.getMinGrade());
values.put(WaypointsColumns.MAXGRADE, waypoint.getMaxGrade());
values.put(WaypointsColumns.TOTALDISTANCE, stats.getTotalDistance());
values.put(WaypointsColumns.TOTALTIME, stats.getTotalTime());
values.put(WaypointsColumns.MOVINGTIME, stats.getMovingTime());
values.put(WaypointsColumns.AVGSPEED, stats.getAverageSpeed());
values.put(WaypointsColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed());
values.put(WaypointsColumns.MAXSPEED, stats.getMaxSpeed());
values.put(WaypointsColumns.MINELEVATION, stats.getMinElevation());
values.put(WaypointsColumns.MAXELEVATION, stats.getMaxElevation());
values.put(WaypointsColumns.ELEVATIONGAIN, stats.getTotalElevationGain());
values.put(WaypointsColumns.MINGRADE, stats.getMinGrade());
values.put(WaypointsColumns.MAXGRADE, stats.getMaxGrade());
Location location = waypoint.getLocation();
if (location != null) {
@@ -171,6 +171,15 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public Location createLocation(Cursor cursor) {
Location location = new Location("");
fillLocation(cursor, location);
return location;
}
@Override
public void fillLocation(Cursor cursor, Location location) {
location.reset();
int idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
int idxLongitude =
cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
@@ -180,7 +189,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
Location location = new Location("");
if (!cursor.isNull(idxLatitude)) {
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
}
@@ -202,16 +210,10 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
if (!cursor.isNull(idxAccuracy)) {
location.setAccuracy(cursor.getFloat(idxAccuracy));
}
return location;
}
/**
* Creates a Track object from a given cursor.
*
* @param cursor a cursor pointing at a db or provider with tracks
* @return a new Track object
*/
private static Track createTrack(Cursor cursor) {
@Override
public Track createTrack(Cursor cursor) {
int idxId = cursor.getColumnIndexOrThrow(TracksColumns._ID);
int idxName = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
int idxDescription =
@@ -232,9 +234,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
int idxTotalTime = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
int idxMovingTime = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
int idxAverageSpeed = cursor.getColumnIndexOrThrow(TracksColumns.AVGSPEED);
int idxAverageMovingSpeed =
cursor.getColumnIndexOrThrow(TracksColumns.AVGMOVINGSPEED);
int idxMaxSpeed = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
int idxMinElevation =
cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION);
@@ -246,6 +245,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
int idxMaxGrade = cursor.getColumnIndexOrThrow(TracksColumns.MAXGRADE);
Track track = new Track();
TripStatistics stats = track.getStatistics();
if (!cursor.isNull(idxId)) {
track.setId(cursor.getLong(idxId));
}
@@ -265,10 +265,10 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
track.setStartId(cursor.getInt(idxStartId));
}
if (!cursor.isNull(idxStartTime)) {
track.setStartTime(cursor.getLong(idxStartTime));
stats.setStartTime(cursor.getLong(idxStartTime));
}
if (!cursor.isNull(idxStopTime)) {
track.setStopTime(cursor.getLong(idxStopTime));
stats.setStopTime(cursor.getLong(idxStopTime));
}
if (!cursor.isNull(idxStopId)) {
track.setStopId(cursor.getInt(idxStopId));
@@ -277,13 +277,13 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
track.setNumberOfPoints(cursor.getInt(idxNumPoints));
}
if (!cursor.isNull(idxTotalDistance)) {
track.setTotalDistance(cursor.getFloat(idxTotalDistance));
stats.setTotalDistance(cursor.getFloat(idxTotalDistance));
}
if (!cursor.isNull(idxTotalTime)) {
track.setTotalTime(cursor.getLong(idxTotalTime));
stats.setTotalTime(cursor.getLong(idxTotalTime));
}
if (!cursor.isNull(idxMovingTime)) {
track.setMovingTime(cursor.getLong(idxMovingTime));
stats.setMovingTime(cursor.getLong(idxMovingTime));
}
if (!cursor.isNull(idxMaxlat)
&& !cursor.isNull(idxMinlat)
@@ -293,31 +293,25 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
int bottom = cursor.getInt(idxMinlat);
int right = cursor.getInt(idxMaxlon);
int left = cursor.getInt(idxMinlon);
track.setBounds(left, top, right, bottom);
}
if (!cursor.isNull(idxAverageSpeed)) {
track.setAverageSpeed(cursor.getFloat(idxAverageSpeed));
}
if (!cursor.isNull(idxAverageMovingSpeed)) {
track.setAverageMovingSpeed(cursor.getFloat(idxAverageMovingSpeed));
stats.setBounds(left, top, right, bottom);
}
if (!cursor.isNull(idxMaxSpeed)) {
track.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
stats.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
}
if (!cursor.isNull(idxMinElevation)) {
track.setMinElevation(cursor.getFloat(idxMinElevation));
stats.setMinElevation(cursor.getFloat(idxMinElevation));
}
if (!cursor.isNull(idxMaxElevation)) {
track.setMaxElevation(cursor.getFloat(idxMaxElevation));
stats.setMaxElevation(cursor.getFloat(idxMaxElevation));
}
if (!cursor.isNull(idxElevationGain)) {
track.setTotalElevationGain(cursor.getFloat(idxElevationGain));
stats.setTotalElevationGain(cursor.getFloat(idxElevationGain));
}
if (!cursor.isNull(idxMinGrade)) {
track.setMinGrade(cursor.getFloat(idxMinGrade));
stats.setMinGrade(cursor.getFloat(idxMinGrade));
}
if (!cursor.isNull(idxMaxGrade)) {
track.setMaxGrade(cursor.getFloat(idxMaxGrade));
stats.setMaxGrade(cursor.getFloat(idxMaxGrade));
}
return track;
}
@@ -343,10 +337,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
int idxTotalTime = cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALTIME);
int idxMovingTime =
cursor.getColumnIndexOrThrow(WaypointsColumns.MOVINGTIME);
int idxAverageSpeed =
cursor.getColumnIndexOrThrow(WaypointsColumns.AVGSPEED);
int idxAverageMovingSpeed =
cursor.getColumnIndexOrThrow(WaypointsColumns.AVGMOVINGSPEED);
int idxMaxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXSPEED);
int idxMinElevation =
cursor.getColumnIndexOrThrow(WaypointsColumns.MINELEVATION);
@@ -366,6 +356,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
int idxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.SPEED);
Waypoint waypoint = new Waypoint();
TripStatistics stats = waypoint.getStatistics();
if (!cursor.isNull(idxId)) {
waypoint.setId(cursor.getLong(idxId));
}
@@ -394,7 +386,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
waypoint.setDuration(cursor.getLong(idxDuration));
}
if (!cursor.isNull(idxStartTime)) {
waypoint.setStartTime(cursor.getLong(idxStartTime));
stats.setStartTime(cursor.getLong(idxStartTime));
}
if (!cursor.isNull(idxStartId)) {
waypoint.setStartId(cursor.getLong(idxStartId));
@@ -403,37 +395,31 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
waypoint.setStopId(cursor.getLong(idxStopId));
}
if (!cursor.isNull(idxTotalDistance)) {
waypoint.setTotalDistance(cursor.getFloat(idxTotalDistance));
stats.setTotalDistance(cursor.getFloat(idxTotalDistance));
}
if (!cursor.isNull(idxTotalTime)) {
waypoint.setTotalTime(cursor.getLong(idxTotalTime));
stats.setTotalTime(cursor.getLong(idxTotalTime));
}
if (!cursor.isNull(idxMovingTime)) {
waypoint.setMovingTime(cursor.getLong(idxMovingTime));
}
if (!cursor.isNull(idxAverageSpeed)) {
waypoint.setAverageSpeed(cursor.getFloat(idxAverageSpeed));
}
if (!cursor.isNull(idxAverageMovingSpeed)) {
waypoint.setAverageMovingSpeed(cursor.getFloat(idxAverageMovingSpeed));
stats.setMovingTime(cursor.getLong(idxMovingTime));
}
if (!cursor.isNull(idxMaxSpeed)) {
waypoint.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
stats.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
}
if (!cursor.isNull(idxMinElevation)) {
waypoint.setMinElevation(cursor.getFloat(idxMinElevation));
stats.setMinElevation(cursor.getFloat(idxMinElevation));
}
if (!cursor.isNull(idxMaxElevation)) {
waypoint.setMaxElevation(cursor.getFloat(idxMaxElevation));
stats.setMaxElevation(cursor.getFloat(idxMaxElevation));
}
if (!cursor.isNull(idxElevationGain)) {
waypoint.setTotalElevationGain(cursor.getFloat(idxElevationGain));
stats.setTotalElevationGain(cursor.getFloat(idxElevationGain));
}
if (!cursor.isNull(idxMinGrade)) {
waypoint.setMinGrade(cursor.getFloat(idxMinGrade));
stats.setMinGrade(cursor.getFloat(idxMinGrade));
}
if (!cursor.isNull(idxMaxGrade)) {
waypoint.setMaxGrade(cursor.getFloat(idxMaxGrade));
stats.setMaxGrade(cursor.getFloat(idxMaxGrade));
}
Location location = new Location("");
@@ -462,25 +448,22 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public void deleteAllTracks() {
context.getContentResolver().delete(TracksColumns.CONTENT_URI, null, null);
context.getContentResolver().delete(TrackPointsColumns.CONTENT_URI,
null, null);
context.getContentResolver().delete(
WaypointsColumns.CONTENT_URI, null, null);
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
contentResolver.delete(TrackPointsColumns.CONTENT_URI, null, null);
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
}
@Override
public void deleteTrack(long trackId) {
Track track = getTrack(trackId);
if (track != null) {
context.getContentResolver().delete(TrackPointsColumns.CONTENT_URI,
contentResolver.delete(TrackPointsColumns.CONTENT_URI,
"_id>=" + track.getStartId() + " AND _id<=" + track.getStopId(),
null);
}
context.getContentResolver().delete(WaypointsColumns.CONTENT_URI,
contentResolver.delete(WaypointsColumns.CONTENT_URI,
WaypointsColumns.TRACKID + "=" + trackId, null);
context.getContentResolver().delete(
TracksColumns.CONTENT_URI, "_id=" + trackId, null);
contentResolver.delete(TracksColumns.CONTENT_URI, "_id=" + trackId, null);
}
@Override
@@ -494,7 +477,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
if (nextWaypoint != null) {
Log.d(MyTracksProvider.TAG, "Correcting marker " + nextWaypoint.getId()
+ " after deleted marker " + deletedWaypoint.getId());
nextWaypoint.combine(deletedWaypoint);
nextWaypoint.getStatistics().merge(deletedWaypoint.getStatistics());
nextWaypoint.setDescription(
descriptionGenerator.generateWaypointDescription(nextWaypoint));
if (!updateWaypoint(nextWaypoint)) {
@@ -505,7 +488,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
"No statistics marker after the deleted one was found.");
}
}
context.getContentResolver().delete(
contentResolver.delete(
WaypointsColumns.CONTENT_URI, "_id=" + waypointId, null);
}
@@ -517,7 +500,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
final String sortOrder = WaypointsColumns._ID + " LIMIT 1";
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(
cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
null /*projection*/,
selection,
@@ -539,7 +522,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public boolean updateWaypoint(Waypoint waypoint) {
try {
final int rows = context.getContentResolver().update(
final int rows = contentResolver.update(
WaypointsColumns.CONTENT_URI,
createContentValues(waypoint),
"_id=" + waypoint.getId(),
@@ -560,7 +543,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
private Location findLocationBy(String select) {
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(
cursor = contentResolver.query(
TrackPointsColumns.CONTENT_URI, null, select, null, null);
if (cursor != null && cursor.moveToNext()) {
return createLocation(cursor);
@@ -584,7 +567,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
private Track findTrackBy(String select) {
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(
cursor = contentResolver.query(
TracksColumns.CONTENT_URI, null, select, null, null);
if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor);
@@ -606,7 +589,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public Waypoint getFirstWaypoint(long trackId) {
Cursor cursor = context.getContentResolver().query(
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
null /*projection*/,
"trackid=" + trackId,
@@ -628,7 +611,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public Waypoint getWaypoint(long waypointId) {
Cursor cursor = context.getContentResolver().query(
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
null /*projection*/,
"_id=" + waypointId,
@@ -651,7 +634,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public long getLastLocationId(long trackId) {
final String[] projection = {"_id"};
Cursor cursor = context.getContentResolver().query(
Cursor cursor = contentResolver.query(
TrackPointsColumns.CONTENT_URI,
projection,
"_id=(select max(_id) from trackpoints WHERE trackid=" + trackId + ")",
@@ -675,7 +658,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public long getFirstWaypointId(long trackId) {
final String[] projection = {"_id"};
Cursor cursor = context.getContentResolver().query(
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
projection,
"trackid=" + trackId,
@@ -699,7 +682,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public long getLastWaypointId(long trackId) {
final String[] projection = {"_id"};
Cursor cursor = context.getContentResolver().query(
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
projection,
WaypointsColumns.TRACKID + "=" + trackId,
@@ -724,7 +707,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
public Track getLastTrack() {
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(
cursor = contentResolver.query(
TracksColumns.CONTENT_URI, null, "_id=(select max(_id) from tracks)",
null, null);
if (cursor != null && cursor.moveToNext()) {
@@ -743,7 +726,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public long getLastTrackId() {
String[] proj = { TracksColumns._ID };
Cursor cursor = context.getContentResolver().query(
Cursor cursor = contentResolver.query(
TracksColumns.CONTENT_URI, proj, "_id=(select max(_id) from tracks)",
null, null);
if (cursor != null) {
@@ -783,13 +766,13 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
sortOrder += " LIMIT " + maxLocations;
}
return context.getContentResolver().query(
return contentResolver.query(
TrackPointsColumns.CONTENT_URI, null, selection, null, sortOrder);
}
@Override
public Cursor getWaypointsCursor(long trackId, long minWaypointId,
long maxWaypoints) {
int maxWaypoints) {
String selection;
if (minWaypointId > 0) {
selection = String.format("%s=%d AND %s>=%d",
@@ -805,7 +788,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
sortOrder += " LIMIT " + maxWaypoints;
}
return context.getContentResolver().query(
return contentResolver.query(
WaypointsColumns.CONTENT_URI, null, selection, null, sortOrder);
}
@@ -814,6 +797,21 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
String select = TracksColumns._ID + "=" + id;
return findTrackBy(select);
}
@Override
public List<Track> getAllTracks() {
Cursor cursor = getTracksCursor(null);
if (cursor == null || !cursor.moveToFirst()) {
return new ArrayList<Track>();
}
List<Track> tracks = new ArrayList<Track>(cursor.getCount());
do {
tracks.add(createTrack(cursor));
} while(cursor.moveToNext());
return tracks;
}
@Override
public long getTrackPoints(Track track, int maxPoints) {
@@ -844,55 +842,9 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
return lastId;
}
@Override
public void getTrackPoints(Track track, TrackBuffer buffer) {
long startingPoint = buffer.getLastLocationRead() == 0 ? track.getStartId()
: buffer.getLastLocationRead();
buffer.reset();
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();
return;
}
try {
if (cursor.getCount() == 0) {
Log.w(MyTracksProvider.TAG, "No matching locations found.");
buffer.resetAt(startingPoint + buffer.getSize());
return;
}
if (!cursor.moveToFirst()) {
Log.w(MyTracksProvider.TAG, "Could not move to first.");
buffer.setInvalid();
return;
}
while (cursor.moveToNext()) {
Location location = createLocation(cursor);
if (location == null) {
continue;
}
buffer.add(location, cursor.getLong(idColumnIdx));
}
if (buffer.getLocationsLoaded() == 0) {
Log.w(MyTracksProvider.TAG, "No locations read.");
buffer.resetAt(startingPoint + buffer.getSize());
}
} catch (RuntimeException e) {
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
} finally {
cursor.close();
}
}
@Override
public Cursor getTracksCursor(String selection) {
Cursor cursor = context.getContentResolver().query(
Cursor cursor = contentResolver.query(
TracksColumns.CONTENT_URI, null, selection, null, "_id");
return cursor;
}
@@ -900,56 +852,43 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public Uri insertTrack(Track track) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrack");
return context.getContentResolver().insert(TracksColumns.CONTENT_URI,
return contentResolver.insert(TracksColumns.CONTENT_URI,
createContentValues(track));
}
@Override
public Uri insertTrackPoint(Location location, long trackId) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrackPoint");
return context.getContentResolver().insert(TrackPointsColumns.CONTENT_URI,
return contentResolver.insert(TrackPointsColumns.CONTENT_URI,
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 contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI, values);
}
@Override
public Uri insertWaypoint(Waypoint waypoint) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
waypoint.setId(-1);
return context.getContentResolver().insert(WaypointsColumns.CONTENT_URI,
return contentResolver.insert(WaypointsColumns.CONTENT_URI,
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;
try {
final String[] projection = { TracksColumns._ID };
cursor = context.getContentResolver().query(
cursor = contentResolver.query(
TracksColumns.CONTENT_URI,
projection,
TracksColumns._ID + "=" + id/*selection*/,
@@ -969,7 +908,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
@Override
public void updateTrack(Track track) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.updateTrack");
context.getContentResolver().update(TracksColumns.CONTENT_URI,
contentResolver.update(TracksColumns.CONTENT_URI,
createContentValues(track), "_id=" + track.getId(), null);
}
}
@@ -15,6 +15,8 @@
*/
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.stats.TripStatistics;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,7 +26,10 @@ import java.util.ArrayList;
/**
* A class representing a (GPS) Track.
*
* TODO: hashCode and equals
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
public class Track implements Parcelable {
@@ -34,6 +39,7 @@ public class Track implements Parcelable {
public static class Creator implements Parcelable.Creator<Track> {
public Track createFromParcel(Parcel source) {
ClassLoader classLoader = getClass().getClassLoader();
Track track = new Track();
track.id = source.readLong();
track.name = source.readString();
@@ -42,28 +48,14 @@ public class Track implements Parcelable {
track.category = source.readString();
track.startId = source.readLong();
track.stopId = source.readLong();
track.startTime = source.readLong();
track.stopTime = source.readLong();
track.totalDistance = source.readDouble();
track.totalTime = source.readLong();
track.movingTime = source.readLong();
track.averageSpeed = source.readDouble();
track.averageMovingSpeed = source.readDouble();
track.maxSpeed = source.readDouble();
track.minElevation = source.readDouble();
track.maxElevation = source.readDouble();
track.totalElevationGain = source.readDouble();
track.minGrade = source.readDouble();
track.maxGrade = source.readDouble();
track.left = source.readInt();
track.top = source.readInt();
track.right = source.readInt();
track.bottom = source.readInt();
track.stats = source.readParcelable(classLoader);
track.numberOfPoints = source.readInt();
for (int i = 0; i < track.numberOfPoints; ++i) {
Location loc = source.readParcelable(null);
Location loc = source.readParcelable(classLoader);
track.locations.add(loc);
}
return track;
}
@@ -75,10 +67,16 @@ public class Track implements Parcelable {
public static final Creator CREATOR = new Creator();
/**
* The track points.
* The track points (which may not have been loaded).
*/
private ArrayList<Location> locations = new ArrayList<Location>();
/**
* The number of location points (present even if the points themselves were
* not loaded).
*/
private int numberOfPoints = 0;
private long id = -1;
private String name = "";
private String description = "";
@@ -87,34 +85,9 @@ public class Track implements Parcelable {
private long stopId = -1;
private String category = "";
// Fields derived from locations. Use setDerivedFields() to set them:
//-------------------------------------------------------------------
private long startTime = -1;
private long stopTime = -1;
private int numberOfPoints = 0;
private double totalDistance = 0;
private long totalTime = 0;
private long movingTime = 0;
private int left;
private int top;
private int right;
private int bottom;
private double averageSpeed = 0;
private double averageMovingSpeed = 0;
private double maxSpeed = 0;
private double minElevation = 0;
private double maxElevation = 0;
private double totalElevationGain = 0;
private double minGrade = 0;
private double maxGrade = 0;
private TripStatistics stats = new TripStatistics();
public Track() {
left = Integer.MAX_VALUE;
top = Integer.MIN_VALUE;
right = Integer.MIN_VALUE;
bottom = Integer.MAX_VALUE;
}
public void writeToParcel(Parcel dest, int flags) {
@@ -125,23 +98,8 @@ public class Track implements Parcelable {
dest.writeString(category);
dest.writeLong(startId);
dest.writeLong(stopId);
dest.writeLong(startTime);
dest.writeLong(stopTime);
dest.writeDouble(totalDistance);
dest.writeLong(totalTime);
dest.writeLong(movingTime);
dest.writeDouble(averageSpeed);
dest.writeDouble(averageMovingSpeed);
dest.writeDouble(maxSpeed);
dest.writeDouble(minElevation);
dest.writeDouble(maxElevation);
dest.writeDouble(totalElevationGain);
dest.writeDouble(minGrade);
dest.writeDouble(maxGrade);
dest.writeInt(left);
dest.writeInt(top);
dest.writeInt(right);
dest.writeInt(bottom);
dest.writeParcelable(stats, 0);
dest.writeInt(numberOfPoints);
for (int i = 0; i < numberOfPoints; ++i) {
dest.writeParcelable(locations.get(i), 0);
@@ -151,29 +109,6 @@ public class Track implements Parcelable {
// Getters and setters:
//---------------------
public int getLeft() {
return left;
}
public int getTop() {
return top;
}
public int getRight() {
return right;
}
public int getBottom() {
return bottom;
}
public void setBounds(int aLeft, int aTop, int aRight, int aBottom) {
left = aLeft;
top = aTop;
right = aRight;
bottom = aBottom;
}
public int describeContents() {
return 0;
}
@@ -186,46 +121,6 @@ public class Track implements Parcelable {
this.id = id;
}
public double getTotalDistance() {
return totalDistance;
}
public void setTotalDistance(double totalDistance) {
this.totalDistance = totalDistance;
}
public long getTotalTime() {
return totalTime;
}
public void setTotalTime(long totalTime) {
this.totalTime = totalTime;
}
public long getMovingTime() {
return movingTime;
}
public void setMovingTime(long movingTime) {
this.movingTime = movingTime;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getStopTime() {
return stopTime;
}
public void setStopTime(long stopTime) {
this.stopTime = stopTime;
}
public String getName() {
return name;
}
@@ -274,70 +169,6 @@ public class Track implements Parcelable {
this.category = category;
}
public double getAverageSpeed() {
return averageSpeed;
}
public void setAverageSpeed(double averageSpeed) {
this.averageSpeed = averageSpeed;
}
public double getAverageMovingSpeed() {
return averageMovingSpeed;
}
public void setAverageMovingSpeed(double averageMovingSpeed) {
this.averageMovingSpeed = averageMovingSpeed;
}
public double getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
public double getMinElevation() {
return minElevation;
}
public void setMinElevation(double minElevation) {
this.minElevation = minElevation;
}
public double getMaxElevation() {
return maxElevation;
}
public void setMaxElevation(double maxElevation) {
this.maxElevation = maxElevation;
}
public double getTotalElevationGain() {
return totalElevationGain;
}
public void setTotalElevationGain(double totalElevationGain) {
this.totalElevationGain = totalElevationGain;
}
public double getMinGrade() {
return minGrade;
}
public void setMinGrade(double minGrade) {
this.minGrade = minGrade;
}
public double getMaxGrade() {
return maxGrade;
}
public void setMaxGrade(double maxGrade) {
this.maxGrade = maxGrade;
}
public int getNumberOfPoints() {
return numberOfPoints;
}
@@ -357,4 +188,12 @@ public class Track implements Parcelable {
public void setLocations(ArrayList<Location> locations) {
this.locations = locations;
}
public TripStatistics getStatistics() {
return stats;
}
public void setStatistics(TripStatistics stats) {
this.stats = stats;
}
}
@@ -1,133 +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.mytracks.content;
import com.google.android.apps.mytracks.util.MyTracksUtils;
import android.location.Location;
/**
* A buffer of Locations from a track.
* This class contains some additional information about the loaded points.
*
* @author Sandor Dornbush
*/
public class TrackBuffer {
/**
* An array of location objects that are part of a track.
*/
private final Location[] buffer;
/**
* The id of the last location read from the track.
*/
private long lastLocationRead = 0;
/**
* The number of locations loaded into the buffer.
*/
private int locationsLoaded = 0;
/**
* Create a TrackBuffer with size elements.
*
* @param size The size of the buffer
*/
public TrackBuffer(int size) {
buffer = new Location[size];
}
/**
* Reset the track to a state with no locations.
*/
public void reset() {
resetAt(0);
}
/**
* Reset the track at the given starting location id.
*/
public void resetAt(long lastLocation) {
lastLocationRead = lastLocation;
locationsLoaded = 0;
}
public void setInvalid() {
lastLocationRead = Integer.MAX_VALUE;
}
/**
* @return The number of locations that can be stored in this buffer
*/
public int getSize() {
return buffer.length;
}
/**
* @param index The index of the location to fetch
* @return The location for the given index
*/
public Location get(int index) {
return buffer[index];
}
/**
* Adds a location to the end of the buffer.
* @param location The location to add.
* @param id The id of the location to be added.
*/
public void add(Location location, long id) {
buffer[locationsLoaded++] = location;
lastLocationRead = Math.max(lastLocationRead, id);
}
/**
* @return The id of the last location loaded into the buffer
*/
public long getLastLocationRead() {
return lastLocationRead;
}
/**
* @return The number of locations loaded into the buffer.
*/
public int getLocationsLoaded() {
return locationsLoaded;
}
/**
* Finds the start location, i.e. the one which is the first point of a
* segment with at least two points.
*
* @return the start location
*/
public Location findStartLocation() {
int numValidLocations = 0;
for (int i = 0; i < getLocationsLoaded(); i++) {
Location location = buffer[i];
if (MyTracksUtils.isValidLocation(location)) {
numValidLocations++;
if (numValidLocations == 2) {
return buffer[i - 1];
}
} else {
numValidLocations = 0;
}
}
return null;
}
}
@@ -15,6 +15,8 @@
*/
package com.google.android.apps.mytracks.content;
import static com.google.android.apps.mytracks.content.ContentTypeIds.*;
import android.net.Uri;
import android.provider.BaseColumns;
@@ -42,4 +44,14 @@ public interface TrackPointsColumns extends BaseColumns {
public static final String TIME = "time";
public static final String ACCURACY = "accuracy";
public static final String SPEED = "speed";
/** Columns that go into the backup. */
public static final String[] BACKUP_COLUMNS =
{ _ID, TRACKID, LATITUDE, LONGITUDE, ALTITUDE, BEARING, TIME,
ACCURACY, SPEED };
/** Types of the columns that go into the backup. */
public static final byte[] BACKUP_COLUMN_TYPES =
{ LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID, FLOAT_TYPE_ID,
FLOAT_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_TYPE_ID };
}
@@ -15,6 +15,8 @@
*/
package com.google.android.apps.mytracks.content;
import static com.google.android.apps.mytracks.content.ContentTypeIds.*;
import android.net.Uri;
import android.provider.BaseColumns;
@@ -59,4 +61,16 @@ 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[] BACKUP_COLUMNS = {
_ID, NAME, DESCRIPTION, CATEGORY, STARTID, STOPID, STARTTIME, STOPTIME,
NUMPOINTS, TOTALDISTANCE, TOTALTIME, MOVINGTIME, AVGSPEED, AVGMOVINGSPEED,
MAXSPEED, MINELEVATION, MAXELEVATION, ELEVATIONGAIN, MINGRADE, MAXGRADE,
MINLAT, MAXLAT, MINLON, MAXLON };
public static final byte[] 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 };
}
@@ -15,6 +15,8 @@
*/
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.stats.TripStatistics;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,16 +25,20 @@ import android.os.Parcelable;
* A way point. It has a location, meta data such as name, description,
* category, and icon, plus it can store track statistics for a "sub-track".
*
* TODO: hashCode and equals
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
public final class Waypoint implements Parcelable {
/**
* Creator for a Waypoint object
* Creator for a Waypoint object
*/
public static class Creator implements Parcelable.Creator<Waypoint> {
public Waypoint createFromParcel(Parcel source) {
ClassLoader classLoader = getClass().getClassLoader();
Waypoint waypoint = new Waypoint();
waypoint.id = source.readLong();
waypoint.name = source.readString();
@@ -41,26 +47,13 @@ public final class Waypoint implements Parcelable {
waypoint.icon = source.readString();
waypoint.trackId = source.readLong();
waypoint.type = source.readInt();
waypoint.length = source.readDouble();
waypoint.duration = source.readLong();
waypoint.startTime = source.readLong();
waypoint.startId = source.readLong();
waypoint.stopId = source.readLong();
waypoint.stats = source.readParcelable(classLoader);
byte hasLocation = source.readByte();
if (hasLocation > 0) {
waypoint.location = source.readParcelable(null);
waypoint.location = source.readParcelable(classLoader);
}
waypoint.totalDistance = source.readDouble();
waypoint.totalTime = source.readLong();
waypoint.movingTime = source.readLong();
waypoint.averageSpeed = source.readDouble();
waypoint.averageMovingSpeed = source.readDouble();
waypoint.maxSpeed = source.readDouble();
waypoint.minElevation = source.readDouble();
waypoint.maxElevation = source.readDouble();
waypoint.totalElevationGain = source.readDouble();
waypoint.minGrade = source.readDouble();
waypoint.maxGrade = source.readDouble();
return waypoint;
}
@@ -84,28 +77,18 @@ public final class Waypoint implements Parcelable {
private Location location;
/** Length along the track in meters. */
private double length = 0;
/** Time along the track in seconds. */
private long duration = 0;
/** Start time of a segment */
private long startTime = -1;
/** Start track point id */
private long startId = -1;
/** Stop track point id */
private long stopId = -1;
private double totalDistance = 0;
private long totalTime = 0;
private long movingTime = 0;
private double averageSpeed = 0;
private double averageMovingSpeed = 0;
private double maxSpeed = 0;
private double minElevation = 0;
private double maxElevation = 0;
private double totalElevationGain = 0;
private double minGrade = 0;
private double maxGrade = 0;
private TripStatistics stats = new TripStatistics();
/** The length of the track, without smoothing. */
private double length;
/** The total duration of the track (not from the last waypoint) */
private long duration;
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
@@ -115,46 +98,13 @@ public final class Waypoint implements Parcelable {
dest.writeString(icon);
dest.writeLong(trackId);
dest.writeInt(type);
dest.writeDouble(length);
dest.writeLong(duration);
dest.writeLong(startTime);
dest.writeLong(startId);
dest.writeLong(stopId);
dest.writeParcelable(stats, 0);
dest.writeByte(location == null ? (byte) 0 : (byte) 1);
if (location != null) {
dest.writeParcelable(location, 0);
}
dest.writeDouble(totalDistance);
dest.writeLong(totalTime);
dest.writeLong(movingTime);
dest.writeDouble(averageSpeed);
dest.writeDouble(averageMovingSpeed);
dest.writeDouble(maxSpeed);
dest.writeDouble(minElevation);
dest.writeDouble(maxElevation);
dest.writeDouble(totalElevationGain);
dest.writeDouble(minGrade);
dest.writeDouble(maxGrade);
}
/**
* Combines the statistics from another (earlier) waypoint with this one.
* This is needed when the user deletes a statistic waypoint.
*
* @param other the other waypoint
*/
public void combine(Waypoint other) {
totalTime += other.totalTime;
movingTime += other.movingTime;
totalDistance += other.totalDistance;
totalElevationGain += other.totalElevationGain;
averageSpeed = totalDistance / totalTime;
averageMovingSpeed = totalDistance / movingTime;
maxSpeed = Math.max(maxSpeed, other.maxSpeed);
minElevation = Math.min(minElevation, other.minElevation);
maxElevation = Math.max(maxElevation, other.maxElevation);
minGrade = Math.min(minGrade, other.minGrade);
maxGrade = Math.max(maxGrade, other.maxGrade);
}
// Getters and setters:
@@ -188,30 +138,6 @@ public final class Waypoint implements Parcelable {
this.id = id;
}
public double getTotalDistance() {
return totalDistance;
}
public void setTotalDistance(double totalDistance) {
this.totalDistance = totalDistance;
}
public long getTotalTime() {
return totalTime;
}
public void setTotalTime(long totalTime) {
this.totalTime = totalTime;
}
public long getMovingTime() {
return movingTime;
}
public void setMovingTime(long movingTime) {
this.movingTime = movingTime;
}
public String getName() {
return name;
}
@@ -232,30 +158,6 @@ public final class Waypoint implements Parcelable {
this.type = type;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getStartId() {
return startId;
}
@@ -288,71 +190,34 @@ public final class Waypoint implements Parcelable {
this.category = category;
}
public double getAverageSpeed() {
return averageSpeed;
}
public void setAverageSpeed(double averageSpeed) {
this.averageSpeed = averageSpeed;
}
public double getAverageMovingSpeed() {
return averageMovingSpeed;
}
public void setAverageMovingSpeed(double averageMovingSpeed) {
this.averageMovingSpeed = averageMovingSpeed;
}
public double getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
public double getMinElevation() {
return minElevation;
}
public void setMinElevation(double minElevation) {
this.minElevation = minElevation;
}
public double getMaxElevation() {
return maxElevation;
}
public void setMaxElevation(double maxElevation) {
this.maxElevation = maxElevation;
}
public double getTotalElevationGain() {
return totalElevationGain;
}
public void setTotalElevationGain(double totalElevationGain) {
this.totalElevationGain = totalElevationGain;
}
public double getMinGrade() {
return minGrade;
}
public void setMinGrade(double minGrade) {
this.minGrade = minGrade;
}
public double getMaxGrade() {
return maxGrade;
}
public void setMaxGrade(double maxGrade) {
this.maxGrade = maxGrade;
}
public void setLocation(Location location) {
this.location = location;
}
public TripStatistics getStatistics() {
return stats;
}
public void setStatistics(TripStatistics stats) {
this.stats = stats;
}
// WARNING: These fields are used for internal state keeping. You probably
// want to look at getStatistics instead.
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
}
@@ -15,6 +15,8 @@
*/
package com.google.android.apps.mytracks.content;
import static com.google.android.apps.mytracks.content.ContentTypeIds.*;
import android.net.Uri;
import android.provider.BaseColumns;
@@ -66,4 +68,19 @@ public interface WaypointsColumns extends BaseColumns {
public static final String ELEVATIONGAIN = "elevationgain";
public static final String MINGRADE = "mingrade";
public static final String MAXGRADE = "maxgrade";
public static final String[] BACKUP_COLUMNS = {
_ID, TRACKID, NAME, DESCRIPTION, CATEGORY, ICON, TYPE, LENGTH, DURATION,
STARTTIME, STARTID, STOPID, LATITUDE, LONGITUDE, ALTITUDE, BEARING, TIME,
ACCURACY, SPEED, TOTALDISTANCE, TOTALTIME, MOVINGTIME, AVGSPEED,
AVGMOVINGSPEED, MAXSPEED, MINELEVATION, MAXELEVATION, ELEVATIONGAIN,
MINGRADE, MAXGRADE };
public static final byte[] BACKUP_COLUMN_TYPES = {
LONG_TYPE_ID, LONG_TYPE_ID, STRING_TYPE_ID, STRING_TYPE_ID,
STRING_TYPE_ID, STRING_TYPE_ID, INT_TYPE_ID, FLOAT_TYPE_ID, LONG_TYPE_ID,
LONG_TYPE_ID, LONG_TYPE_ID, LONG_TYPE_ID, INT_TYPE_ID, INT_TYPE_ID,
FLOAT_TYPE_ID, FLOAT_TYPE_ID, LONG_TYPE_ID, FLOAT_TYPE_ID, FLOAT_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 };
}
@@ -20,7 +20,6 @@ import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
import android.location.Location;
import android.os.Environment;
import java.io.OutputStream;
import java.io.PrintWriter;
@@ -47,7 +46,7 @@ import java.util.TimeZone;
public class CsvTrackWriter implements TrackFormatWriter {
static final SimpleDateFormat TIMESTAMP_FORMAT =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
{
static {
TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
@@ -56,13 +55,6 @@ public class CsvTrackWriter implements TrackFormatWriter {
private PrintWriter pw;
private Track track;
@Override
public String getDefaultDirectory() {
return Environment.getExternalStorageDirectory()
+ System.getProperty("file.separator")
+ getExtension();
}
@Override
public String getExtension() {
return TrackFileFormat.CSV.getExtension();
@@ -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.TripStatistics;
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();
track.setStartTime(startTime);
// Calculate statistics for the imported track
TripStatistics stats = new TripStatistics(startTime);
for (Location location : locations) {
if (MyTracksUtils.isValidLocation(location)) {
/* Any time works here. The totalTime will be set by "pauseAt" later: */
stats.addLocation(location, location.getTime());
}
}
long lastPointTime = locations.get(locations.size() - 1).getTime();
stats.pauseAt(lastPointTime);
track.setStopTime(lastPointTime);
stats.fillStatisticsForTrack(track);
}
}
}
}
}
}
@@ -0,0 +1,530 @@
/*
* 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 com.google.android.apps.mytracks.util.StringUtils;
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.util.ArrayList;
import java.util.List;
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 {
/*
* 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 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; }
// Parse the time
long time;
try {
time = StringUtils.parseXmlDateTime(content.trim());
} catch (IllegalArgumentException e) {
String msg = createErrorMessage("Unable to parse time: " + content);
throw new SAXException(msg, e);
}
// Calculate derived attributes from previous point
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));
}
// Fill in the time
location.setTime(time);
// initialize start time with time of first track point
if (statsBuilder == null) {
statsBuilder = new TripStatisticsBuilder(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;
}
/**
* 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();
}
}
@@ -21,7 +21,6 @@ import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
import com.google.android.apps.mytracks.util.StringUtils;
import android.location.Location;
import android.os.Environment;
import java.io.OutputStream;
import java.io.PrintWriter;
@@ -38,7 +37,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
static final SimpleDateFormat TIMESTAMP_FORMAT =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
{
static {
TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
@@ -51,13 +50,6 @@ public class GpxTrackWriter implements TrackFormatWriter {
this.pw = new PrintWriter(out);
}
@Override
public String getDefaultDirectory() {
return Environment.getExternalStorageDirectory()
+ System.getProperty("file.separator")
+ getExtension();
}
@Override
public String getExtension() {
return TrackFileFormat.GPX.getExtension();
@@ -145,6 +137,7 @@ public class GpxTrackWriter implements TrackFormatWriter {
@Override
public void writeWaypoint(Waypoint waypoint) {
if (pw != null) {
// TODO: The gpx spec says waypoints should come *before* tracks
Location l = waypoint.getLocation();
if (l != null) {
pw.println("<wpt lat=\"" + l.getLatitude() + "\" lon=\""
@@ -22,7 +22,6 @@ import com.google.android.apps.mytracks.util.StringUtils;
import android.content.Context;
import android.location.Location;
import android.os.Environment;
import java.io.OutputStream;
import java.io.PrintWriter;
@@ -58,12 +57,6 @@ public class KmlTrackWriter implements TrackFormatWriter {
this.pw = new PrintWriter(out);
}
@Override
public String getDefaultDirectory() {
return Environment.getExternalStorageDirectory()
+ System.getProperty("file.separator") + getExtension();
}
@Override
public String getExtension() {
return TrackFileFormat.KML.getExtension();
@@ -24,6 +24,7 @@ import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
import com.google.android.apps.mytracks.io.gdata.GDataWrapper;
import com.google.android.apps.mytracks.io.gdata.GDataWrapper.QueryFunction;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.ResourceUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
@@ -111,7 +112,9 @@ public class SendToDocs {
SharedPreferences preferences = activity.getSharedPreferences(
MyTracksSettings.SETTINGS_NAME, 0);
if (preferences != null) {
metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true);
metricUnits =
preferences.getBoolean(activity.getString(R.string.metric_units_key),
true);
} else {
metricUnits = true;
}
@@ -206,7 +209,7 @@ public class SendToDocs {
androidClient = GDataClientFactory.getGDataClient(activity);
SpreadsheetsClient gdataClient = new SpreadsheetsClient(androidClient,
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
wiseWrapper.setClient(androidClient, gdataClient);
wiseWrapper.setClient(gdataClient);
Log.d(MyTracksConstants.TAG,
"GData connection prepared: " + this.writelyAuth);
String sheetTitle = "My Tracks";
@@ -217,7 +220,7 @@ public class SendToDocs {
DocumentsClient docsGdataClient = new DocumentsClient(androidClient,
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
writelyWrapper.setClient(androidClient, docsGdataClient);
writelyWrapper.setClient(docsGdataClient);
// First try to find the spreadsheet:
if (!getSpreadsheetId(writelyWrapper, sheetTitle)) {
@@ -429,6 +432,7 @@ public class SendToDocs {
String worksheetId) throws IOException {
String worksheetUri = String.format(DOCS_SPREADSHEET_URL_FORMAT,
spreadsheetId, worksheetId);
TripStatistics stats = track.getStatistics();
/* Prepare the Post-Text we are going to send. */
StringBuilder sb = new StringBuilder();
@@ -437,29 +441,29 @@ public class SendToDocs {
+ "2006/extended'>");
appendTag("name", track.getName(), sb);
appendTag("description", track.getDescription(), sb);
appendTag("date", String.format("%tc", track.getStartTime()), sb);
appendTag("date", String.format("%tc", stats.getStartTime()), sb);
appendTag("totaltime", StringUtils.formatTimeAlwaysShowingHours(
track.getTotalTime()), sb);
stats.getTotalTime()), sb);
appendTag("movingtime", StringUtils.formatTimeAlwaysShowingHours(
track.getMovingTime()), sb);
appendLargeUnitsTag("distance", track.getTotalDistance() / 1000, sb);
stats.getMovingTime()), sb);
appendLargeUnitsTag("distance", stats.getTotalDistance() / 1000, sb);
appendTag("distanceunit",
metricUnits
? activity.getString(R.string.kilometer)
: activity.getString(R.string.mile),
sb);
appendLargeUnitsTag("averagespeed", track.getAverageSpeed() * 3.6, sb);
appendLargeUnitsTag("averagespeed", stats.getAverageSpeed() * 3.6, sb);
appendLargeUnitsTag("averagemovingspeed",
track.getAverageMovingSpeed() * 3.6, sb);
appendLargeUnitsTag("maxspeed", track.getMaxSpeed() * 3.6, sb);
stats.getAverageMovingSpeed() * 3.6, sb);
appendLargeUnitsTag("maxspeed", stats.getMaxSpeed() * 3.6, sb);
appendTag("speedunit",
metricUnits
? activity.getString(R.string.kilometer_per_hour)
: activity.getString(R.string.mile_per_hour),
sb);
appendSmallUnitsTag("elevationgain", track.getTotalElevationGain(), sb);
appendSmallUnitsTag("minelevation", track.getMinElevation(), sb);
appendSmallUnitsTag("maxelevation", track.getMaxElevation(), sb);
appendSmallUnitsTag("elevationgain", stats.getTotalElevationGain(), sb);
appendSmallUnitsTag("minelevation", stats.getMinElevation(), sb);
appendSmallUnitsTag("maxelevation", stats.getMaxElevation(), sb);
appendTag("elevationunit",
metricUnits
? activity.getString(R.string.meter)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,184 @@
/*
* 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.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
import android.location.Location;
import android.os.Build;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
/**
* Write out a a track in the Garmin training center database, tcx format.
* As defined by:
* http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2
*
* @author Sandor Dornbush
*/
public class TcxTrackWriter implements TrackFormatWriter {
private PrintWriter pw = null;
private Track track;
static final SimpleDateFormat TIMESTAMP_FORMAT =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
static {
TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public TcxTrackWriter() {
}
@Override
public void prepare(Track track, OutputStream out) {
this.track = track;
this.pw = new PrintWriter(out);
}
@Override
public void close() {
if (pw != null) {
pw.close();
pw = null;
}
}
@Override
public String getExtension() {
return TrackFileFormat.TCX.getExtension();
}
@Override
public void writeHeader() {
if (pw == null) {
return;
}
pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>");
pw.print("<TrainingCenterDatabase ");
pw.print("xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
pw.print("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
pw.print("xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 ");
pw.println("http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">");
pw.println();
}
@Override
public void writeBeginTrack(Location firstPoint) {
if (pw == null) {
return;
}
pw.println(" <Activities>");
pw.print(" <Activity Sport=\"");
if (track.getCategory() != null) {
pw.print(track.getCategory());
}
pw.println("\">");
pw.print(" <Id>");
pw.print(TIMESTAMP_FORMAT.format(track.getStatistics().getStartTime()));
pw.println("</Id>");
pw.println(" <Lap>");
pw.print(" <TotalTimeSeconds>");
pw.print(track.getStatistics().getTotalTime() / 1000);
pw.println("</TotalTimeSeconds>");
pw.print(" <DistanceMeters>");
pw.print(track.getStatistics().getTotalDistance());
pw.println("</DistanceMeters>");
// TODO max speed etc.
// Calories are a required element just put in 0.
pw.print("<Calories>0</Calories>");
pw.println("<Intensity>Active</Intensity>");
pw.println("<TriggerMethod>Manual</TriggerMethod>)");
}
@Override
public void writeOpenSegment() {
if (pw != null) {
pw.println(" <Track>");
}
}
@Override
public void writeLocation(Location location) {
if (pw == null) {
return;
}
pw.println(" <Trackpoint>");
Date d = new Date(location.getTime());
pw.println(" <Time>" + TIMESTAMP_FORMAT.format(d) + "</Time>");
pw.println(" <Position>");
pw.print(" <LatitudeDegrees>");
pw.print(location.getLatitude());
pw.println("</LatitudeDegrees>");
pw.print(" <LongitudeDegrees>");
pw.print(location.getLongitude());
pw.println("</LongitudeDegrees>");
pw.println(" </Position>");
pw.print(" <AltitudeMeters>");
pw.print(location.getAltitude());
pw.println("</AltitudeMeters>");
pw.println(" </Trackpoint>");
}
@Override
public void writeCloseSegment() {
if (pw != null) {
pw.println(" </Track>");
}
}
@Override
public void writeEndTrack(Location lastPoint) {
if (pw == null) {
return;
}
pw.println(" </Lap>");
pw.print(" <Creator xsi:type=\"Device_t\">");
pw.print("<Name>");
pw.print(Build.MODEL);
pw.print("</Name>");
pw.println("</Creator>)");
pw.println(" </Activity>");
pw.println(" </Activities>");
}
@Override
public void writeFooter() {
if (pw == null) {
return;
}
pw.print(" <Author xsi:type=\"Application_t\">");
pw.print("<Name>My Tracks by Google</Name>");
pw.println("</Author>");
pw.println("</TrainingCenterDatabase>");
}
@Override
public void writeWaypoint(Waypoint waypoint) {
// TODO Write out the waypoints somewhere.
}
}
@@ -0,0 +1,73 @@
/*
* 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 android.os.Environment;
import java.io.File;
/**
* A class to clean up old temporary files.
* @author Sandor Dornbush
*/
public class TempFileCleaner {
private long currentTimeMillis;
public static void clean() {
(new TempFileCleaner(System.currentTimeMillis())).cleanImpl();
}
// @VisibleForTesting
TempFileCleaner(long time) {
currentTimeMillis = time;
}
private void cleanImpl() {
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
return; // Can't do anything now.
}
cleanTmpDirectory("csv");
cleanTmpDirectory("gpx");
cleanTmpDirectory("kml");
cleanTmpDirectory("tcx");
}
private void cleanTmpDirectory(String name) {
String sep = System.getProperty("file.separator");
cleanTmpDirectory(
new File(
Environment.getExternalStorageDirectory() + sep + name + sep
+ "tmp"));
}
// @VisibleForTesting
int cleanTmpDirectory(File dir) {
if (!dir.exists()) {
return 0;
}
int count = 0;
long oldest = currentTimeMillis - 1000 * 3600;
for (File f : dir.listFiles()) {
if (f.lastModified() < oldest) {
f.delete();
count++;
}
}
return count;
}
}
@@ -53,14 +53,6 @@ public interface TrackFormatWriter {
*/
void prepare(Track track, OutputStream out);
/**
* Determines the default location that will be written to (if no other is
* specified).
*
* @return The absolute path for the the file created, or null on failure
*/
String getDefaultDirectory();
/**
* @return The file extentsion (i.e. gpx, kml, ...)
*/
@@ -18,8 +18,8 @@ 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.content.TrackBuffer;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.MyTracksUtils;
import com.google.android.maps.mytracks.R;
@@ -27,14 +27,12 @@ import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.HashSet;
/**
* The class which exports tracks to the SD card.
@@ -51,39 +49,20 @@ public class TrackWriter {
private final MyTracksProviderUtils providerUtils;
private final Track track;
private final TrackFormatWriter writer;
private final FileUtils fileUtils;
private Runnable onCompletion = null;
private boolean success = false;
private int errorMessage = -1;
private File directory = null;
private File file = null;
/**
* A set of characters that are prohibited from being in file names.
*/
private static final HashSet<Character> PROHIBITED_CHARACTERS =
new HashSet<Character>();
static {
for (int i = 0; i < 48; i++) {
PROHIBITED_CHARACTERS.add(new Character((char) i));
}
for (int i = 58; i < 65; i++) {
PROHIBITED_CHARACTERS.add(new Character((char) i));
}
for (int i = 91; i < 97; i++) {
PROHIBITED_CHARACTERS.add(new Character((char) i));
}
for (int i = 123; i < 128; i++) {
PROHIBITED_CHARACTERS.add(new Character((char) i));
}
}
public TrackWriter(Context context, MyTracksProviderUtils providerUtils,
Track track, TrackFormatWriter writer) {
this.context = context;
this.providerUtils = providerUtils;
this.track = track;
this.writer = writer;
this.fileUtils = new FileUtils();
}
/**
@@ -149,26 +128,6 @@ public class TrackWriter {
* ===============
*/
/**
* Makes sure the given directory exists, creating it if necessary.
*
* @param dir the directory to ensure exists
* @return True on success
*/
private static boolean ensureExists(File dir) {
if (dir.exists()) {
return true;
}
File parent = dir.getParentFile();
if (parent == null) {
return true;
}
if (!ensureExists(parent)) {
return false;
}
return dir.mkdir();
}
private void finished() {
if (onCompletion != null) {
runOnUiThread(onCompletion);
@@ -195,9 +154,15 @@ public class TrackWriter {
return false;
}
// Make sure the name will work on FAT
String fileName =
sanitizeName(track.getName()) + "." + writer.getExtension();
// Make sure the file doesn't exist yet (possibly by changing the filename)
String fileName = fileUtils.buildUniqueFileName(
directory, track.getName(), writer.getExtension());
if (fileName == null) {
Log.e(MyTracksConstants.TAG,
"Unable to get a unique filename for " + fileName);
return false;
}
Log.i(MyTracksConstants.TAG, "Writing track to: " + fileName);
try {
writer.prepare(track, newOutputStream(fileName));
@@ -214,17 +179,18 @@ public class TrackWriter {
*/
protected boolean canWriteFile() {
if (directory == null) {
directory = newFile(writer.getDefaultDirectory());
String dirName =
fileUtils.buildExternalDirectoryPath(writer.getExtension());
directory = newFile(dirName);
}
if (!Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED)) {
if (!fileUtils.isSdCardAvailable()) {
Log.i(MyTracksConstants.TAG, "Could not find SD card.");
errorMessage = R.string.io_no_external_storage_found;
return false;
}
if (!ensureExists(directory)) {
Log.i(MyTracksConstants.TAG, "Could not create GPX directory.");
if (!fileUtils.ensureDirectoryExists(directory)) {
Log.i(MyTracksConstants.TAG, "Could not create export directory.");
errorMessage = R.string.io_create_dir_failed;
return false;
}
@@ -250,25 +216,6 @@ public class TrackWriter {
return new File(path);
}
/**
* Normalizes the input string and make sure it is a valid fat32 file name.
*/
static String sanitizeName(String name) {
StringBuilder cleaned = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (!PROHIBITED_CHARACTERS.contains(c)) {
cleaned.append(c);
}
}
// Max = 260
// Boiler plate /[gpx|kml]/ .[gpx|kml] = 8
return (cleaned.length() > 252)
? cleaned.substring(0, 252)
: cleaned.toString();
}
/**
* Writes the waypoints for the given track.
*
@@ -306,55 +253,8 @@ public class TrackWriter {
void writeDocument() {
Log.d(MyTracksConstants.TAG, "Started writing track.");
writer.writeHeader();
TrackBuffer buffer = new TrackBuffer(1024);
Location last = null;
boolean wroteFirst = false;
boolean segmentOpen = false;
int nValidLocations = 0;
// Fetch small pieces of the track.
while (buffer.getLastLocationRead() < track.getStopId()) {
Log.d(MyTracksConstants.TAG,
"Reading track points starting at: " + buffer.getLastLocationRead());
providerUtils.getTrackPoints(track, buffer);
if (!wroteFirst) {
Location first = buffer.findStartLocation();
writer.writeBeginTrack(first);
wroteFirst = true;
}
Log.d(MyTracksConstants.TAG,
"Reading " + buffer.getLocationsLoaded()
+ " Ending at: " + buffer.getLastLocationRead());
for (int i = 0; i < buffer.getLocationsLoaded(); i++) {
Location location = buffer.get(i);
if (MyTracksUtils.isValidLocation(location)) {
nValidLocations++;
if (!segmentOpen) {
writer.writeOpenSegment();
segmentOpen = true;
}
writer.writeLocation(location);
if (nValidLocations >= 2) {
last = location;
}
} else {
nValidLocations = 0;
last = null;
if (segmentOpen) {
writer.writeCloseSegment();
segmentOpen = false;
}
}
}
}
if (segmentOpen) {
writer.writeCloseSegment();
segmentOpen = false;
}
if (wroteFirst) {
writer.writeEndTrack(last);
}
// TODO: Fix ordering (in GPX waypoints should come first)
writeLocations();
writeWaypoints(track.getId());
writer.writeFooter();
writer.close();
@@ -362,4 +262,66 @@ public class TrackWriter {
Log.d(MyTracksConstants.TAG, "Done writing track.");
errorMessage = R.string.io_write_finished;
}
private void writeLocations() {
boolean wroteFirst = false;
boolean segmentOpen = false;
Location lastLoc = null, loc = null;
boolean isLastValid = false;
Cursor locationsCursor =
providerUtils.getLocationsCursor(track.getId(), 0, -1, false);
if (locationsCursor == null || !locationsCursor.moveToFirst()) {
Log.w(MyTracksConstants.TAG, "Unable to get any points to write");
return;
}
do {
if (loc == null) loc = new Location("");
providerUtils.fillLocation(locationsCursor, loc);
boolean isValid = MyTracksUtils.isValidLocation(loc);
boolean validSegment = isValid && isLastValid;
if (!wroteFirst && validSegment) {
// Found the first two consecutive points which are valid
writer.writeBeginTrack(lastLoc);
wroteFirst = true;
}
if (validSegment) {
if (!segmentOpen) {
// Start a segment for this point
writer.writeOpenSegment();
segmentOpen = true;
// Write the previous point, which we had previously skipped
writer.writeLocation(lastLoc);
}
// Write the current point
writer.writeLocation(loc);
} else {
if (segmentOpen) {
writer.writeCloseSegment();
segmentOpen = false;
}
}
// Swap loc and lastLoc (so lastLoc is reused)
Location tmp = lastLoc;
lastLoc = loc;
loc = tmp;
if (loc != null) loc.reset();
isLastValid = isValid;
} while (locationsCursor.moveToNext());
if (segmentOpen) {
writer.writeCloseSegment();
segmentOpen = false;
}
if (wroteFirst) {
writer.writeEndTrack(lastLoc);
}
}
}
@@ -49,6 +49,12 @@ public class TrackWriterFactory {
public TrackFormatWriter newFormatWriter(Context context) {
return new CsvTrackWriter();
}
},
TCX {
@Override
public TrackFormatWriter newFormatWriter(Context context) {
return new TcxTrackWriter();
}
};
/**
@@ -96,7 +102,7 @@ public class TrackWriterFactory {
* @param format the output format to write in
* @return the new track writer
*/
public static TrackWriter newWriter(Context context,
private static TrackWriter newWriter(Context context,
MyTracksProviderUtils providerUtils,
Track track, TrackFileFormat format) {
TrackFormatWriter writer = format.newFormatWriter(context);
@@ -0,0 +1,247 @@
/*
* 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.backup;
import com.google.android.apps.mytracks.MyTracks;
import com.google.android.apps.mytracks.MyTracksConstants;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
/**
* Helper which shows a UI for writing or restoring a backup,
* and calls the appropriate handler for actually executing those
* operations.
*
* @author Rodrigo Damazio
*/
public class BackupActivityHelper {
// Since the user sees this format, we use the local timezone
private static final SimpleDateFormat DISPLAY_BACKUP_FORMAT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final Comparator<Date> REVERSE_DATE_ORDER =
new Comparator<Date>() {
@Override
public int compare(Date s1, Date s2) {
return s2.compareTo(s1);
}
};
private final FileUtils fileUtils;
private final ExternalFileBackup backup;
private final Activity activity;
public BackupActivityHelper(Activity activity) {
this.activity = activity;
this.fileUtils = new FileUtils();
this.backup = new ExternalFileBackup(activity, fileUtils);
}
/**
* Writes a full backup to the default file.
* This shows the results to the user.
*/
public void writeBackup() {
if (!fileUtils.isSdCardAvailable()) {
showToast(R.string.io_no_external_storage_found);
return;
}
if (!backup.isBackupsDirectoryAvailable(true)) {
showToast(R.string.io_create_dir_failed);
return;
}
final ProgressDialog progressDialog = ProgressDialog.show(
activity,
activity.getString(R.string.progress_title),
activity.getString(R.string.backup_write_progress_message),
true);
// Do the writing in another thread
new Thread() {
@Override
public void run() {
try {
backup.writeToDefaultFile();
showToast(R.string.io_write_finished);
} catch (IOException e) {
Log.e(MyTracksConstants.TAG, "Failed to write backup", e);
showToast(R.string.io_write_failed);
} finally {
dismissDialog(progressDialog);
}
}
}.start();
}
/**
* Restores a full backup from the SD card.
* The user will be given a choice of which backup to restore as well as a
* confirmation dialog.
*/
public void restoreBackup() {
// Get the list of existing backups
if (!fileUtils.isSdCardAvailable()) {
showToast(R.string.io_no_external_storage_found);
return;
}
if (!backup.isBackupsDirectoryAvailable(false)) {
showToast(R.string.no_backups);
return;
}
final Date[] backupDates = backup.getAvailableBackups();
if (backupDates == null || backupDates.length == 0) {
showToast(R.string.no_backups);
return;
}
Arrays.sort(backupDates, REVERSE_DATE_ORDER);
// Show a confirmation dialog
Builder confirmationDialogBuilder = new AlertDialog.Builder(activity);
confirmationDialogBuilder.setMessage(R.string.restore_overwrites_warning);
confirmationDialogBuilder.setCancelable(true);
confirmationDialogBuilder.setPositiveButton(android.R.string.yes,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
pickBackupForRestore(backupDates);
}
});
confirmationDialogBuilder.setNegativeButton(android.R.string.no, null);
confirmationDialogBuilder.create().show();
}
/**
* Shows a backup list for the user to pick, then restores it.
*
* @param backupDir the backup directory
* @param backupDates the list of available backup files
*/
private void pickBackupForRestore(final Date[] backupDates) {
if (backupDates.length == 1) {
// Only one choice, don't bother showing the list
restoreFromDateAsync(backupDates[0]);
return;
}
// Make a user-visible version of the backup filenames
final String backupDateStrs[] = new String[backupDates.length];
for (int i = 0; i < backupDates.length; i++) {
backupDateStrs[i] = DISPLAY_BACKUP_FORMAT.format(backupDates[i]);
}
// Show a dialog for the user to pick which backup to restore
Builder dialogBuilder = new AlertDialog.Builder(activity);
dialogBuilder.setCancelable(true);
dialogBuilder.setTitle(R.string.select_backup_to_restore);
dialogBuilder.setItems(backupDateStrs, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// User picked to restore this one
restoreFromDateAsync(backupDates[which]);
}
});
dialogBuilder.create().show();
}
/**
* Shows a progress dialog, then starts restoring the backup osynchronously.
*
* @param inputFile the file to restore from
*/
private void restoreFromDateAsync(final Date date) {
// Show a progress dialog
final ProgressDialog progressDialog = ProgressDialog.show(
activity,
activity.getString(R.string.progress_title),
activity.getString(R.string.backup_import_progress_message),
true);
// Do the actual importing in another thread (don't block the UI)
new Thread() {
@Override
public void run() {
try {
backup.restoreFromDate(date);
showToast(R.string.io_read_finished);
} catch (IOException e) {
Log.e(MyTracksConstants.TAG, "Failed to restore backup", e);
showToast(R.string.io_read_failed);
} finally {
dismissDialog(progressDialog);
// Data may have been restored, "reboot" the app to catch it
restartApplication();
}
}
}.start();
}
/**
* Restarts My Tracks completely.
* This forces any modified data to be re-read.
*/
private void restartApplication() {
Intent intent = new Intent(activity, MyTracks.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
activity.startActivity(intent);
}
/**
* Shows a toast with the given contents.
*/
private void showToast(final int resId) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, resId, Toast.LENGTH_LONG).show();
}
});
}
/**
* Safely dismisses the given dialog.
*/
private void dismissDialog(final Dialog dialog) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
dialog.dismiss();
}
});
}
}
@@ -0,0 +1,77 @@
/*
* 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.backup;
import com.google.android.apps.mytracks.util.ApiFeatures;
import android.app.backup.BackupManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
/**
* Shared preferences listener which notifies the backup system about new data
* being available for backup.
* This class is API-version-safe and will provide a dummy implementation if
* the device doesn't support backup services.
*
* @author Rodrigo Damazio
*/
public abstract class BackupPreferencesListener
implements OnSharedPreferenceChangeListener {
/**
* Real implementation of the listener, which calls the {@link BackupManager}.
*/
private static class BackupPreferencesListenerImpl
extends BackupPreferencesListener {
private final BackupManager backupManager;
public BackupPreferencesListenerImpl(Context context) {
this.backupManager = new BackupManager(context);
}
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
backupManager.dataChanged();
}
}
/**
* Dummy implementation of the listener which does nothing.
*/
private static class DummyBackupPreferencesListener
extends BackupPreferencesListener {
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
// Do nothing
}
}
/**
* Creates and returns a proper instance of the listener for this device.
*/
public static BackupPreferencesListener create(
Context context, ApiFeatures apiFeatures) {
if (apiFeatures.hasBackup()) {
return new BackupPreferencesListenerImpl(context);
} else {
return new DummyBackupPreferencesListener();
}
}
}
@@ -0,0 +1,236 @@
/*
* 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.backup;
import com.google.android.apps.mytracks.content.ContentTypeIds;
import android.database.Cursor;
import android.database.MergeCursor;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* Database dumper which is able to write only part of the database
* according to some query.
*
* This dumper is symmetrical to {@link DatabaseImporter}.
*
* @author Rodrigo Damazio
*/
class DatabaseDumper {
/** The names of the columns being dumped. */
private final String[] columnNames;
/** The types of the columns being dumped. */
private final byte[] columnTypes;
/** Whether to output null fields. */
private final boolean outputNullFields;
// Temporary state
private int[] columnIndices;
private boolean[] hasFields;
public DatabaseDumper(String[] columnNames, byte[] columnTypes,
boolean outputNullFields) {
if (columnNames.length != columnTypes.length) {
throw new IllegalArgumentException("Names don't match types");
}
this.columnNames = columnNames;
this.columnTypes = columnTypes;
this.outputNullFields = outputNullFields;
}
/**
* Writes the header plus all rows that can be read from the given cursor.
* This assumes the cursor will have the same column and column indices on
* every row (and thus may not work with a {@link MergeCursor}).
*/
public void writeAllRows(Cursor cursor, DataOutputStream writer)
throws IOException {
writeHeaders(cursor, cursor.getCount(), writer);
if (!cursor.moveToFirst()) {
return;
}
do {
writeOneRow(cursor, writer);
} while (cursor.moveToNext());
}
/**
* Writes just the headers for the data that will come from the given cursor.
* The headers include column information and the number of rows that will be
* written.
*
* @param cursor the cursor to get columns from
* @param numRows the number of rows that will be later written
* @throws IOException if there are errors while writing
*/
public void writeHeaders(Cursor cursor, int numRows, DataOutputStream writer)
throws IOException {
initializeCachedValues(cursor);
writeQueryMetadata(cursor, numRows, writer);
}
/**
* Writes the current row from the cursor. The cursor is not advanced.
* This must be called after {@link #writeHeaders}.
*
* @param cursor the cursor to write data from
* @throws IOException if there are any errors while writing
*/
public void writeOneRow(Cursor cursor, DataOutputStream writer)
throws IOException {
if (columnIndices == null) {
throw new IllegalStateException(
"Cannot write rows before writing the header");
}
if (columnIndices.length > Long.SIZE) {
throw new IllegalArgumentException("Too many fields");
}
// Build a bitmap of which fields are present
long fields = 0;
for (int i = 0; i < columnIndices.length; i++) {
hasFields[i] = !cursor.isNull(columnIndices[i]);
fields |= (hasFields[i] ? 1 : 0) << i;
}
writer.writeLong(fields);
// Actually write the present fields
for (int i = 0; i < columnIndices.length; i++) {
if (hasFields[i]) {
writeCell(columnIndices[i], columnTypes[i], cursor, writer);
} else if (outputNullFields) {
writeDummyCell(columnTypes[i], writer);
}
}
}
/**
* Initializes the column indices and other temporary state for reading from
* the given cursor.
*/
private void initializeCachedValues(Cursor cursor) {
// These indices are constant for every row (unless we're fed a MergeCursor)
if (cursor instanceof MergeCursor) {
throw new IllegalArgumentException("Cannot use a MergeCursor");
}
columnIndices = new int[columnNames.length];
for (int i = 0; i < columnNames.length; i++) {
String columnName = columnNames[i];
columnIndices[i] = cursor.getColumnIndexOrThrow(columnName);
}
hasFields = new boolean[columnIndices.length];
}
/**
* Writes metadata about the query to be dumped.
*
* @param cursor the cursor that the data will come from
* @param numRows the number of rows that will be dumped
* @throws IOException if there are any errors while writing
*/
private void writeQueryMetadata(
Cursor cursor, int numRows, DataOutputStream writer) throws IOException {
// Write column data
writer.writeInt(columnNames.length);
for (int i = 0; i < columnNames.length; i++) {
String columnName = columnNames[i];
byte columnType = columnTypes[i];
writer.writeUTF(columnName);
writer.writeByte(columnType);
}
// Write the number of rows
writer.writeInt(numRows);
}
/**
* Writes a single cell of the database to the output.
*
* @param columnIdx the column index to read from
* @param columnType the type of the column to be read
* @param cursor the cursor to read from
* @throws IOException if there are any errors while writing
*/
private void writeCell(
int columnIdx, byte columnTypeId, Cursor cursor, DataOutputStream writer)
throws IOException {
switch (columnTypeId) {
case ContentTypeIds.LONG_TYPE_ID:
writer.writeLong(cursor.getLong(columnIdx));
return;
case ContentTypeIds.DOUBLE_TYPE_ID:
writer.writeDouble(cursor.getDouble(columnIdx));
return;
case ContentTypeIds.FLOAT_TYPE_ID:
writer.writeFloat(cursor.getFloat(columnIdx));
return;
case ContentTypeIds.BOOLEAN_TYPE_ID:
writer.writeBoolean(cursor.getInt(columnIdx) != 0);
return;
case ContentTypeIds.INT_TYPE_ID:
writer.writeInt(cursor.getInt(columnIdx));
return;
case ContentTypeIds.STRING_TYPE_ID:
writer.writeUTF(cursor.getString(columnIdx));
return;
default:
throw new IllegalArgumentException(
"Type " + columnTypeId + " not supported");
}
}
/**
* Writes a dummy cell value to the output.
*
* @param columnType the type of the value to write
* @throws IOException if there are any errors while writing
*/
private void writeDummyCell(byte columnTypeId, DataOutputStream writer)
throws IOException {
switch (columnTypeId) {
case ContentTypeIds.LONG_TYPE_ID:
writer.writeLong(0L);
return;
case ContentTypeIds.DOUBLE_TYPE_ID:
writer.writeDouble(0.0);
return;
case ContentTypeIds.FLOAT_TYPE_ID:
writer.writeFloat(0.0f);
return;
case ContentTypeIds.BOOLEAN_TYPE_ID:
writer.writeBoolean(false);
return;
case ContentTypeIds.INT_TYPE_ID:
writer.writeInt(0);
return;
case ContentTypeIds.STRING_TYPE_ID:
writer.writeUTF("");
return;
default:
throw new IllegalArgumentException(
"Type " + columnTypeId + " not supported");
}
}
}

Some files were not shown because too many files have changed in this diff Show More