forked from upstream-mirrors/OpenTracks
Ignoring Matthew's second head by no-op merging it.
This commit is contained in:
@@ -3,3 +3,4 @@ syntax:glob
|
|||||||
*/bin
|
*/bin
|
||||||
*/gen/**
|
*/gen/**
|
||||||
.metadata
|
.metadata
|
||||||
|
MyTracksTest/annotations.log
|
||||||
|
|||||||
+10
-10
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry kind="lib" path="libs/googleloginclient-helper.jar"/>
|
<classpathentry kind="lib" path="libs/googleloginclient-helper.jar"/>
|
||||||
<classpathentry kind="lib" path="libs/google-common.jar"/>
|
<classpathentry kind="lib" path="libs/google-common.jar"/>
|
||||||
<classpathentry kind="lib" path="libs/accounts.jar"/>
|
<classpathentry kind="lib" path="libs/accounts.jar"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.google.android.maps.mytracks"
|
package="com.google.android.maps.mytracks"
|
||||||
|
android:installLocation="auto"
|
||||||
android:versionCode="20"
|
android:versionCode="20"
|
||||||
android:versionName="1.0.19">
|
android:versionName="1.0.19">
|
||||||
<application android:icon="@drawable/arrow_icon"
|
<application android:icon="@drawable/arrow_icon"
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
android:value="AEdPqrEAAAAIi-_QiwoRSc9_bAC9cmuNXTQyU8ajJmGtKdhskQ" />
|
android:value="AEdPqrEAAAAIi-_QiwoRSc9_bAC9cmuNXTQyU8ajJmGtKdhskQ" />
|
||||||
|
|
||||||
<provider android:name="com.google.android.apps.mytracks.content.MyTracksProvider"
|
<provider android:name="com.google.android.apps.mytracks.content.MyTracksProvider"
|
||||||
android:multiprocess="true"
|
android:process=":trackrecord"
|
||||||
android:authorities="com.google.android.maps.mytracks" />
|
android:authorities="com.google.android.maps.mytracks" />
|
||||||
|
|
||||||
<activity android:name="com.google.android.apps.mytracks.MyTracks"
|
<activity android:name="com.google.android.apps.mytracks.MyTracks"
|
||||||
@@ -101,6 +102,9 @@
|
|||||||
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.writely" />
|
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.writely" />
|
||||||
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
|
<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" />
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="3"/>
|
<uses-sdk android:minSdkVersion="3"/>
|
||||||
<supports-screens
|
<supports-screens
|
||||||
android:largeScreens="true"
|
android:largeScreens="true"
|
||||||
|
|||||||
@@ -147,6 +147,14 @@ limitations under the License.
|
|||||||
<item>1 分钟</item>
|
<item>1 分钟</item>
|
||||||
</string-array>
|
</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 -->
|
<!-- Other non-preference arrays -->
|
||||||
<array name="activity_types">
|
<array name="activity_types">
|
||||||
<item>未知</item>
|
<item>未知</item>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<!-- DO NOT TRANSLATE -->
|
|
||||||
<string name="app_name">My Tracks</string>
|
|
||||||
|
|
||||||
<!-- Used by the Menu -->
|
<!-- Used by the Menu -->
|
||||||
<string name="start_recording">记录轨迹</string>
|
<string name="start_recording">记录轨迹</string>
|
||||||
@@ -32,6 +32,7 @@ limitations under the License.
|
|||||||
<string name="choose_track_message">请选择要查看的轨迹</string>
|
<string name="choose_track_message">请选择要查看的轨迹</string>
|
||||||
<string name="current_track">当前轨迹</string>
|
<string name="current_track">当前轨迹</string>
|
||||||
<string name="current_segment">上一段轨迹</string>
|
<string name="current_segment">上一段轨迹</string>
|
||||||
|
<string name="aggregated_stats">累积统计</string>
|
||||||
|
|
||||||
<!-- Error messages -->
|
<!-- Error messages -->
|
||||||
<string name="error_no_location_manager">此设备上没有位置管理器!</string>
|
<string name="error_no_location_manager">此设备上没有位置管理器!</string>
|
||||||
@@ -122,6 +123,7 @@ limitations under the License.
|
|||||||
<string name="tracklist_save_as_gpx">保存为 GPX 文件</string>
|
<string name="tracklist_save_as_gpx">保存为 GPX 文件</string>
|
||||||
<string name="tracklist_save_as_kml">保存为 KML 文件</string>
|
<string name="tracklist_save_as_kml">保存为 KML 文件</string>
|
||||||
<string name="tracklist_save_as_csv">保存为 CSV 文件</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_send_all_to_mymaps">发送到 Google My Maps</string>
|
||||||
<string name="tracklist_btn_delete_all">删除所有轨迹</string>
|
<string name="tracklist_btn_delete_all">删除所有轨迹</string>
|
||||||
<!-- Export All -->
|
<!-- Export All -->
|
||||||
@@ -135,6 +137,7 @@ limitations under the License.
|
|||||||
<string name="tracklist_share_gpx_file">GPX 文件</string>
|
<string name="tracklist_share_gpx_file">GPX 文件</string>
|
||||||
<string name="tracklist_share_kml_file">KML 文件</string>
|
<string name="tracklist_share_kml_file">KML 文件</string>
|
||||||
<string name="tracklist_share_csv_file">CSV 文件</string>
|
<string name="tracklist_share_csv_file">CSV 文件</string>
|
||||||
|
<string name="tracklist_share_tcx_file">TCX 文件</string>
|
||||||
<string name="tracklist_clear_map">清除地图</string>
|
<string name="tracklist_clear_map">清除地图</string>
|
||||||
|
|
||||||
<!-- Waypoints list -->
|
<!-- Waypoints list -->
|
||||||
@@ -165,9 +168,10 @@ limitations under the License.
|
|||||||
<string name="settings_recording">记录</string>
|
<string name="settings_recording">记录</string>
|
||||||
<string name="settings_sharing">分享</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">提示频度</string>
|
||||||
<string name="settings_announcement_frequency_summary">语音提示的最小时间间隔</string>
|
<string name="settings_announcement_frequency_summary">语音提示的最小时间间隔</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="settings_min_recording_distance">最小记录间距</string>
|
<string name="settings_min_recording_distance">最小记录间距</string>
|
||||||
<string name="settings_min_recording_distance_summary">位置记录的最小间隔距离</string>
|
<string name="settings_min_recording_distance_summary">位置记录的最小间隔距离</string>
|
||||||
@@ -202,7 +206,38 @@ limitations under the License.
|
|||||||
|
|
||||||
<string name="settings_split_frequency">切割频度</string>
|
<string name="settings_split_frequency">切割频度</string>
|
||||||
<string name="settings_split_frequency_summary">插入新切割标记所需要达到的最大时间或距离</string>
|
<string name="settings_split_frequency_summary">插入新切割标记所需要达到的最大时间或距离</string>
|
||||||
<string name="pace_label">步数</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 -->
|
<!-- MyTracksSky -->
|
||||||
<string name="mytracks_sky">GPS 卫星</string>
|
<string name="mytracks_sky">GPS 卫星</string>
|
||||||
@@ -241,7 +276,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<!-- Visibility -->
|
<!-- Visibility -->
|
||||||
<string name="public_map">公开</string>
|
<string name="public_map">公开</string>
|
||||||
<string name="unlisted_map">Unlisted</string>
|
<string name="unlisted_map">未公布</string>
|
||||||
<string name="mapslist_no_maps_available">你还没有保存任何地图.</string>
|
<string name="mapslist_no_maps_available">你还没有保存任何地图.</string>
|
||||||
|
|
||||||
<!-- Statistics View -->
|
<!-- Statistics View -->
|
||||||
@@ -292,24 +327,32 @@ limitations under the License.
|
|||||||
<string name="second">秒</string>
|
<string name="second">秒</string>
|
||||||
<string name="seconds">秒</string>
|
<string name="seconds">秒</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Distance -->
|
<!-- Distance -->
|
||||||
<string name="meter">m</string>
|
|
||||||
<string name="kilometer">km</string>
|
|
||||||
<string name="kilometers_long">千米</string>
|
<string name="kilometers_long">千米</string>
|
||||||
<string name="feet">ft</string>
|
|
||||||
<string name="mile">mi</string>
|
|
||||||
<string name="miles_long">英里</string>
|
<string name="miles_long">英里</string>
|
||||||
|
|
||||||
<!-- Speed and Pace -->
|
<!-- Speed and Pace -->
|
||||||
<string name="kilometer_per_hour">km/h</string>
|
|
||||||
<string name="kilometer_per_hour_long">千米每小时</string>
|
<string name="kilometer_per_hour_long">千米每小时</string>
|
||||||
<string name="min_per_kilometer">min/km</string>
|
|
||||||
<string name="per_kilometer">每千米</string>
|
<string name="per_kilometer">每千米</string>
|
||||||
<string name="mile_per_hour">mi/h</string>
|
|
||||||
<string name="mile_per_hour_long">英里每小时</string>
|
<string name="mile_per_hour_long">英里每小时</string>
|
||||||
<string name="min_per_mile">min/mi</string>
|
|
||||||
<string name="per_mile">每英里</string>
|
<string name="per_mile">每英里</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Stats Labels -->
|
<!-- Stats Labels -->
|
||||||
<string name="moving_time_label">移动时间</string>
|
<string name="moving_time_label">移动时间</string>
|
||||||
<string name="total_time_label">总时间</string>
|
<string name="total_time_label">总时间</string>
|
||||||
@@ -320,7 +363,11 @@ limitations under the License.
|
|||||||
<string name="statistics">统计</string>
|
<string name="statistics">统计</string>
|
||||||
<string name="total_distance_label">总距离</string>
|
<string name="total_distance_label">总距离</string>
|
||||||
<string name="distance_label">距离</string>
|
<string name="distance_label">距离</string>
|
||||||
<string name="unknown">-</string>
|
<string name="power">电源</string>
|
||||||
|
<string name="cadence">节奏</string>
|
||||||
|
<string name="heart_rate">心率</string>
|
||||||
|
<string name="max_heart_rate">最大心率</string>
|
||||||
|
|
||||||
<string name="waiting">请稍候...</string>
|
<string name="waiting">请稍候...</string>
|
||||||
|
|
||||||
<!-- End User License Agreement -->
|
<!-- End User License Agreement -->
|
||||||
@@ -336,20 +383,27 @@ limitations under the License.
|
|||||||
<string name="about_email">技术支持</string>
|
<string name="about_email">技术支持</string>
|
||||||
<string name="about_support">技术支持:</string>
|
<string name="about_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>
|
|
||||||
<string name="about_dylan">Dylan Casey</string>
|
|
||||||
<string name="about_leif">Leif Hendrik Wilden</string>
|
|
||||||
<string name="about_rdamazio">Rodrigo Damazio Bovendorp</string>
|
|
||||||
<string name="about_sandor">Sandor Dornbush</string>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Send to Google -->
|
<!-- Send to Google -->
|
||||||
<string name="sendtogoogle_title">发送到...</string>
|
<string name="sendtogoogle_title">发送到...</string>
|
||||||
<string name="sendtogoogle_google_mymaps">Google My Maps</string>
|
|
||||||
<string name="sendtogoogle_google_docs">Google Docs</string>
|
|
||||||
<string name="sendtogoogle_send_now">立即发送</string>
|
<string name="sendtogoogle_send_now">立即发送</string>
|
||||||
<string name="sendtogoogle_create_new_map">创建新地图</string>
|
<string name="sendtogoogle_create_new_map">创建新地图</string>
|
||||||
<string name="sendtogoogle_pick_existing_map">选择已有地图</string>
|
<string name="sendtogoogle_pick_existing_map">选择已有地图</string>
|
||||||
@@ -364,8 +418,9 @@ limitations under the License.
|
|||||||
<!-- SD card -->
|
<!-- SD card -->
|
||||||
<string name="io_no_external_storage_found">没有发现存贮设备. 请确认插入了 SD 卡并且你的手机没有将挂载 SD 卡到电脑.</string>
|
<string name="io_no_external_storage_found">没有发现存贮设备. 请确认插入了 SD 卡并且你的手机没有将挂载 SD 卡到电脑.</string>
|
||||||
<string name="io_write_failed">无法写入文件.</string>
|
<string name="io_write_failed">无法写入文件.</string>
|
||||||
<string name="io_create_dir_failed">无法在 SD 卡上创建目录.</string>
|
<string name="io_create_dir_failed">无法在 SD 存储卡上创建目录.</string>
|
||||||
<string name="io_write_finished">轨迹已导出到 SD 卡.</string>
|
<string name="io_write_finished">轨迹已导出到 SD 卡.</string>
|
||||||
|
<string name="io_read_finished">从 SD 存储卡读取完毕.</string>
|
||||||
|
|
||||||
<!-- VersionChecker -->
|
<!-- VersionChecker -->
|
||||||
<string name="version_check_title">检查更新</string>
|
<string name="version_check_title">检查更新</string>
|
||||||
@@ -403,4 +458,29 @@ limitations under the License.
|
|||||||
<string name="gsm_strength">GSM 强度 %1$d</string>
|
<string name="gsm_strength">GSM 强度 %1$d</string>
|
||||||
<string name="cdma_strength">CDMA 强度 %1$d dBm</string>
|
<string name="cdma_strength">CDMA 强度 %1$d dBm</string>
|
||||||
<string name="evdo_strength">EVDO 强度 %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>
|
</resources>
|
||||||
|
|||||||
@@ -211,8 +211,8 @@ limitations under the License.
|
|||||||
<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_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">Timestamp as track name</string>
|
||||||
<string name="settings_timestamp_track_name_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_on">The default track name will be the date and time at the start of the track</string>
|
||||||
<string name="settings_timestamp_track_name_off">Track names will have numbers which increment for each new 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="pace_label">Pace</string>
|
||||||
|
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ limitations under the License.
|
|||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="@string/timestamp_track_name_key"
|
android:key="@string/timestamp_track_name_key"
|
||||||
android:title="@string/settings_timestamp_track_name"
|
android:title="@string/settings_timestamp_track_name"
|
||||||
android:summaryOn="@string/settings_timestamp_track_name_on"
|
android:summaryOn="@string/settings_timestamp_track_name_summary_on"
|
||||||
android:summaryOff="@string/settings_timestamp_track_name_off"
|
android:summaryOff="@string/settings_timestamp_track_name_summary_off"
|
||||||
android:defaultValue="true" />
|
android:defaultValue="true" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|||||||
@@ -298,13 +298,13 @@ public class ChartActivity extends Activity implements
|
|||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
Log.d(MyTracksConstants.TAG, "ChartActivity: ContentObserver.onChange");
|
Log.d(MyTracksConstants.TAG, "ChartActivity: ContentObserver.onChange");
|
||||||
// Check for any new locations and append them to the currently
|
// Check for any new locations and append them to the currently
|
||||||
// recording track:
|
// recording track.
|
||||||
if (!MyTracks.getInstance().isRecording()) {
|
if (recordingTrackId < 0) {
|
||||||
// No track is being recorded. We should not be here.
|
// No track is being recorded. We should not be here.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedTrackId != recordingTrackId) {
|
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.
|
// bother.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import android.graphics.Path;
|
|||||||
import java.text.DecimalFormat;
|
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
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ import com.google.android.apps.mytracks.io.AuthManagerFactory;
|
|||||||
import com.google.android.apps.mytracks.io.GpxImporter;
|
import com.google.android.apps.mytracks.io.GpxImporter;
|
||||||
import com.google.android.apps.mytracks.io.SendToDocs;
|
import com.google.android.apps.mytracks.io.SendToDocs;
|
||||||
import com.google.android.apps.mytracks.io.SendToMyMaps;
|
import com.google.android.apps.mytracks.io.SendToMyMaps;
|
||||||
import com.google.android.apps.mytracks.io.SendToMyMaps.OnSendCompletedListener;
|
|
||||||
import com.google.android.apps.mytracks.io.TrackWriter;
|
import com.google.android.apps.mytracks.io.TrackWriter;
|
||||||
import com.google.android.apps.mytracks.io.TrackWriterFactory;
|
import com.google.android.apps.mytracks.io.TrackWriterFactory;
|
||||||
|
import com.google.android.apps.mytracks.io.SendToMyMaps.OnSendCompletedListener;
|
||||||
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
||||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||||
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
||||||
@@ -65,9 +65,9 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnTouchListener;
|
||||||
import android.view.ViewGroup.LayoutParams;
|
import android.view.ViewGroup.LayoutParams;
|
||||||
import android.view.Window;
|
|
||||||
import android.view.WindowManager.BadTokenException;
|
import android.view.WindowManager.BadTokenException;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TabHost;
|
import android.widget.TabHost;
|
||||||
@@ -97,9 +97,10 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
/**
|
/**
|
||||||
* Singleton instance
|
* Singleton instance
|
||||||
*/
|
*/
|
||||||
private static MyTracks instance = null;
|
private static MyTracks instance;
|
||||||
private ChartActivity chartActivity = null;
|
|
||||||
|
|
||||||
|
private ChartActivity chartActivity;
|
||||||
|
|
||||||
public ChartActivity getChartActivity() {
|
public ChartActivity getChartActivity() {
|
||||||
return chartActivity;
|
return chartActivity;
|
||||||
}
|
}
|
||||||
@@ -178,6 +179,8 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
* Utilities to deal with the database.
|
* Utilities to deal with the database.
|
||||||
*/
|
*/
|
||||||
private MyTracksProviderUtils providerUtils;
|
private MyTracksProviderUtils providerUtils;
|
||||||
|
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The connection to the track recording service.
|
* The connection to the track recording service.
|
||||||
@@ -233,20 +236,35 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Checks whether we have a track recording session in progress.
|
||||||
|
* In some cases, when the service has crashed or has been restarted
|
||||||
|
* by the system, we fall back to the shared preferences.
|
||||||
|
*
|
||||||
* @return true if the activity is bound to the track recording service and
|
* @return true if the activity is bound to the track recording service and
|
||||||
* the service is recording a track.
|
* the service is recording a track or in case the service is down,
|
||||||
|
* based on settings from the shared preferences.
|
||||||
*/
|
*/
|
||||||
public boolean isRecording() {
|
public boolean isRecording() {
|
||||||
if (trackRecordingService == null) {
|
if (trackRecordingService == null) {
|
||||||
return false;
|
// Fall back to alternative check method.
|
||||||
|
return isRecordingBasedOnSharedPreferences();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return trackRecordingService.isRecording();
|
return trackRecordingService.isRecording();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(MyTracksConstants.TAG, "MyTracks: Remote exception.", e);
|
Log.e(MyTracksConstants.TAG, "MyTracks: Remote exception.", e);
|
||||||
return false;
|
|
||||||
|
// Fall back to alternative check method.
|
||||||
|
return isRecordingBasedOnSharedPreferences();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isRecordingBasedOnSharedPreferences() {
|
||||||
|
// TrackRecordingServices guarantees that recordingTrackId is set to
|
||||||
|
// -1 if the track has been stopped.
|
||||||
|
// TODO: Refresh recordingTrackId.
|
||||||
|
return recordingTrackId >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Application lifetime events:
|
* Application lifetime events:
|
||||||
@@ -260,6 +278,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
instance = this;
|
instance = this;
|
||||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||||
menuManager = new MenuManager(this);
|
menuManager = new MenuManager(this);
|
||||||
|
sharedPreferences = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||||
|
|
||||||
// The volume we want to control is the Text-To-Speech volume
|
// The volume we want to control is the Text-To-Speech volume
|
||||||
int volumeStream =
|
int volumeStream =
|
||||||
@@ -295,14 +314,18 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
tabHost.addView(layout);
|
tabHost.addView(layout);
|
||||||
layout.setOnTouchListener(this);
|
layout.setOnTouchListener(this);
|
||||||
|
|
||||||
SharedPreferences prefs =
|
if (sharedPreferences != null) {
|
||||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
|
||||||
if (prefs != null) {
|
|
||||||
selectedTrackId =
|
selectedTrackId =
|
||||||
prefs.getLong(getString(R.string.selected_track_key), -1);
|
sharedPreferences.getLong(getString(R.string.selected_track_key), -1);
|
||||||
recordingTrackId =
|
recordingTrackId = sharedPreferences.getLong(
|
||||||
prefs.getLong(getString(R.string.recording_track_key), -1);
|
getString(R.string.recording_track_key), -1);
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
Log.d(MyTracksConstants.TAG, "recordingTrackId: " + recordingTrackId
|
||||||
|
+ ", selectedTrackId: " + selectedTrackId);
|
||||||
|
if (recordingTrackId > 0) {
|
||||||
|
Intent startIntent = new Intent(this, TrackRecordingService.class);
|
||||||
|
startService(startIntent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will show the eula until the user accepts or quits the app.
|
// This will show the eula until the user accepts or quits the app.
|
||||||
@@ -1008,20 +1031,15 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
|
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
|
||||||
getResources().getText(R.string.share_map_subject).toString());
|
getResources().getText(R.string.share_map_subject).toString());
|
||||||
|
|
||||||
SharedPreferences prefs =
|
|
||||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
|
||||||
boolean shareUrlOnly = true;
|
boolean shareUrlOnly = true;
|
||||||
if (prefs != null) {
|
if (sharedPreferences != null) {
|
||||||
shareUrlOnly =
|
shareUrlOnly = sharedPreferences.getBoolean(
|
||||||
prefs.getBoolean(getString(R.string.share_url_only_key), false);
|
getString(R.string.share_url_only_key), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
String url = MyMapsConstants.MAPSHOP_BASE_URL + "?msa=0&msid=" + mapId;
|
String url = MyMapsConstants.MAPSHOP_BASE_URL + "?msa=0&msid=" + mapId;
|
||||||
String msg = (shareUrlOnly
|
String msg = shareUrlOnly ? url : String.format(
|
||||||
? url
|
getResources().getText(R.string.share_map_body_format).toString(), url);
|
||||||
: String.format(
|
|
||||||
getResources().getText(R.string.share_map_body_format).toString(),
|
|
||||||
url));
|
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, msg);
|
shareIntent.putExtra(Intent.EXTRA_TEXT, msg);
|
||||||
startActivity(Intent.createChooser(shareIntent,
|
startActivity(Intent.createChooser(shareIntent,
|
||||||
getResources().getText(R.string.share_map).toString()));
|
getResources().getText(R.string.share_map).toString()));
|
||||||
@@ -1235,9 +1253,10 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
private void startRecordingNewTrack() {
|
private void startRecordingNewTrack() {
|
||||||
try {
|
try {
|
||||||
recordingTrackId = trackRecordingService.startNewTrack();
|
recordingTrackId = trackRecordingService.startNewTrack();
|
||||||
|
// Select the recording track.
|
||||||
|
setSelectedTrack(recordingTrackId);
|
||||||
Toast.makeText(this, getString(R.string.status_now_recording),
|
Toast.makeText(this, getString(R.string.status_now_recording),
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
setSelectedAndRecordingTrack(recordingTrackId, recordingTrackId);
|
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Toast.makeText(this,
|
Toast.makeText(this,
|
||||||
getString(R.string.error_unable_to_start_recording),
|
getString(R.string.error_unable_to_start_recording),
|
||||||
@@ -1272,7 +1291,6 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(MyTracksConstants.TAG, "Unable to stop recording.", e);
|
Log.e(MyTracksConstants.TAG, "Unable to stop recording.", e);
|
||||||
}
|
}
|
||||||
setRecordingTrack(-1);
|
|
||||||
Intent intent = new Intent(MyTracks.this, MyTracksDetails.class);
|
Intent intent = new Intent(MyTracks.this, MyTracksDetails.class);
|
||||||
intent.putExtra("trackid", recordingTrackId);
|
intent.putExtra("trackid", recordingTrackId);
|
||||||
intent.putExtra("hasCancelButton", false);
|
intent.putExtra("hasCancelButton", false);
|
||||||
@@ -1327,9 +1345,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
public void setSelectedTrack(final long trackId) {
|
public void setSelectedTrack(final long trackId) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
SharedPreferences prefs =
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putLong(getString(R.string.selected_track_key), trackId);
|
editor.putLong(getString(R.string.selected_track_key), trackId);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
@@ -1340,44 +1356,6 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
return selectedTrackId;
|
return selectedTrackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the recording track id to the shared preferences.
|
|
||||||
* Executed on the UI thread.
|
|
||||||
*
|
|
||||||
* @param trackId the id of the track
|
|
||||||
*/
|
|
||||||
private void setRecordingTrack(final long trackId) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
SharedPreferences prefs =
|
|
||||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putLong(getString(R.string.recording_track_key), trackId);
|
|
||||||
editor.commit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the selected and the recording track id to the shared preferences.
|
|
||||||
* Executed on UI thread.
|
|
||||||
*/
|
|
||||||
private void setSelectedAndRecordingTrack(final long theSelectedTrackId,
|
|
||||||
final long theRecordingTrackId) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
SharedPreferences prefs =
|
|
||||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
|
||||||
if (prefs != null) {
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putLong(getString(R.string.selected_track_key), theSelectedTrackId);
|
|
||||||
editor.putLong(getString(R.string.recording_track_key), theRecordingTrackId);
|
|
||||||
editor.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds to track recording service if it is running.
|
* Binds to track recording service if it is running.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public class MyTracksMap extends MapActivity
|
|||||||
private double variation;
|
private double variation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From the shared preferences:
|
* From the shared preferences.
|
||||||
*/
|
*/
|
||||||
private int minRequiredAccuracy =
|
private int minRequiredAccuracy =
|
||||||
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY;
|
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY;
|
||||||
@@ -135,8 +135,8 @@ public class MyTracksMap extends MapActivity
|
|||||||
private Location currentLocation;
|
private Location currentLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A thread with a looper. Post to updateTrackHandler to execute Runnables on
|
* A thread with a looper. Post to updateTrackHandler to execute
|
||||||
* this thread.
|
* {@link Runnable}s on this thread.
|
||||||
*/
|
*/
|
||||||
private HandlerThread updateTrackThread;
|
private HandlerThread updateTrackThread;
|
||||||
|
|
||||||
@@ -146,6 +146,8 @@ public class MyTracksMap extends MapActivity
|
|||||||
private Handler updateTrackHandler;
|
private Handler updateTrackHandler;
|
||||||
|
|
||||||
private MyTracksProviderUtils providerUtils;
|
private MyTracksProviderUtils providerUtils;
|
||||||
|
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A runnable that updates the track from the provider (looking for points
|
* A runnable that updates the track from the provider (looking for points
|
||||||
@@ -331,12 +333,6 @@ public class MyTracksMap extends MapActivity
|
|||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
Log.d(MyTracksConstants.TAG, "MyTracksMap: ContentObserver.onChange");
|
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 (!isRecordingSelected()) {
|
if (!isRecordingSelected()) {
|
||||||
// No track, or one other than the recording track is selected,
|
// No track, or one other than the recording track is selected,
|
||||||
// don't bother.
|
// don't bother.
|
||||||
@@ -361,30 +357,25 @@ public class MyTracksMap extends MapActivity
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read shared preferences and register change listener:
|
// Read shared preferences and register change listener.
|
||||||
SharedPreferences preferences =
|
sharedPreferences = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
if (sharedPreferences != null) {
|
||||||
if (preferences != null) {
|
reloadSharedPreferences(sharedPreferences, null);
|
||||||
minRequiredAccuracy = preferences.getInt(
|
|
||||||
getString(R.string.min_required_accuracy_key),
|
|
||||||
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY);
|
|
||||||
recordingTrackId =
|
|
||||||
preferences.getLong(getString(R.string.recording_track_key), -1);
|
|
||||||
long selectedTrackId =
|
|
||||||
preferences.getLong(getString(R.string.selected_track_key), -1);
|
|
||||||
if (selectedTrackId >= 0) {
|
|
||||||
setSelectedTrack(selectedTrackId);
|
|
||||||
}
|
|
||||||
updateOptionsButton();
|
updateOptionsButton();
|
||||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
Log.d(MyTracksConstants.TAG, "MyTracksMap.onDestroy");
|
Log.d(MyTracksConstants.TAG, "MyTracksMap.onDestroy");
|
||||||
|
|
||||||
if (updateTrackThread != null) {
|
if (updateTrackThread != null) {
|
||||||
updateTrackThread.quit();
|
ApiFeatures.getInstance().getApiPlatformAdapter().stopHandlerThread(
|
||||||
|
updateTrackThread);
|
||||||
|
}
|
||||||
|
if (sharedPreferences != null) {
|
||||||
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
@@ -451,6 +442,9 @@ public class MyTracksMap extends MapActivity
|
|||||||
Log.d(MyTracksConstants.TAG, "MyTracksMap.onResume");
|
Log.d(MyTracksConstants.TAG, "MyTracksMap.onResume");
|
||||||
super.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
|
// Make sure any updates that might have happened are propagated to the
|
||||||
// Map overlay:
|
// Map overlay:
|
||||||
observer.onChange(false);
|
observer.onChange(false);
|
||||||
@@ -707,11 +701,14 @@ public class MyTracksMap extends MapActivity
|
|||||||
* @param trackId a given track id
|
* @param trackId a given track id
|
||||||
*/
|
*/
|
||||||
public void setSelectedTrack(final long trackId) {
|
public void setSelectedTrack(final long trackId) {
|
||||||
|
Log.d(MyTracksConstants.TAG, "MyTracksMap.setSelectedTrack: "
|
||||||
|
+ "selectedtTrackId = " + selectedTrackId + ", trackId = " + trackId);
|
||||||
|
|
||||||
if (selectedTrackId == trackId) {
|
if (selectedTrackId == trackId) {
|
||||||
// Selected track did not change, nothing to do.
|
// Selected track did not change, nothing to do.
|
||||||
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
|
mapOverlay.setTrackDrawingEnabled(isATrackSelected());
|
||||||
mapView.invalidate();
|
|
||||||
updateOptionsButton();
|
updateOptionsButton();
|
||||||
|
mapView.invalidate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -786,7 +783,7 @@ public class MyTracksMap extends MapActivity
|
|||||||
menu.setHeaderTitle(R.string.tracklist_this_track);
|
menu.setHeaderTitle(R.string.tracklist_this_track);
|
||||||
menu.add(0, MyTracksConstants.MENU_EDIT, 0,
|
menu.add(0, MyTracksConstants.MENU_EDIT, 0,
|
||||||
R.string.tracklist_edit_track);
|
R.string.tracklist_edit_track);
|
||||||
if (!MyTracks.getInstance().isRecording() || !isRecordingSelected()) {
|
if (!isRecordingSelected()) {
|
||||||
menu.add(0, MyTracksConstants.MENU_SEND_TO_GOOGLE, 0,
|
menu.add(0, MyTracksConstants.MENU_SEND_TO_GOOGLE, 0,
|
||||||
R.string.tracklist_send_to_google);
|
R.string.tracklist_send_to_google);
|
||||||
SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0,
|
SubMenu share = menu.addSubMenu(0, MyTracksConstants.MENU_SHARE, 0,
|
||||||
@@ -906,30 +903,13 @@ public class MyTracksMap extends MapActivity
|
|||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(
|
public void onSharedPreferenceChanged(
|
||||||
final SharedPreferences sharedPreferences, final String key) {
|
final SharedPreferences sharedPreferences, final String key) {
|
||||||
|
Log.d(MyTracksConstants.TAG,
|
||||||
|
"MyTracksMap.onSharedPreferenceChanged: " + key);
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
uiHandler.post(new Runnable() {
|
uiHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (key.equals(getString(R.string.min_required_accuracy_key))) {
|
reloadSharedPreferences(sharedPreferences, key);
|
||||||
minRequiredAccuracy = sharedPreferences.getInt(
|
|
||||||
getString(R.string.min_required_accuracy_key),
|
|
||||||
MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY);
|
|
||||||
} else if (key.equals(getString(R.string.selected_track_key))) {
|
|
||||||
long selectedTrackId =
|
|
||||||
sharedPreferences.getLong(
|
|
||||||
getString(R.string.selected_track_key),
|
|
||||||
-1);
|
|
||||||
setSelectedTrack(selectedTrackId);
|
|
||||||
} else if (key.equals(getString(R.string.recording_track_key))) {
|
|
||||||
recordingTrackId =
|
|
||||||
sharedPreferences.getLong(
|
|
||||||
getString(R.string.recording_track_key),
|
|
||||||
-1);
|
|
||||||
if (isATrackSelected()) {
|
|
||||||
mapOverlay.setShowEndMarker(!isRecordingSelected());
|
|
||||||
mapView.postInvalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1009,6 +989,31 @@ public class MyTracksMap extends MapActivity
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void readAllNewTrackPoints() {
|
private void readAllNewTrackPoints() {
|
||||||
int numPoints = mapOverlay.getNumLocations();
|
int numPoints = mapOverlay.getNumLocations();
|
||||||
if (numPoints >= MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS) {
|
if (numPoints >= MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS) {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ import android.widget.Toast;
|
|||||||
public class StatsActivity extends Activity
|
public class StatsActivity extends Activity
|
||||||
implements OnSharedPreferenceChangeListener {
|
implements OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
private final StatsUtilities utils;
|
private StatsUtilities utils;
|
||||||
private UIUpdateThread thread;
|
private UIUpdateThread thread;
|
||||||
|
|
||||||
private ContentObserver observer;
|
private ContentObserver observer;
|
||||||
@@ -94,7 +94,7 @@ public class StatsActivity extends Activity
|
|||||||
*/
|
*/
|
||||||
private boolean showCurrentSegment = false;
|
private boolean showCurrentSegment = false;
|
||||||
|
|
||||||
private final MyTracksProviderUtils providerUtils;
|
private MyTracksProviderUtils providerUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A runnable for posting to the UI thread. Will update the total time field.
|
* A runnable for posting to the UI thread. Will update the total time field.
|
||||||
@@ -133,16 +133,14 @@ public class StatsActivity extends Activity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public StatsActivity() {
|
|
||||||
utils = new StatsUtilities(this);
|
|
||||||
providerUtils = new MyTracksProviderUtilsImpl(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called when the activity is first created. */
|
/** Called when the activity is first created. */
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
utils = new StatsUtilities(this);
|
||||||
|
providerUtils = new MyTracksProviderUtilsImpl(getContentResolver());
|
||||||
|
|
||||||
// The volume we want to control is the Text-To-Speech volume
|
// The volume we want to control is the Text-To-Speech volume
|
||||||
int volumeStream =
|
int volumeStream =
|
||||||
new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream();
|
new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream();
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ public interface MyTracksProviderUtils {
|
|||||||
* Creates an instance of {@link MyTracksProviderUtils}.
|
* Creates an instance of {@link MyTracksProviderUtils}.
|
||||||
*/
|
*/
|
||||||
protected MyTracksProviderUtils newForContext(Context context) {
|
protected MyTracksProviderUtils newForContext(Context context) {
|
||||||
return new MyTracksProviderUtilsImpl(context);
|
return new MyTracksProviderUtilsImpl(context.getContentResolver());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-34
@@ -17,8 +17,8 @@ package com.google.android.apps.mytracks.content;
|
|||||||
|
|
||||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -35,10 +35,10 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||||
|
|
||||||
private final Context context;
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
public MyTracksProviderUtilsImpl(Context context) {
|
public MyTracksProviderUtilsImpl(ContentResolver contentResolver) {
|
||||||
this.context = context;
|
this.contentResolver = contentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -448,25 +448,22 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteAllTracks() {
|
public void deleteAllTracks() {
|
||||||
context.getContentResolver().delete(TracksColumns.CONTENT_URI, null, null);
|
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
||||||
context.getContentResolver().delete(TrackPointsColumns.CONTENT_URI,
|
contentResolver.delete(TrackPointsColumns.CONTENT_URI, null, null);
|
||||||
null, null);
|
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
||||||
context.getContentResolver().delete(
|
|
||||||
WaypointsColumns.CONTENT_URI, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteTrack(long trackId) {
|
public void deleteTrack(long trackId) {
|
||||||
Track track = getTrack(trackId);
|
Track track = getTrack(trackId);
|
||||||
if (track != null) {
|
if (track != null) {
|
||||||
context.getContentResolver().delete(TrackPointsColumns.CONTENT_URI,
|
contentResolver.delete(TrackPointsColumns.CONTENT_URI,
|
||||||
"_id>=" + track.getStartId() + " AND _id<=" + track.getStopId(),
|
"_id>=" + track.getStartId() + " AND _id<=" + track.getStopId(),
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
context.getContentResolver().delete(WaypointsColumns.CONTENT_URI,
|
contentResolver.delete(WaypointsColumns.CONTENT_URI,
|
||||||
WaypointsColumns.TRACKID + "=" + trackId, null);
|
WaypointsColumns.TRACKID + "=" + trackId, null);
|
||||||
context.getContentResolver().delete(
|
contentResolver.delete(TracksColumns.CONTENT_URI, "_id=" + trackId, null);
|
||||||
TracksColumns.CONTENT_URI, "_id=" + trackId, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -491,7 +488,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
"No statistics marker after the deleted one was found.");
|
"No statistics marker after the deleted one was found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.getContentResolver().delete(
|
contentResolver.delete(
|
||||||
WaypointsColumns.CONTENT_URI, "_id=" + waypointId, null);
|
WaypointsColumns.CONTENT_URI, "_id=" + waypointId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,7 +500,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
final String sortOrder = WaypointsColumns._ID + " LIMIT 1";
|
final String sortOrder = WaypointsColumns._ID + " LIMIT 1";
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
cursor = context.getContentResolver().query(
|
cursor = contentResolver.query(
|
||||||
WaypointsColumns.CONTENT_URI,
|
WaypointsColumns.CONTENT_URI,
|
||||||
null /*projection*/,
|
null /*projection*/,
|
||||||
selection,
|
selection,
|
||||||
@@ -525,7 +522,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public boolean updateWaypoint(Waypoint waypoint) {
|
public boolean updateWaypoint(Waypoint waypoint) {
|
||||||
try {
|
try {
|
||||||
final int rows = context.getContentResolver().update(
|
final int rows = contentResolver.update(
|
||||||
WaypointsColumns.CONTENT_URI,
|
WaypointsColumns.CONTENT_URI,
|
||||||
createContentValues(waypoint),
|
createContentValues(waypoint),
|
||||||
"_id=" + waypoint.getId(),
|
"_id=" + waypoint.getId(),
|
||||||
@@ -546,7 +543,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
private Location findLocationBy(String select) {
|
private Location findLocationBy(String select) {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
cursor = context.getContentResolver().query(
|
cursor = contentResolver.query(
|
||||||
TrackPointsColumns.CONTENT_URI, null, select, null, null);
|
TrackPointsColumns.CONTENT_URI, null, select, null, null);
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
return createLocation(cursor);
|
return createLocation(cursor);
|
||||||
@@ -570,7 +567,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
private Track findTrackBy(String select) {
|
private Track findTrackBy(String select) {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
cursor = context.getContentResolver().query(
|
cursor = contentResolver.query(
|
||||||
TracksColumns.CONTENT_URI, null, select, null, null);
|
TracksColumns.CONTENT_URI, null, select, null, null);
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
return createTrack(cursor);
|
return createTrack(cursor);
|
||||||
@@ -592,7 +589,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Waypoint getFirstWaypoint(long trackId) {
|
public Waypoint getFirstWaypoint(long trackId) {
|
||||||
Cursor cursor = context.getContentResolver().query(
|
Cursor cursor = contentResolver.query(
|
||||||
WaypointsColumns.CONTENT_URI,
|
WaypointsColumns.CONTENT_URI,
|
||||||
null /*projection*/,
|
null /*projection*/,
|
||||||
"trackid=" + trackId,
|
"trackid=" + trackId,
|
||||||
@@ -614,7 +611,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Waypoint getWaypoint(long waypointId) {
|
public Waypoint getWaypoint(long waypointId) {
|
||||||
Cursor cursor = context.getContentResolver().query(
|
Cursor cursor = contentResolver.query(
|
||||||
WaypointsColumns.CONTENT_URI,
|
WaypointsColumns.CONTENT_URI,
|
||||||
null /*projection*/,
|
null /*projection*/,
|
||||||
"_id=" + waypointId,
|
"_id=" + waypointId,
|
||||||
@@ -637,7 +634,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public long getLastLocationId(long trackId) {
|
public long getLastLocationId(long trackId) {
|
||||||
final String[] projection = {"_id"};
|
final String[] projection = {"_id"};
|
||||||
Cursor cursor = context.getContentResolver().query(
|
Cursor cursor = contentResolver.query(
|
||||||
TrackPointsColumns.CONTENT_URI,
|
TrackPointsColumns.CONTENT_URI,
|
||||||
projection,
|
projection,
|
||||||
"_id=(select max(_id) from trackpoints WHERE trackid=" + trackId + ")",
|
"_id=(select max(_id) from trackpoints WHERE trackid=" + trackId + ")",
|
||||||
@@ -661,7 +658,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public long getFirstWaypointId(long trackId) {
|
public long getFirstWaypointId(long trackId) {
|
||||||
final String[] projection = {"_id"};
|
final String[] projection = {"_id"};
|
||||||
Cursor cursor = context.getContentResolver().query(
|
Cursor cursor = contentResolver.query(
|
||||||
WaypointsColumns.CONTENT_URI,
|
WaypointsColumns.CONTENT_URI,
|
||||||
projection,
|
projection,
|
||||||
"trackid=" + trackId,
|
"trackid=" + trackId,
|
||||||
@@ -685,7 +682,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public long getLastWaypointId(long trackId) {
|
public long getLastWaypointId(long trackId) {
|
||||||
final String[] projection = {"_id"};
|
final String[] projection = {"_id"};
|
||||||
Cursor cursor = context.getContentResolver().query(
|
Cursor cursor = contentResolver.query(
|
||||||
WaypointsColumns.CONTENT_URI,
|
WaypointsColumns.CONTENT_URI,
|
||||||
projection,
|
projection,
|
||||||
WaypointsColumns.TRACKID + "=" + trackId,
|
WaypointsColumns.TRACKID + "=" + trackId,
|
||||||
@@ -710,7 +707,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
public Track getLastTrack() {
|
public Track getLastTrack() {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
cursor = context.getContentResolver().query(
|
cursor = contentResolver.query(
|
||||||
TracksColumns.CONTENT_URI, null, "_id=(select max(_id) from tracks)",
|
TracksColumns.CONTENT_URI, null, "_id=(select max(_id) from tracks)",
|
||||||
null, null);
|
null, null);
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
@@ -729,7 +726,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public long getLastTrackId() {
|
public long getLastTrackId() {
|
||||||
String[] proj = { TracksColumns._ID };
|
String[] proj = { TracksColumns._ID };
|
||||||
Cursor cursor = context.getContentResolver().query(
|
Cursor cursor = contentResolver.query(
|
||||||
TracksColumns.CONTENT_URI, proj, "_id=(select max(_id) from tracks)",
|
TracksColumns.CONTENT_URI, proj, "_id=(select max(_id) from tracks)",
|
||||||
null, null);
|
null, null);
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
@@ -769,7 +766,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
sortOrder += " LIMIT " + maxLocations;
|
sortOrder += " LIMIT " + maxLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.getContentResolver().query(
|
return contentResolver.query(
|
||||||
TrackPointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
TrackPointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -791,7 +788,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
sortOrder += " LIMIT " + maxWaypoints;
|
sortOrder += " LIMIT " + maxWaypoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.getContentResolver().query(
|
return contentResolver.query(
|
||||||
WaypointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
WaypointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,7 +844,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor getTracksCursor(String selection) {
|
public Cursor getTracksCursor(String selection) {
|
||||||
Cursor cursor = context.getContentResolver().query(
|
Cursor cursor = contentResolver.query(
|
||||||
TracksColumns.CONTENT_URI, null, selection, null, "_id");
|
TracksColumns.CONTENT_URI, null, selection, null, "_id");
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
@@ -855,14 +852,14 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public Uri insertTrack(Track track) {
|
public Uri insertTrack(Track track) {
|
||||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrack");
|
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrack");
|
||||||
return context.getContentResolver().insert(TracksColumns.CONTENT_URI,
|
return contentResolver.insert(TracksColumns.CONTENT_URI,
|
||||||
createContentValues(track));
|
createContentValues(track));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri insertTrackPoint(Location location, long trackId) {
|
public Uri insertTrackPoint(Location location, long trackId) {
|
||||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrackPoint");
|
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrackPoint");
|
||||||
return context.getContentResolver().insert(TrackPointsColumns.CONTENT_URI,
|
return contentResolver.insert(TrackPointsColumns.CONTENT_URI,
|
||||||
createContentValues(location, trackId));
|
createContentValues(location, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -875,14 +872,14 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
values[i] = createContentValues(locations[i], trackId);
|
values[i] = createContentValues(locations[i], trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.getContentResolver().bulkInsert(TrackPointsColumns.CONTENT_URI, values);
|
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri insertWaypoint(Waypoint waypoint) {
|
public Uri insertWaypoint(Waypoint waypoint) {
|
||||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
|
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
|
||||||
waypoint.setId(-1);
|
waypoint.setId(-1);
|
||||||
return context.getContentResolver().insert(WaypointsColumns.CONTENT_URI,
|
return contentResolver.insert(WaypointsColumns.CONTENT_URI,
|
||||||
createContentValues(waypoint));
|
createContentValues(waypoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,7 +888,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
final String[] projection = { TracksColumns._ID };
|
final String[] projection = { TracksColumns._ID };
|
||||||
cursor = context.getContentResolver().query(
|
cursor = contentResolver.query(
|
||||||
TracksColumns.CONTENT_URI,
|
TracksColumns.CONTENT_URI,
|
||||||
projection,
|
projection,
|
||||||
TracksColumns._ID + "=" + id/*selection*/,
|
TracksColumns._ID + "=" + id/*selection*/,
|
||||||
@@ -911,7 +908,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public void updateTrack(Track track) {
|
public void updateTrack(Track track) {
|
||||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.updateTrack");
|
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.updateTrack");
|
||||||
context.getContentResolver().update(TracksColumns.CONTENT_URI,
|
contentResolver.update(TracksColumns.CONTENT_URI,
|
||||||
createContentValues(track), "_id=" + track.getId(), null);
|
createContentValues(track), "_id=" + track.getId(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -468,8 +468,7 @@ public class GpxImporter extends DefaultHandler {
|
|||||||
|
|
||||||
// initialize start time with time of first track point
|
// initialize start time with time of first track point
|
||||||
if (statsBuilder == null) {
|
if (statsBuilder == null) {
|
||||||
statsBuilder = new TripStatisticsBuilder();
|
statsBuilder = new TripStatisticsBuilder(time);
|
||||||
statsBuilder.resumeAt(time);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import java.util.Date;
|
|||||||
/**
|
/**
|
||||||
* Creates a default track name based on the current default track name policy.
|
* Creates a default track name based on the current default track name policy.
|
||||||
*
|
*
|
||||||
* @author simmonmt@google.com (Matthew Simmons)
|
* @author Matthew Simmons
|
||||||
*/
|
*/
|
||||||
class DefaultTrackNameFactory {
|
class DefaultTrackNameFactory {
|
||||||
private static final String TIMESTAMP_DATE_FORMAT = "yyyy-MM-dd HH:mm";
|
private static final String TIMESTAMP_DATE_FORMAT = "yyyy-MM-dd HH:mm";
|
||||||
@@ -60,13 +60,7 @@ class DefaultTrackNameFactory {
|
|||||||
protected boolean useTimestampTrackName() {
|
protected boolean useTimestampTrackName() {
|
||||||
SharedPreferences prefs =
|
SharedPreferences prefs =
|
||||||
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||||
|
return prefs.getBoolean(
|
||||||
boolean useTimestamp = false;
|
context.getString(R.string.timestamp_track_name_key), true);
|
||||||
if (prefs != null) {
|
|
||||||
useTimestamp = prefs.getBoolean(
|
|
||||||
context.getString(R.string.timestamp_track_name_key), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return useTimestamp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.google.android.apps.mytracks.MyTracksSettings;
|
|||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.Editor;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,6 +31,7 @@ import android.util.Log;
|
|||||||
public class PreferenceManager {
|
public class PreferenceManager {
|
||||||
private TrackRecordingService service;
|
private TrackRecordingService service;
|
||||||
private final String announcementFrequencyKey;
|
private final String announcementFrequencyKey;
|
||||||
|
private final String autoResumeTrackCurrentRetryKey;
|
||||||
private final String autoResumeTrackTimeoutKey;
|
private final String autoResumeTrackTimeoutKey;
|
||||||
private final String maxRecordingDistanceKey;
|
private final String maxRecordingDistanceKey;
|
||||||
private final String metricUnitsKey;
|
private final String metricUnitsKey;
|
||||||
@@ -40,11 +42,22 @@ public class PreferenceManager {
|
|||||||
private final String signalSamplingFrequencyKey;
|
private final String signalSamplingFrequencyKey;
|
||||||
private final String splitFrequencyKey;
|
private final String splitFrequencyKey;
|
||||||
|
|
||||||
|
private final SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
public PreferenceManager(TrackRecordingService service) {
|
public PreferenceManager(TrackRecordingService service) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
|
this.sharedPreferences =
|
||||||
|
service.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
||||||
|
if (sharedPreferences == null) {
|
||||||
|
Log.w(MyTracksConstants.TAG,
|
||||||
|
"TrackRecordingService: Couldn't get shared preferences.");
|
||||||
|
throw new IllegalStateException("Couldn't get shared preferences");
|
||||||
|
}
|
||||||
|
|
||||||
announcementFrequencyKey =
|
announcementFrequencyKey =
|
||||||
service.getString(R.string.announcement_frequency_key);
|
service.getString(R.string.announcement_frequency_key);
|
||||||
|
autoResumeTrackCurrentRetryKey =
|
||||||
|
service.getString(R.string.auto_resume_track_current_retry_key);
|
||||||
autoResumeTrackTimeoutKey =
|
autoResumeTrackTimeoutKey =
|
||||||
service.getString(R.string.auto_resume_track_timeout_key);
|
service.getString(R.string.auto_resume_track_timeout_key);
|
||||||
maxRecordingDistanceKey =
|
maxRecordingDistanceKey =
|
||||||
@@ -57,12 +70,12 @@ public class PreferenceManager {
|
|||||||
service.getString(R.string.min_recording_interval_key);
|
service.getString(R.string.min_recording_interval_key);
|
||||||
minRequiredAccuracyKey =
|
minRequiredAccuracyKey =
|
||||||
service.getString(R.string.min_required_accuracy_key);
|
service.getString(R.string.min_required_accuracy_key);
|
||||||
splitFrequencyKey =
|
|
||||||
service.getString(R.string.split_frequency_key);
|
|
||||||
signalSamplingFrequencyKey =
|
|
||||||
service.getString(R.string.signal_sampling_frequency_key);
|
|
||||||
recordingTrackKey =
|
recordingTrackKey =
|
||||||
service.getString(R.string.recording_track_key);
|
service.getString(R.string.recording_track_key);
|
||||||
|
signalSamplingFrequencyKey =
|
||||||
|
service.getString(R.string.signal_sampling_frequency_key);
|
||||||
|
splitFrequencyKey =
|
||||||
|
service.getString(R.string.split_frequency_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,14 +85,6 @@ public class PreferenceManager {
|
|||||||
* @param key the key that changed (may be null to update all preferences)
|
* @param key the key that changed (may be null to update all preferences)
|
||||||
*/
|
*/
|
||||||
public void onSharedPreferenceChanged(String key) {
|
public void onSharedPreferenceChanged(String key) {
|
||||||
SharedPreferences sharedPreferences =
|
|
||||||
service.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
|
||||||
if (sharedPreferences == null) {
|
|
||||||
Log.w(MyTracksConstants.TAG,
|
|
||||||
"TrackRecordingService: Couldn't get shared preferences.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == null || key.equals(minRecordingDistanceKey)) {
|
if (key == null || key.equals(minRecordingDistanceKey)) {
|
||||||
service.setMinRecordingDistance(
|
service.setMinRecordingDistance(
|
||||||
sharedPreferences.getInt(
|
sharedPreferences.getInt(
|
||||||
@@ -156,4 +161,16 @@ public class PreferenceManager {
|
|||||||
sharedPreferences.getBoolean(metricUnitsKey, true));
|
sharedPreferences.getBoolean(metricUnitsKey, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAutoResumeTrackCurrentRetry(int retryAttempts) {
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putInt(autoResumeTrackCurrentRetryKey, retryAttempts);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecordingTrack(long id) {
|
||||||
|
Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putLong(recordingTrackKey, id);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.speech.tts.TextToSpeech;
|
import android.speech.tts.TextToSpeech;
|
||||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||||
|
import android.telephony.PhoneStateListener;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -63,6 +65,26 @@ public class StatusAnnouncerTask implements PeriodicTask {
|
|||||||
*/
|
*/
|
||||||
private boolean ready = false;
|
private boolean ready = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we're allowed to speak right now.
|
||||||
|
*/
|
||||||
|
private boolean speechAllowed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener which updates {@link #speechAllowed} when the phone state changes.
|
||||||
|
*/
|
||||||
|
private final PhoneStateListener phoneListener = new PhoneStateListener() {
|
||||||
|
@Override
|
||||||
|
public void onCallStateChanged(int state, String incomingNumber) {
|
||||||
|
speechAllowed = state == TelephonyManager.CALL_STATE_IDLE;
|
||||||
|
|
||||||
|
if (!speechAllowed && tts.isSpeaking()) {
|
||||||
|
// If we're already speaking, stop it.
|
||||||
|
tts.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the announcer and start the TTS engine.
|
* Constructs the announcer and start the TTS engine.
|
||||||
*/
|
*/
|
||||||
@@ -94,8 +116,8 @@ public class StatusAnnouncerTask implements PeriodicTask {
|
|||||||
int languageAvailability = tts.isLanguageAvailable(speechLanguage);
|
int languageAvailability = tts.isLanguageAvailable(speechLanguage);
|
||||||
if (languageAvailability == TextToSpeech.LANG_MISSING_DATA ||
|
if (languageAvailability == TextToSpeech.LANG_MISSING_DATA ||
|
||||||
languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
|
languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||||
// English is probably supported
|
// English is probably supported.
|
||||||
// TODO: Somehow use announcement strings from English too
|
// TODO: Somehow use announcement strings from English too.
|
||||||
Log.w(MyTracksConstants.TAG, "Default language not available, using English.");
|
Log.w(MyTracksConstants.TAG, "Default language not available, using English.");
|
||||||
speechLanguage = Locale.ENGLISH;
|
speechLanguage = Locale.ENGLISH;
|
||||||
}
|
}
|
||||||
@@ -116,6 +138,12 @@ public class StatusAnnouncerTask implements PeriodicTask {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!speechAllowed) {
|
||||||
|
Log.i(MyTracksConstants.TAG,
|
||||||
|
"Not making announcement - not allowed at this time");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (service == null || service.getTripStatistics() == null) {
|
if (service == null || service.getTripStatistics() == null) {
|
||||||
Log.e(MyTracksConstants.TAG, "StatusAnnouncer stats not initialized.");
|
Log.e(MyTracksConstants.TAG, "StatusAnnouncer stats not initialized.");
|
||||||
return;
|
return;
|
||||||
@@ -193,12 +221,23 @@ public class StatusAnnouncerTask implements PeriodicTask {
|
|||||||
context.getString(speedLabel));
|
context.getString(speedLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
@Override
|
||||||
tts.shutdown();
|
public void start() {
|
||||||
|
// Register ourselves as a listener so we won't speak during a call.
|
||||||
|
speechAllowed = true;
|
||||||
|
TelephonyManager telephony =
|
||||||
|
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||||
|
telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void shutdown() {
|
||||||
|
// Stop listening to phone state.
|
||||||
|
TelephonyManager telephony =
|
||||||
|
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||||
|
telephony.listen(phoneListener, PhoneStateListener.LISTEN_NONE);
|
||||||
|
|
||||||
|
tts.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getVolumeStream() {
|
public static int getVolumeStream() {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.google.android.apps.mytracks.content.WaypointsColumns;
|
|||||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||||
|
import com.google.android.apps.mytracks.util.ApiPlatformAdapter;
|
||||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
@@ -69,7 +70,7 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
private static final String STATISTICS_ICON_URL =
|
private static final String STATISTICS_ICON_URL =
|
||||||
"http://maps.google.com/mapfiles/ms/micons/ylw-pushpin.png";
|
"http://maps.google.com/mapfiles/ms/micons/ylw-pushpin.png";
|
||||||
|
|
||||||
private static final int MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS = 3;
|
static final int MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS = 3;
|
||||||
|
|
||||||
private NotificationManager notificationManager;
|
private NotificationManager notificationManager;
|
||||||
private LocationManager locationManager;
|
private LocationManager locationManager;
|
||||||
@@ -96,8 +97,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
*/
|
*/
|
||||||
private MyTracksProviderUtils providerUtils;
|
private MyTracksProviderUtils providerUtils;
|
||||||
|
|
||||||
private TripStatisticsBuilder statsBuilder = new TripStatisticsBuilder();
|
private TripStatisticsBuilder statsBuilder;
|
||||||
private TripStatisticsBuilder waypointStatsBuilder = new TripStatisticsBuilder();
|
private TripStatisticsBuilder waypointStatsBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current length of the recorded track. This length is calculated from the
|
* Current length of the recorded track. This length is calculated from the
|
||||||
@@ -331,6 +332,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
* Shows the notification message and icon in the notification bar.
|
* Shows the notification message and icon in the notification bar.
|
||||||
*/
|
*/
|
||||||
public void showNotification() {
|
public void showNotification() {
|
||||||
|
final ApiPlatformAdapter apiPlatformAdapter =
|
||||||
|
ApiFeatures.getInstance().getApiPlatformAdapter();
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
Notification notification = new Notification(
|
Notification notification = new Notification(
|
||||||
R.drawable.arrow_320, null /* tickerText */,
|
R.drawable.arrow_320, null /* tickerText */,
|
||||||
@@ -341,9 +344,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
notification.setLatestEventInfo(this, getString(R.string.app_name),
|
notification.setLatestEventInfo(this, getString(R.string.app_name),
|
||||||
getString(R.string.recording_your_track), contentIntent);
|
getString(R.string.recording_your_track), contentIntent);
|
||||||
notification.flags += Notification.FLAG_NO_CLEAR;
|
notification.flags += Notification.FLAG_NO_CLEAR;
|
||||||
notificationManager.notify(1, notification);
|
apiPlatformAdapter.startForeground(this, notificationManager, 1,
|
||||||
|
notification);
|
||||||
} else {
|
} else {
|
||||||
notificationManager.cancelAll();
|
apiPlatformAdapter.stopForeground(this, notificationManager, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,9 +396,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void restoreStats(Track track) {
|
private void restoreStats(Track track) {
|
||||||
|
Log.d(MyTracksConstants.TAG,
|
||||||
|
"Restoring stats of track with ID: " + track.getId());
|
||||||
|
|
||||||
TripStatistics stats = track.getStatistics();
|
TripStatistics stats = track.getStatistics();
|
||||||
statsBuilder = new TripStatisticsBuilder();
|
statsBuilder = new TripStatisticsBuilder(stats.getStartTime());
|
||||||
statsBuilder.resumeAt(stats.getStartTime());
|
|
||||||
setUpAnnouncer();
|
setUpAnnouncer();
|
||||||
|
|
||||||
signalManager.restore();
|
signalManager.restore();
|
||||||
@@ -405,11 +411,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
Waypoint waypoint = providerUtils.getFirstWaypoint(recordingTrackId);
|
Waypoint waypoint = providerUtils.getFirstWaypoint(recordingTrackId);
|
||||||
if (waypoint != null) {
|
if (waypoint != null) {
|
||||||
currentWaypointId = waypoint.getId();
|
currentWaypointId = waypoint.getId();
|
||||||
waypointStatsBuilder = new TripStatisticsBuilder(waypoint.getStatistics());
|
waypointStatsBuilder = new TripStatisticsBuilder(
|
||||||
|
waypoint.getStatistics());
|
||||||
} else {
|
} else {
|
||||||
// This should never happen, but we got to do something so life goes on:
|
// This should never happen, but we got to do something so life goes on:
|
||||||
waypointStatsBuilder = new TripStatisticsBuilder();
|
waypointStatsBuilder = new TripStatisticsBuilder(stats.getStartTime());
|
||||||
waypointStatsBuilder.resumeAt(stats.getStartTime());
|
|
||||||
currentWaypointId = -1;
|
currentWaypointId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +439,7 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
}
|
}
|
||||||
statsBuilder.getStatistics().setMovingTime(stats.getMovingTime());
|
statsBuilder.getStatistics().setMovingTime(stats.getMovingTime());
|
||||||
statsBuilder.pauseAt(stats.getStopTime());
|
statsBuilder.pauseAt(stats.getStopTime());
|
||||||
statsBuilder.resume();
|
statsBuilder.resumeAt(System.currentTimeMillis());
|
||||||
} else {
|
} else {
|
||||||
Log.e(MyTracksConstants.TAG, "Could not get track points cursor.");
|
Log.e(MyTracksConstants.TAG, "Could not get track points cursor.");
|
||||||
}
|
}
|
||||||
@@ -658,6 +664,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
if (recordingTrack != null) {
|
if (recordingTrack != null) {
|
||||||
restoreStats(recordingTrack);
|
restoreStats(recordingTrack);
|
||||||
isRecording = true;
|
isRecording = true;
|
||||||
|
} else {
|
||||||
|
// Make sure we have consistent state in shared preferences.
|
||||||
|
Log.w(MyTracksConstants.TAG, "TrackRecordingService.onCreate: Resetting "
|
||||||
|
+ "an orphaned recording track: " + recordingTrackId);
|
||||||
|
prefManager.setRecordingTrack(recordingTrackId = -1);
|
||||||
}
|
}
|
||||||
showNotification();
|
showNotification();
|
||||||
}
|
}
|
||||||
@@ -741,8 +752,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
// Make sure that the current track exists and is fresh enough.
|
// Make sure that the current track exists and is fresh enough.
|
||||||
if (recordingTrack == null || !shouldResumeTrack(recordingTrack)) {
|
if (recordingTrack == null || !shouldResumeTrack(recordingTrack)) {
|
||||||
Log.i(MyTracksConstants.TAG,
|
Log.i(MyTracksConstants.TAG,
|
||||||
"TrackRecordingService: Not resuming because the previous track "
|
"TrackRecordingService: Not resuming, because the previous track ("
|
||||||
+ "doesn't exist or is too old");
|
+ recordingTrack + ") doesn't exist or is too old");
|
||||||
|
isRecording = false;
|
||||||
|
prefManager.setRecordingTrack(recordingTrackId = -1);
|
||||||
stopSelfResult(startId);
|
stopSelfResult(startId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -755,16 +768,12 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
SharedPreferences sharedPreferences, int retryAttempts) {
|
SharedPreferences sharedPreferences, int retryAttempts) {
|
||||||
Log.d(MyTracksConstants.TAG,
|
Log.d(MyTracksConstants.TAG,
|
||||||
"Updating auto-resume retry attempts to: " + retryAttempts);
|
"Updating auto-resume retry attempts to: " + retryAttempts);
|
||||||
|
prefManager.setAutoResumeTrackCurrentRetry(retryAttempts);
|
||||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
|
||||||
editor.putInt(
|
|
||||||
getString(R.string.auto_resume_track_current_retry_key), retryAttempts);
|
|
||||||
editor.commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldResumeTrack(Track track) {
|
private boolean shouldResumeTrack(Track track) {
|
||||||
Log.d(MyTracksConstants.TAG,
|
Log.d(MyTracksConstants.TAG, "shouldResumeTrack: autoResumeTrackTimeout = "
|
||||||
"maybeResumeTrack: autoResumeTrackTimeout = " + autoResumeTrackTimeout);
|
+ autoResumeTrackTimeout);
|
||||||
|
|
||||||
// Check if we haven't exceeded the maximum number of retry attempts.
|
// Check if we haven't exceeded the maximum number of retry attempts.
|
||||||
SharedPreferences sharedPreferences =
|
SharedPreferences sharedPreferences =
|
||||||
@@ -772,11 +781,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
int retries = sharedPreferences.getInt(
|
int retries = sharedPreferences.getInt(
|
||||||
getString(R.string.auto_resume_track_current_retry_key), 0);
|
getString(R.string.auto_resume_track_current_retry_key), 0);
|
||||||
Log.d(MyTracksConstants.TAG,
|
Log.d(MyTracksConstants.TAG,
|
||||||
"TrackRecordingService: Attempting to auto-resume the track ("
|
"shouldResumeTrack: Attempting to auto-resume the track ("
|
||||||
+ (retries + 1) + "/" + MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS + ")");
|
+ (retries + 1) + "/" + MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS + ")");
|
||||||
if (retries >= MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS) {
|
if (retries >= MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS) {
|
||||||
Log.i(MyTracksConstants.TAG,
|
Log.i(MyTracksConstants.TAG,
|
||||||
"TrackRecordingService: Not resuming because exceeded the maximum "
|
"shouldResumeTrack: Not resuming because exceeded the maximum "
|
||||||
+ "number of auto-resume retries");
|
+ "number of auto-resume retries");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -787,9 +796,13 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
// Check for special cases.
|
// Check for special cases.
|
||||||
if (autoResumeTrackTimeout == 0) {
|
if (autoResumeTrackTimeout == 0) {
|
||||||
// Never resume.
|
// Never resume.
|
||||||
|
Log.d(MyTracksConstants.TAG,
|
||||||
|
"shouldResumeTrack: Auto-resume disabled (never resume)");
|
||||||
return false;
|
return false;
|
||||||
} else if (autoResumeTrackTimeout == -1) {
|
} else if (autoResumeTrackTimeout == -1) {
|
||||||
// Always resume.
|
// Always resume.
|
||||||
|
Log.d(MyTracksConstants.TAG,
|
||||||
|
"shouldResumeTrack: Auto-resume forced (always resume)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +810,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
long lastModified =
|
long lastModified =
|
||||||
track.getStatistics() != null ? track.getStatistics().getStopTime() : 0;
|
track.getStatistics() != null ? track.getStatistics().getStopTime() : 0;
|
||||||
Log.d(MyTracksConstants.TAG,
|
Log.d(MyTracksConstants.TAG,
|
||||||
"maybeResumeTrack: lastModified = " + lastModified);
|
"shouldResumeTrack: lastModified = " + lastModified
|
||||||
|
+ ", autoResumeTrackTimeout: " + autoResumeTrackTimeout);
|
||||||
return lastModified > 0 && System.currentTimeMillis() - lastModified <=
|
return lastModified > 0 && System.currentTimeMillis() - lastModified <=
|
||||||
autoResumeTrackTimeout * 60 * 1000;
|
autoResumeTrackTimeout * 60 * 1000;
|
||||||
}
|
}
|
||||||
@@ -807,6 +821,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long insertWaypointMarker(Waypoint waypoint) {
|
public long insertWaypointMarker(Waypoint waypoint) {
|
||||||
|
if (!isRecording()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unable to insert waypoint marker while not recording!");
|
||||||
|
}
|
||||||
|
|
||||||
if (waypoint.getLocation() != null) {
|
if (waypoint.getLocation() != null) {
|
||||||
waypoint.setLength(length);
|
waypoint.setLength(length);
|
||||||
waypoint.setDuration(waypoint.getLocation().getTime()
|
waypoint.setDuration(waypoint.getLocation().getTime()
|
||||||
@@ -825,6 +844,11 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
* @return the unique id of the inserted marker
|
* @return the unique id of the inserted marker
|
||||||
*/
|
*/
|
||||||
public long insertStatisticsMarker(Location location) {
|
public long insertStatisticsMarker(Location location) {
|
||||||
|
if (!isRecording()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unable to insert statistics marker while not recording!");
|
||||||
|
}
|
||||||
|
|
||||||
StringUtils utils = new StringUtils(TrackRecordingService.this);
|
StringUtils utils = new StringUtils(TrackRecordingService.this);
|
||||||
|
|
||||||
// Create a new waypoint to save
|
// Create a new waypoint to save
|
||||||
@@ -852,8 +876,7 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
Uri uri = providerUtils.insertWaypoint(waypoint);
|
Uri uri = providerUtils.insertWaypoint(waypoint);
|
||||||
|
|
||||||
// Create a new stats keeper for the next marker
|
// Create a new stats keeper for the next marker
|
||||||
waypointStatsBuilder = new TripStatisticsBuilder();
|
waypointStatsBuilder = new TripStatisticsBuilder(time);
|
||||||
waypointStatsBuilder.resumeAt(time);
|
|
||||||
updateCurrentWaypoint();
|
updateCurrentWaypoint();
|
||||||
return Long.parseLong(uri.getLastPathSegment());
|
return Long.parseLong(uri.getLastPathSegment());
|
||||||
}
|
}
|
||||||
@@ -910,6 +933,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
@Override
|
@Override
|
||||||
public void endCurrentTrack() {
|
public void endCurrentTrack() {
|
||||||
Log.d(MyTracksConstants.TAG, "TrackRecordingService.endCurrentTrack");
|
Log.d(MyTracksConstants.TAG, "TrackRecordingService.endCurrentTrack");
|
||||||
|
if (recordingTrackId == -1 || !isRecording) {
|
||||||
|
throw new IllegalStateException("No recording track in progress!");
|
||||||
|
}
|
||||||
|
|
||||||
isRecording = false;
|
isRecording = false;
|
||||||
Track recordingTrack = providerUtils.getTrack(recordingTrackId);
|
Track recordingTrack = providerUtils.getTrack(recordingTrackId);
|
||||||
if (recordingTrack != null) {
|
if (recordingTrack != null) {
|
||||||
@@ -929,12 +956,15 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
"_id=" + recordingTrack.getId(), null);
|
"_id=" + recordingTrack.getId(), null);
|
||||||
}
|
}
|
||||||
showNotification();
|
showNotification();
|
||||||
recordingTrackId = -1;
|
prefManager.setRecordingTrack(recordingTrackId = -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteAllTracks() {
|
public void deleteAllTracks() {
|
||||||
endCurrentTrack();
|
if (isRecording()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot delete all tracks while recording!");
|
||||||
|
}
|
||||||
providerUtils.deleteAllTracks();
|
providerUtils.deleteAllTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,6 +983,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
|
|
||||||
public long startNewTrack() {
|
public long startNewTrack() {
|
||||||
Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack");
|
Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack");
|
||||||
|
if (recordingTrackId != -1 || isRecording) {
|
||||||
|
throw new IllegalStateException("A track is already in progress!");
|
||||||
|
}
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
@@ -964,26 +998,29 @@ public class TrackRecordingService extends Service implements LocationListener {
|
|||||||
track.setId(recordingTrackId);
|
track.setId(recordingTrackId);
|
||||||
track.setName(new DefaultTrackNameFactory(this).newTrackName(
|
track.setName(new DefaultTrackNameFactory(this).newTrackName(
|
||||||
recordingTrackId, startTime));
|
recordingTrackId, startTime));
|
||||||
providerUtils.updateTrack(track);
|
|
||||||
|
|
||||||
currentWaypointId = insertStatisticsMarker(null);
|
|
||||||
isRecording = true;
|
isRecording = true;
|
||||||
isMoving = true;
|
isMoving = true;
|
||||||
statsBuilder = new TripStatisticsBuilder();
|
|
||||||
statsBuilder.resumeAt(startTime);
|
providerUtils.updateTrack(track);
|
||||||
|
statsBuilder = new TripStatisticsBuilder(startTime);
|
||||||
|
waypointStatsBuilder = new TripStatisticsBuilder(startTime);
|
||||||
|
currentWaypointId = insertStatisticsMarker(null);
|
||||||
setUpAnnouncer();
|
setUpAnnouncer();
|
||||||
length = 0;
|
length = 0;
|
||||||
showNotification();
|
showNotification();
|
||||||
registerLocationListener();
|
registerLocationListener();
|
||||||
splitManager.restore();
|
splitManager.restore();
|
||||||
signalManager.restore();
|
signalManager.restore();
|
||||||
|
|
||||||
// Reset the number of auto-resume retries.
|
// Reset the number of auto-resume retries.
|
||||||
SharedPreferences sharedPreferences =
|
setAutoResumeTrackRetries(
|
||||||
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0), 0);
|
||||||
setAutoResumeTrackRetries(sharedPreferences, 0);
|
// Persist the current recording track.
|
||||||
|
prefManager.setRecordingTrack(recordingTrackId);
|
||||||
|
|
||||||
return recordingTrackId;
|
return recordingTrackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
TripStatistics getTripStatistics() {
|
TripStatistics getTripStatistics() {
|
||||||
return statsBuilder.getStatistics();
|
return statsBuilder.getStatistics();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ public class TripStatisticsBuilder {
|
|||||||
/**
|
/**
|
||||||
* The last location that the gps reported.
|
* The last location that the gps reported.
|
||||||
*/
|
*/
|
||||||
private Location lastLocation = null;
|
private Location lastLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last location that contributed to the stats. It is also the last
|
* The last location that contributed to the stats. It is also the last
|
||||||
* location the user was found to be moving.
|
* location the user was found to be moving.
|
||||||
*/
|
*/
|
||||||
private Location lastMovingLocation = null;
|
private Location lastMovingLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current speed in meters/second as reported by the gps.
|
* The current speed in meters/second as reported by the gps.
|
||||||
@@ -90,10 +90,13 @@ public class TripStatisticsBuilder {
|
|||||||
private long totalLocations = 0;
|
private long totalLocations = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new trip starting at the current system time.
|
* Creates a new trip starting at the given time.
|
||||||
|
*
|
||||||
|
* @param startTime the start time.
|
||||||
*/
|
*/
|
||||||
public TripStatisticsBuilder() {
|
public TripStatisticsBuilder(long startTime) {
|
||||||
data = new TripStatistics();
|
data = new TripStatistics();
|
||||||
|
resumeAt(startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,6 +106,9 @@ public class TripStatisticsBuilder {
|
|||||||
*/
|
*/
|
||||||
public TripStatisticsBuilder(TripStatistics statsData) {
|
public TripStatisticsBuilder(TripStatistics statsData) {
|
||||||
data = new TripStatistics(statsData);
|
data = new TripStatistics(statsData);
|
||||||
|
if (data.getStartTime() > 0) {
|
||||||
|
resumeAt(data.getStartTime());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,8 +146,8 @@ public class TripStatisticsBuilder {
|
|||||||
|
|
||||||
// Don't do anything if we didn't move since last fix:
|
// Don't do anything if we didn't move since last fix:
|
||||||
double distance = lastLocation.distanceTo(currentLocation);
|
double distance = lastLocation.distanceTo(currentLocation);
|
||||||
if (distance < MyTracksConstants.MAX_NO_MOVEMENT_DISTANCE
|
if (distance < MyTracksConstants.MAX_NO_MOVEMENT_DISTANCE &&
|
||||||
&& currentSpeed < MyTracksConstants.MAX_NO_MOVEMENT_SPEED) {
|
currentSpeed < MyTracksConstants.MAX_NO_MOVEMENT_SPEED) {
|
||||||
lastLocation = currentLocation;
|
lastLocation = currentLocation;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -291,13 +297,6 @@ public class TripStatisticsBuilder {
|
|||||||
data.updateGradeExtremities(gradeBuffer.getAverage());
|
data.updateGradeExtremities(gradeBuffer.getAverage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the track at the current time.
|
|
||||||
*/
|
|
||||||
public void pause() {
|
|
||||||
pauseAt(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses the track at the given time.
|
* Pauses the track at the given time.
|
||||||
*
|
*
|
||||||
@@ -312,13 +311,6 @@ public class TripStatisticsBuilder {
|
|||||||
paused = true;
|
paused = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resumes the current track at the current time.
|
|
||||||
*/
|
|
||||||
public void resume() {
|
|
||||||
resumeAt(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resumes the current track at the given time.
|
* Resumes the current track at the given time.
|
||||||
*
|
*
|
||||||
@@ -364,8 +356,7 @@ public class TripStatisticsBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TripStatistics getStatistics() {
|
public TripStatistics getStatistics() {
|
||||||
// Take a snapshot - we do't want anyone messing with our internals
|
// Take a snapshot - we don't want anyone messing with our internals
|
||||||
return new TripStatistics(data);
|
return new TripStatistics(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.apps.mytracks.util;
|
package com.google.android.apps.mytracks.util;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for determining if newer-API features are available on the
|
* Utility class for determining if newer-API features are available on the
|
||||||
@@ -28,9 +31,15 @@ public class ApiFeatures {
|
|||||||
/**
|
/**
|
||||||
* The API level of the Android version we're being run under.
|
* The API level of the Android version we're being run under.
|
||||||
*/
|
*/
|
||||||
public static final int ANDROID_API_LEVEL = Integer.parseInt(Build.VERSION.SDK);
|
public static final int ANDROID_API_LEVEL = Integer.parseInt(
|
||||||
|
Build.VERSION.SDK);
|
||||||
|
|
||||||
private static ApiFeatures instance;
|
private static ApiFeatures instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API platform adapter supported by this system.
|
||||||
|
*/
|
||||||
|
private ApiPlatformAdapter apiPlatformAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the singleton instance of this class.
|
* Returns the singleton instance of this class.
|
||||||
@@ -52,7 +61,28 @@ public class ApiFeatures {
|
|||||||
/**
|
/**
|
||||||
* Allow subclasses for mocking, but no direct instantiation.
|
* Allow subclasses for mocking, but no direct instantiation.
|
||||||
*/
|
*/
|
||||||
protected ApiFeatures() {}
|
protected ApiFeatures() {
|
||||||
|
if (getApiLevel() >= 5) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(
|
||||||
|
"com.google.android.apps.mytracks.util.EclairPlatformAdapter");
|
||||||
|
apiPlatformAdapter = (ApiPlatformAdapter) clazz.newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i(MyTracksConstants.TAG, "ApiFeatures: Unable to instantiate Eclair"
|
||||||
|
+ " platform adapter", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (apiPlatformAdapter == null) {
|
||||||
|
Log.i(MyTracksConstants.TAG,
|
||||||
|
"ApiFeatures: Using default platform adapter");
|
||||||
|
// Cupcake adapter is always supported, so it's safe to do static linkage.
|
||||||
|
apiPlatformAdapter = new CupcakePlatformAdapter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiPlatformAdapter getApiPlatformAdapter() {
|
||||||
|
return apiPlatformAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether cloud backup (a.k.a. Froyo backup) is available.
|
* Returns whether cloud backup (a.k.a. Froyo backup) is available.
|
||||||
@@ -81,7 +111,7 @@ public class ApiFeatures {
|
|||||||
public boolean hasModernSignalStrength() {
|
public boolean hasModernSignalStrength() {
|
||||||
return getApiLevel() >= 7;
|
return getApiLevel() >= 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visible for testing.
|
// Visible for testing.
|
||||||
protected int getApiLevel() {
|
protected int getApiLevel() {
|
||||||
return ANDROID_API_LEVEL;
|
return ANDROID_API_LEVEL;
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.apps.mytracks.util;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of methods that may be implemented in a platform specific way.
|
||||||
|
*
|
||||||
|
* @author Bartlomiej Niechwiej
|
||||||
|
*/
|
||||||
|
public interface ApiPlatformAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the specified service into foreground.
|
||||||
|
*
|
||||||
|
* @param service the service to be put in foreground.
|
||||||
|
* @param notificationManager the notification manager used to post the given
|
||||||
|
* notification.
|
||||||
|
* @param id the ID of the notification, unique within the application.
|
||||||
|
* @param notification the notification to post.
|
||||||
|
*/
|
||||||
|
void startForeground(Service service, NotificationManager notificationManager,
|
||||||
|
int id, Notification notification);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the given service into background.
|
||||||
|
* @param service the service to put into background.
|
||||||
|
* @param notificationManager the notification manager to user when removing
|
||||||
|
* notifications.
|
||||||
|
* @param id the ID of the notification to be remove, or -1 if the
|
||||||
|
* notification shouldn't be removed.
|
||||||
|
*/
|
||||||
|
void stopForeground(Service service, NotificationManager notificationManager,
|
||||||
|
int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminates the given handler thread.
|
||||||
|
*
|
||||||
|
* @param handlerThread the thread to be terminated.
|
||||||
|
* @return true whether the handler has been stopped or not.
|
||||||
|
*/
|
||||||
|
boolean stopHandlerThread(HandlerThread handlerThread);
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Cupcake (API level 3) specific implementation of the
|
||||||
|
* {@link ApiPlatformAdapter}.
|
||||||
|
*
|
||||||
|
* @author Bartlomiej Niechwiej
|
||||||
|
*/
|
||||||
|
public class CupcakePlatformAdapter implements ApiPlatformAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startForeground(Service service,
|
||||||
|
NotificationManager notificationManager, int id,
|
||||||
|
Notification notification) {
|
||||||
|
service.setForeground(true);
|
||||||
|
notificationManager.notify(id, notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopForeground(Service service,
|
||||||
|
NotificationManager notificationManager, int id) {
|
||||||
|
service.setForeground(false);
|
||||||
|
if (id != -1) {
|
||||||
|
notificationManager.cancel(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopHandlerThread(HandlerThread handlerThread) {
|
||||||
|
// Do nothing, as Cupcake doesn't provide quit().
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Eclair (API level 5) specific implementation of the
|
||||||
|
* {@link ApiPlatformAdapter}.
|
||||||
|
*
|
||||||
|
* @author Bartlomiej Niechwiej
|
||||||
|
*/
|
||||||
|
public class EclairPlatformAdapter implements ApiPlatformAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startForeground(Service service,
|
||||||
|
NotificationManager notificationManager, int id,
|
||||||
|
Notification notification) {
|
||||||
|
service.startForeground(id, notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopForeground(Service service,
|
||||||
|
NotificationManager notificationManager, int id) {
|
||||||
|
service.stopForeground(id != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopHandlerThread(HandlerThread handlerThread) {
|
||||||
|
return handlerThread.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ public class FileUtils {
|
|||||||
* A set of characters that are prohibited from being in file names.
|
* A set of characters that are prohibited from being in file names.
|
||||||
*/
|
*/
|
||||||
private static final Pattern PROHIBITED_CHAR_PATTERN =
|
private static final Pattern PROHIBITED_CHAR_PATTERN =
|
||||||
Pattern.compile("[^ A-Za-z0-9_.()]+");
|
Pattern.compile("[^ A-Za-z0-9_.()-]+");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a path inside the My Tracks directory in the SD card.
|
* Builds a path inside the My Tracks directory in the SD card.
|
||||||
@@ -69,8 +69,6 @@ public class FileUtils {
|
|||||||
* Normalizes the input string and make sure it is a valid fat32 file name.
|
* Normalizes the input string and make sure it is a valid fat32 file name.
|
||||||
*
|
*
|
||||||
* @param name the name to normalize
|
* @param name the name to normalize
|
||||||
* @param overheadSize the number of additional characters that will be added
|
|
||||||
* to the name after sanitization
|
|
||||||
* @return the sanitized name
|
* @return the sanitized name
|
||||||
*/
|
*/
|
||||||
String sanitizeName(String name) {
|
String sanitizeName(String name) {
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<factorypath>
|
<factorypath>
|
||||||
<factorypathentry kind="PLUGIN" id="org.eclipse.jst.ws.annotations.core" enabled="true" runInBatchMode="false"/>
|
<factorypathentry kind="PLUGIN" id="org.eclipse.jst.ws.annotations.core" enabled="true" runInBatchMode="false"/>
|
||||||
<factorypathentry kind="WKSPJAR" id="/MyTracksTest/libs/AndroidMockGenerator.jar" enabled="true" runInBatchMode="false"/>
|
<factorypathentry kind="WKSPJAR" id="/MyTracksTest/libs/AndroidMockGenerator.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="ANDROID_SDK/platforms/android-8/android.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="ANDROID_SDK/add-ons/addon_google_apis_google_inc_8/libs/maps.jar" enabled="true" runInBatchMode="false"/>
|
||||||
|
<factorypathentry kind="VARJAR" id="MYTRACKS_BIN" enabled="true" runInBatchMode="false"/>
|
||||||
</factorypath>
|
</factorypath>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#Thu Aug 12 19:30:48 BRT 2010
|
#Mon Nov 01 02:42:03 BRST 2010
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
org.eclipse.jdt.apt.aptEnabled=true
|
org.eclipse.jdt.apt.aptEnabled=true
|
||||||
org.eclipse.jdt.apt.genSrcDir=.apt_generated
|
org.eclipse.jdt.apt.genSrcDir=.apt_generated
|
||||||
org.eclipse.jdt.apt.processorOptions/bin_dir=MyTracksTest/bin
|
org.eclipse.jdt.apt.processorOptions/bin_dir=%PROJECT.DIR%/bin
|
||||||
org.eclipse.jdt.apt.processorOptions/logfile=MyTracksTest/annotations.log
|
org.eclipse.jdt.apt.processorOptions/logfile=%PROJECT.DIR%/annotations.log
|
||||||
org.eclipse.jdt.apt.reconcileEnabled=false
|
org.eclipse.jdt.apt.reconcileEnabled=false
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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.graphics.Paint;
|
||||||
|
import android.graphics.Paint.Style;
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sandor Dornbush
|
||||||
|
*/
|
||||||
|
public class ChartValueSeriesTest extends AndroidTestCase {
|
||||||
|
private ChartValueSeries series;
|
||||||
|
private Paint fillPaint1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
fillPaint1 = new Paint();
|
||||||
|
fillPaint1.setStyle(Style.FILL);
|
||||||
|
fillPaint1.setColor(getContext().getResources().getColor(R.color.green));
|
||||||
|
fillPaint1.setAntiAlias(true);
|
||||||
|
series = new ChartValueSeries(getContext(),
|
||||||
|
"###,###",
|
||||||
|
fillPaint1,
|
||||||
|
null,
|
||||||
|
100,
|
||||||
|
R.string.elevation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInitialConditions() {
|
||||||
|
assertEquals(25, series.getInterval());
|
||||||
|
assertEquals(1, series.getMaxLabelLength());
|
||||||
|
assertEquals(0, series.getMin());
|
||||||
|
assertEquals(0, series.getMax());
|
||||||
|
assertEquals(0.0, series.getSpread());
|
||||||
|
assertEquals(fillPaint1, series.getPaint());
|
||||||
|
assertEquals(getContext().getString(R.string.elevation),
|
||||||
|
series.getTitle());
|
||||||
|
assertTrue(series.isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEnabled() {
|
||||||
|
series.setEnabled(false);
|
||||||
|
assertFalse(series.isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSmallUpdates() {
|
||||||
|
series.update(0);
|
||||||
|
series.update(10);
|
||||||
|
series.updateDimension();
|
||||||
|
assertEquals(25, series.getInterval());
|
||||||
|
assertEquals(3, series.getMaxLabelLength());
|
||||||
|
assertEquals(0, series.getMin());
|
||||||
|
assertEquals(100, series.getMax());
|
||||||
|
assertEquals(100.0, series.getSpread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBigUpdates() {
|
||||||
|
series.update(0);
|
||||||
|
series.update(901);
|
||||||
|
series.updateDimension();
|
||||||
|
assertEquals(200, series.getInterval());
|
||||||
|
assertEquals(5, series.getMaxLabelLength());
|
||||||
|
assertEquals(0, series.getMin());
|
||||||
|
assertEquals(1000, series.getMax());
|
||||||
|
assertEquals(1000.0, series.getSpread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNotZeroBasedUpdates() {
|
||||||
|
series.update(500);
|
||||||
|
series.update(1401);
|
||||||
|
series.updateDimension();
|
||||||
|
assertEquals(200, series.getInterval());
|
||||||
|
assertEquals(5, series.getMaxLabelLength());
|
||||||
|
assertEquals(500, series.getMin());
|
||||||
|
assertEquals(1500, series.getMax());
|
||||||
|
assertEquals(1000.0, series.getSpread());
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -21,7 +21,16 @@ import android.test.AndroidTestCase;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link DefaultTrackNameFactory}
|
||||||
|
*
|
||||||
|
* @author Matthew Simmons
|
||||||
|
*/
|
||||||
public class DefaultTrackNameFactoryTest extends AndroidTestCase {
|
public class DefaultTrackNameFactoryTest extends AndroidTestCase {
|
||||||
|
/**
|
||||||
|
* A version of the factory which allows us to supply our own answer as to
|
||||||
|
* whether a timestamp-based track name should be used.
|
||||||
|
*/
|
||||||
private static class MockDefaultTrackNameFactory
|
private static class MockDefaultTrackNameFactory
|
||||||
extends DefaultTrackNameFactory {
|
extends DefaultTrackNameFactory {
|
||||||
private final boolean useTimestamp;
|
private final boolean useTimestamp;
|
||||||
|
|||||||
+517
-7
@@ -15,12 +15,33 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.apps.mytracks.services;
|
package com.google.android.apps.mytracks.services;
|
||||||
|
|
||||||
|
import static com.google.android.apps.mytracks.MyTracksConstants.RESUME_TRACK_EXTRA_NAME;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||||
|
import com.google.android.apps.mytracks.content.MyTracksProvider;
|
||||||
|
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||||
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
|
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||||
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.location.Location;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.test.RenamingDelegatingContext;
|
||||||
import android.test.ServiceTestCase;
|
import android.test.ServiceTestCase;
|
||||||
|
import android.test.mock.MockContentResolver;
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
import android.test.suitebuilder.annotation.MediumTest;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the MyTracks track recording service.
|
* Tests for the MyTracks track recording service.
|
||||||
*
|
*
|
||||||
@@ -28,24 +49,513 @@ import android.test.suitebuilder.annotation.SmallTest;
|
|||||||
*/
|
*/
|
||||||
public class TrackRecordingServiceTest
|
public class TrackRecordingServiceTest
|
||||||
extends ServiceTestCase<TrackRecordingService> {
|
extends ServiceTestCase<TrackRecordingService> {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private MyTracksProviderUtils providerUtils;
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
public TrackRecordingServiceTest() {
|
public TrackRecordingServiceTest() {
|
||||||
super(TrackRecordingService.class);
|
super(TrackRecordingService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context wrapper with the user provided {@link ContentResolver}.
|
||||||
|
*/
|
||||||
|
private static class MockContext extends ContextWrapper {
|
||||||
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
|
public MockContext(ContentResolver contentResolver, Context base) {
|
||||||
|
super(base);
|
||||||
|
this.contentResolver = contentResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentResolver getContentResolver() {
|
||||||
|
return contentResolver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock class that forces API level < 5 to make sure we can workaround a bug
|
||||||
|
* in ServiceTestCase (throwing a NPE).
|
||||||
|
* See http://code.google.com/p/android/issues/detail?id=12122 for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
private static class MockApiFeatures extends ApiFeatures {
|
||||||
|
@Override
|
||||||
|
protected int getApiLevel() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
ApiFeatures.injectInstance(new MockApiFeatures());
|
||||||
|
|
||||||
|
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||||
|
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
||||||
|
getContext(), getContext(), "test.");
|
||||||
|
context = new MockContext(mockContentResolver, targetContext);
|
||||||
|
MyTracksProvider provider = new MyTracksProvider();
|
||||||
|
provider.attachInfo(context, null);
|
||||||
|
mockContentResolver.addProvider(MyTracksProviderUtils.AUTHORITY, provider);
|
||||||
|
setContext(context);
|
||||||
|
|
||||||
|
providerUtils = MyTracksProviderUtils.Factory.get(context);
|
||||||
|
|
||||||
|
sharedPreferences = context.getSharedPreferences(
|
||||||
|
MyTracksSettings.SETTINGS_NAME, 0);
|
||||||
|
// Disable auto resume by default.
|
||||||
|
updateAutoResumePrefs(0, -1);
|
||||||
|
// No recording track.
|
||||||
|
Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putLong(context.getString(R.string.recording_track_key), -1);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testStartable() {
|
public void testStartable() {
|
||||||
Intent startIntent = new Intent();
|
startService(createStartIntent());
|
||||||
startIntent.setClass(getContext(), TrackRecordingService.class);
|
|
||||||
startService(startIntent);
|
|
||||||
assertNotNull(getService());
|
assertNotNull(getService());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testBindable() {
|
public void testBindable() {
|
||||||
Intent startIntent = new Intent();
|
IBinder service = bindService(createStartIntent());
|
||||||
startIntent.setClass(getContext(), TrackRecordingService.class);
|
|
||||||
IBinder service = bindService(startIntent);
|
|
||||||
assertNotNull(service);
|
assertNotNull(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testResumeAfterReboot_shouldResume() throws Exception {
|
||||||
|
// Insert a dummy track and mark it as recording track.
|
||||||
|
createDummyTrack(123, System.currentTimeMillis(), true);
|
||||||
|
|
||||||
|
// Clear the number of attempts and set the timeout to 10 min.
|
||||||
|
updateAutoResumePrefs(0, 10);
|
||||||
|
|
||||||
|
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||||
|
Intent startIntent = createStartIntent();
|
||||||
|
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||||
|
startService(startIntent);
|
||||||
|
assertNotNull(getService());
|
||||||
|
|
||||||
|
// We expect to resume the previous track.
|
||||||
|
assertTrue(getService().isRecording());
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertEquals(123, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testResumeAfterReboot_simulateReboot() throws Exception {
|
||||||
|
updateAutoResumePrefs(0, 10);
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
// Simulate recording a track.
|
||||||
|
long id = service.startNewTrack();
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
assertEquals(id, service.getRecordingTrackId());
|
||||||
|
shutdownService();
|
||||||
|
assertEquals(id, sharedPreferences.getLong(
|
||||||
|
context.getString(R.string.recording_track_key), -1));
|
||||||
|
|
||||||
|
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||||
|
Intent startIntent = createStartIntent();
|
||||||
|
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||||
|
startService(startIntent);
|
||||||
|
assertNotNull(getService());
|
||||||
|
|
||||||
|
// TODO: shutdownService() has a bug and doesn't set mServiceCreated
|
||||||
|
// to false, thus preventing from a second call to onCreate().
|
||||||
|
// Report the bug to Android team. Until then, the following check
|
||||||
|
// must be commented out.
|
||||||
|
|
||||||
|
// assertTrue(getService().isRecording());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testResumeAfterReboot_noRecordingTrack() throws Exception {
|
||||||
|
// Insert a dummy track and mark it as recording track.
|
||||||
|
createDummyTrack(123, System.currentTimeMillis(), false);
|
||||||
|
|
||||||
|
// Clear the number of attempts and set the timeout to 10 min.
|
||||||
|
updateAutoResumePrefs(0, 10);
|
||||||
|
|
||||||
|
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||||
|
Intent startIntent = createStartIntent();
|
||||||
|
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||||
|
startService(startIntent);
|
||||||
|
assertNotNull(getService());
|
||||||
|
|
||||||
|
// We don't expect to resume the previous track, because it was stopped.
|
||||||
|
assertFalse(getService().isRecording());
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testResumeAfterReboot_expiredTrack() throws Exception {
|
||||||
|
// Insert a dummy track last updated 20 min ago.
|
||||||
|
createDummyTrack(123, System.currentTimeMillis() - 20 * 60 * 1000, true);
|
||||||
|
|
||||||
|
// Clear the number of attempts and set the timeout to 10 min.
|
||||||
|
updateAutoResumePrefs(0, 10);
|
||||||
|
|
||||||
|
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||||
|
Intent startIntent = createStartIntent();
|
||||||
|
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||||
|
startService(startIntent);
|
||||||
|
assertNotNull(getService());
|
||||||
|
|
||||||
|
// We don't expect to resume the previous track, because it has expired.
|
||||||
|
assertFalse(getService().isRecording());
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testResumeAfterReboot_tooManyAttempts() throws Exception {
|
||||||
|
// Insert a dummy track.
|
||||||
|
createDummyTrack(123, System.currentTimeMillis(), true);
|
||||||
|
|
||||||
|
// Set the number of attempts to max.
|
||||||
|
updateAutoResumePrefs(
|
||||||
|
TrackRecordingService.MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS, 10);
|
||||||
|
|
||||||
|
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||||
|
Intent startIntent = createStartIntent();
|
||||||
|
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||||
|
startService(startIntent);
|
||||||
|
assertNotNull(getService());
|
||||||
|
|
||||||
|
// We don't expect to resume the previous track, because there were already
|
||||||
|
// too many attempts.
|
||||||
|
assertFalse(getService().isRecording());
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testRecording_noTracks() throws Exception {
|
||||||
|
List<Track> tracks = providerUtils.getAllTracks();
|
||||||
|
assertTrue(tracks.isEmpty());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
// Test if we start in no-recording mode by default.
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testRecording_oldTracks() throws Exception {
|
||||||
|
createDummyTrack(123, -1, false);
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testRecording_orphanedRecordingTrack() throws Exception {
|
||||||
|
// Just set recording track to a bogus value.
|
||||||
|
setRecordingTrack(256);
|
||||||
|
|
||||||
|
// Make sure that the service will not start recording and will clear
|
||||||
|
// the bogus track.
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testStartNewTrack_noRecording() throws Exception {
|
||||||
|
List<Track> tracks = providerUtils.getAllTracks();
|
||||||
|
assertTrue(tracks.isEmpty());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
long id = service.startNewTrack();
|
||||||
|
assertTrue(id >= 0);
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
Track track = providerUtils.getTrack(id);
|
||||||
|
assertNotNull(track);
|
||||||
|
assertEquals(id, track.getId());
|
||||||
|
assertEquals(id, sharedPreferences.getLong(
|
||||||
|
context.getString(R.string.recording_track_key), -1));
|
||||||
|
assertEquals(id, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testStartNewTrack_alreadyRecording() throws Exception {
|
||||||
|
createDummyTrack(123, -1, true);
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
|
try {
|
||||||
|
service.startNewTrack();
|
||||||
|
fail("Expecting IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected.
|
||||||
|
}
|
||||||
|
assertEquals(123, sharedPreferences.getLong(
|
||||||
|
context.getString(R.string.recording_track_key), 0));
|
||||||
|
assertEquals(123, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testEndCurrentTrack_alreadyRecording() throws Exception {
|
||||||
|
createDummyTrack(123, -1, true);
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
|
// End the current track.
|
||||||
|
service.endCurrentTrack();
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertEquals(-1, sharedPreferences.getLong(
|
||||||
|
context.getString(R.string.recording_track_key), 0));
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testEndCurrentTrack_noRecording() throws Exception {
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
// End the current track.
|
||||||
|
try {
|
||||||
|
service.endCurrentTrack();
|
||||||
|
fail("Expecting IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected.
|
||||||
|
}
|
||||||
|
assertEquals(-1, sharedPreferences.getLong(
|
||||||
|
context.getString(R.string.recording_track_key), 0));
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testIntegration_completeRecordingSession() throws Exception {
|
||||||
|
List<Track> tracks = providerUtils.getAllTracks();
|
||||||
|
assertTrue(tracks.isEmpty());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
// Start a track.
|
||||||
|
long id = service.startNewTrack();
|
||||||
|
assertTrue(id >= 0);
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
Track track = providerUtils.getTrack(id);
|
||||||
|
assertNotNull(track);
|
||||||
|
assertEquals(id, track.getId());
|
||||||
|
assertEquals(id, sharedPreferences.getLong(
|
||||||
|
context.getString(R.string.recording_track_key), -1));
|
||||||
|
assertEquals(id, service.getRecordingTrackId());
|
||||||
|
|
||||||
|
// Stop the track. Validate if it has correct data.
|
||||||
|
service.endCurrentTrack();
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
|
track = providerUtils.getTrack(id);
|
||||||
|
assertNotNull(track);
|
||||||
|
assertEquals(id, track.getId());
|
||||||
|
TripStatistics tripStatistics = track.getStatistics();
|
||||||
|
assertNotNull(tripStatistics);
|
||||||
|
assertTrue(tripStatistics.getStartTime() > 0);
|
||||||
|
assertTrue(tripStatistics.getStopTime() >= tripStatistics.getStartTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testDeleteAllTracks_noRecording() throws Exception {
|
||||||
|
createDummyTrack(123, -1, false);
|
||||||
|
assertEquals(1, providerUtils.getAllTracks().size());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
// Deleting all tracks should succeed.
|
||||||
|
service.deleteAllTracks();
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertTrue(providerUtils.getAllTracks().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testDeleteAllTracks_noTracks() throws Exception {
|
||||||
|
assertTrue(providerUtils.getAllTracks().isEmpty());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
// Deleting all tracks should succeed.
|
||||||
|
service.deleteAllTracks();
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertTrue(providerUtils.getAllTracks().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testDeleteAllTracks_trackInProgress() throws Exception {
|
||||||
|
createDummyTrack(123, -1, true);
|
||||||
|
assertEquals(1, providerUtils.getAllTracks().size());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
|
// Since we have a track in progress, we expect to fail.
|
||||||
|
try {
|
||||||
|
service.deleteAllTracks();
|
||||||
|
fail("Expecting IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected.
|
||||||
|
}
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
assertEquals(1, providerUtils.getAllTracks().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testHasRecorded_noTracks() throws Exception {
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertFalse(service.hasRecorded());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testHasRecorded_trackInProgress() throws Exception {
|
||||||
|
createDummyTrack(123, -1, true);
|
||||||
|
assertEquals(1, providerUtils.getAllTracks().size());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
assertTrue(service.hasRecorded());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testHasRecorded_oldTracks() throws Exception {
|
||||||
|
createDummyTrack(123, -1, false);
|
||||||
|
assertEquals(1, providerUtils.getAllTracks().size());
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
assertTrue(service.hasRecorded());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testInsertStatisticsMarker_noRecordingTrack() throws Exception {
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
Location loc = new Location("gps");
|
||||||
|
try {
|
||||||
|
service.insertStatisticsMarker(loc);
|
||||||
|
fail("Expecting IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testInsertStatisticsMarker_validLocation() throws Exception {
|
||||||
|
createDummyTrack(123, -1, true);
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
|
Location loc = new Location("gps");
|
||||||
|
assertEquals(1, service.insertStatisticsMarker(loc));
|
||||||
|
assertEquals(2, service.insertStatisticsMarker(loc));
|
||||||
|
|
||||||
|
// TODO: Add more checks.
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
|
Location loc = new Location("gps");
|
||||||
|
Waypoint waypoint = new Waypoint();
|
||||||
|
waypoint.setId(1);
|
||||||
|
waypoint.setLocation(loc);
|
||||||
|
try {
|
||||||
|
service.insertWaypointMarker(waypoint);
|
||||||
|
fail("Expecting IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testInsertWaypointMarker_invalidWaypoint() throws Exception {
|
||||||
|
createDummyTrack(123, -1, true);
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
|
Waypoint waypoint = new Waypoint();
|
||||||
|
assertEquals(-1, service.insertWaypointMarker(waypoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
||||||
|
createDummyTrack(123, -1, true);
|
||||||
|
|
||||||
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
|
Location loc = new Location("gps");
|
||||||
|
Waypoint waypoint = new Waypoint();
|
||||||
|
waypoint.setId(1);
|
||||||
|
waypoint.setLocation(loc);
|
||||||
|
assertEquals(1, service.insertWaypointMarker(waypoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ITrackRecordingService bindAndGetService(Intent intent) {
|
||||||
|
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(
|
||||||
|
bindService(intent));
|
||||||
|
assertNotNull(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Track createDummyTrack(long id, long stopTime, boolean isRecording) {
|
||||||
|
Track dummyTrack = new Track();
|
||||||
|
dummyTrack.setId(id);
|
||||||
|
dummyTrack.setName("Dummy Track");
|
||||||
|
TripStatistics tripStatistics = new TripStatistics();
|
||||||
|
tripStatistics.setStopTime(stopTime);
|
||||||
|
dummyTrack.setStatistics(tripStatistics);
|
||||||
|
addTrack(dummyTrack, isRecording);
|
||||||
|
return dummyTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAutoResumePrefs(int attempts, int timeoutMins) {
|
||||||
|
Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putInt(context.getString(
|
||||||
|
R.string.auto_resume_track_current_retry_key), attempts);
|
||||||
|
editor.putInt(context.getString(
|
||||||
|
R.string.auto_resume_track_timeout_key), timeoutMins);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent createStartIntent() {
|
||||||
|
Intent startIntent = new Intent();
|
||||||
|
startIntent.setClass(context, TrackRecordingService.class);
|
||||||
|
return startIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTrack(Track track, boolean isRecording) {
|
||||||
|
assertTrue(track.getId() >= 0);
|
||||||
|
providerUtils.insertTrack(track);
|
||||||
|
assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
||||||
|
setRecordingTrack(isRecording ? track.getId() : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRecordingTrack(long id) {
|
||||||
|
Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putLong(context.getString(R.string.recording_track_key), id);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -20,13 +20,11 @@ public class TripStatisticsBuilderTest extends TestCase {
|
|||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
builder = new TripStatisticsBuilder();
|
builder = new TripStatisticsBuilder(System.currentTimeMillis());
|
||||||
builder.resume();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAddLocationSimple() throws Exception {
|
public void testAddLocationSimple() throws Exception {
|
||||||
builder = new TripStatisticsBuilder();
|
builder = new TripStatisticsBuilder(1000);
|
||||||
builder.resumeAt(1000);
|
|
||||||
TripStatistics stats = builder.getStatistics();
|
TripStatistics stats = builder.getStatistics();
|
||||||
|
|
||||||
assertEquals(0.0, builder.getSmoothedElevation());
|
assertEquals(0.0, builder.getSmoothedElevation());
|
||||||
@@ -84,8 +82,7 @@ public class TripStatisticsBuilderTest extends TestCase {
|
|||||||
*/
|
*/
|
||||||
public void testElevationSimple() throws Exception {
|
public void testElevationSimple() throws Exception {
|
||||||
for (double elevation = 0; elevation < 1000; elevation += 10) {
|
for (double elevation = 0; elevation < 1000; elevation += 10) {
|
||||||
builder = new TripStatisticsBuilder();
|
builder = new TripStatisticsBuilder(System.currentTimeMillis());
|
||||||
builder.resume();
|
|
||||||
for (int j = 0; j < 100; j++) {
|
for (int j = 0; j < 100; j++) {
|
||||||
assertEquals(0.0, builder.updateElevation(elevation));
|
assertEquals(0.0, builder.updateElevation(elevation));
|
||||||
assertEquals(elevation, builder.getSmoothedElevation());
|
assertEquals(elevation, builder.getSmoothedElevation());
|
||||||
|
|||||||
@@ -75,4 +75,8 @@ public class ApiFeaturesTest extends TestCase {
|
|||||||
features.setApiLevel(8);
|
features.setApiLevel(8);
|
||||||
assertTrue(features.hasModernSignalStrength());
|
assertTrue(features.hasModernSignalStrength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testGetApiPlatformAdapter() {
|
||||||
|
assertNotNull(features.getApiPlatformAdapter());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ import junit.framework.TestCase;
|
|||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class FileUtilsTest extends TestCase {
|
public class FileUtilsTest extends TestCase {
|
||||||
private static final String ORIGINAL_NAME = "Swim\10ming ^across: the/ pacific (ocean).";
|
private static final String ORIGINAL_NAME = "Swim\10ming-^across: the/ pacific (ocean).";
|
||||||
private static final String SANITIZED_NAME = "Swimming across the pacific (ocean).";
|
private static final String SANITIZED_NAME = "Swimming-across the pacific (ocean).";
|
||||||
|
|
||||||
private FileUtils fileUtils;
|
private FileUtils fileUtils;
|
||||||
private Set<String> existingFiles;
|
private Set<String> existingFiles;
|
||||||
|
|||||||
Reference in New Issue
Block a user