diff --git a/MyTracks/res/layout/about.xml b/MyTracks/res/layout/about.xml index e3fe3cfb8..51f432b90 100644 --- a/MyTracks/res/layout/about.xml +++ b/MyTracks/res/layout/about.xml @@ -95,8 +95,9 @@ limitations under the License. android:textSize="20sp" /> diff --git a/MyTracks/res/menu/main.xml b/MyTracks/res/menu/main.xml new file mode 100644 index 000000000..23053e2d5 --- /dev/null +++ b/MyTracks/res/menu/main.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/MyTracks/res/values/arrays.xml b/MyTracks/res/values/arrays.xml index d1be00be5..78075af5f 100644 --- a/MyTracks/res/values/arrays.xml +++ b/MyTracks/res/values/arrays.xml @@ -370,5 +370,6 @@ limitations under the License. GPX KML CSV + TCX diff --git a/MyTracks/res/values/keys.xml b/MyTracks/res/values/keys.xml new file mode 100644 index 000000000..d982f7dd0 --- /dev/null +++ b/MyTracks/res/values/keys.xml @@ -0,0 +1,42 @@ + + + + + announcementFrequency + bluetoothDevice + bluetoothPairing + bluetoothSensor + defaultMapPublic + maxRecordingDistance + metricUnits + minRecordingDistance + minRecordingInterval + minRequiredAccuracy + modularStatsDisplay + pickExistingMap + recordingTrack + reportSpeed + selectedTrack + sendStatsAndPoints + sendToDocs + sendToMyMaps + sensorId + sensorType + shareUrlOnly + signalSamplingFrequency + splitFrequency + \ No newline at end of file diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index 80a182bf6..c8c68c17c 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -122,6 +122,7 @@ limitations under the License. Save a GPX file Save a KML file Save a CSV file + Save a TCX file Send to Google My Maps Delete all tracks @@ -135,6 +136,7 @@ limitations under the License. A GPX file A KML file A CSV file + A TCX file Clear map @@ -204,6 +206,21 @@ limitations under the License. Maximum time or distance after which a new split marker will be inserted Pace + Only supported in Android 2.0 or above + Sensor Type + The type of sensor to connect to. + + Sensor Id + The Id sent to sensors. + The Id must be an integer value. + + Bluetooth sensor + The bluetooth sensor to connect to. + + Modular Stats Display + The stats screen is split in various modes. + Display all stats on one screen + GPS Satellites @@ -291,6 +308,7 @@ limitations under the License. minutes second seconds + ms m @@ -310,6 +328,12 @@ limitations under the License. min/mi per mile + + W + + rpm + bpm + Moving Time Total Time @@ -320,6 +344,10 @@ limitations under the License. Statistics Total Distance Distance + Power + Cadence + Heart Rate + Max Heart Rate - Waiting... @@ -337,7 +365,6 @@ limitations under the License. Support: - 1.0.14 mytracks@googlegroups.com mytracks.appspot.com Copyright 2009 Google Inc. @@ -403,4 +430,29 @@ limitations under the License. GSM Strength: %1$d CDMA Strength: %1$d dBm EVDO Strength: %1$d dBm + + + Bluetooth + Bluetooth options + Device pairing + Open Android\'s bluetooth settings to pair a device + bluetoothSensorsEnabled + Sensor Options + + + NONE + ANT + SRM + ATHLOSOFT + + + Connected to %s + Bluetooth sensor connecting ... + Bluetooth sensor not connected + + + Sensor Data Time + Power meter enabled + Heart rate monitor enabled + diff --git a/MyTracks/res/xml/preferences.xml b/MyTracks/res/xml/preferences.xml index 77f1abcf7..c39a33b02 100644 --- a/MyTracks/res/xml/preferences.xml +++ b/MyTracks/res/xml/preferences.xml @@ -18,26 +18,26 @@ limitations under the License. xmlns:android="http://schemas.android.com/apk/res/android"> - + - + - + - + - + - + diff --git a/MyTracks/src/com/google/android/apps/mymaps/VersionChecker.java b/MyTracks/src/com/google/android/apps/mymaps/VersionChecker.java index 6a47a7879..cfc1f8a3d 100644 --- a/MyTracks/src/com/google/android/apps/mymaps/VersionChecker.java +++ b/MyTracks/src/com/google/android/apps/mymaps/VersionChecker.java @@ -29,6 +29,9 @@ import android.app.Dialog; import android.app.TabActivity; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.Intent; import android.net.Uri; import android.util.Log; @@ -80,7 +83,17 @@ public class VersionChecker { final boolean newVersion; // Returned new version available final boolean newVersionRecommended; // Returned new version recommended final boolean newVersionRequired; // Returned new version required - String currentVersion = parentActivity.getString(R.string.version); + String currentVersion = ""; + try { + PackageInfo pi = + parentActivity.getPackageManager().getPackageInfo( + "com.google.android.maps.mytracks", + PackageManager.GET_META_DATA); + currentVersion = pi.versionName; + } catch (NameNotFoundException e) { + Log.w(MyTracksConstants.TAG, "Failed to get version info.", e); + return; + } GDataParser parser = client.getParserForFeed( Entry.class, diff --git a/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java b/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java index f31539bee..14e3edc6b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java @@ -1,12 +1,12 @@ /* * Copyright 2009 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -49,7 +49,7 @@ import java.util.ArrayList; /** * An activity that displays a chart from the track point provider. - * + * * @author Sandor Dornbush */ public class ChartActivity extends Activity implements @@ -204,23 +204,27 @@ public class ChartActivity extends Activity implements public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key != null) { - if (key.equals(MyTracksSettings.SELECTED_TRACK)) { + if (key.equals(getString(R.string.selected_track_key))) { selectedTrackId = - sharedPreferences.getLong(MyTracksSettings.SELECTED_TRACK, -1); + sharedPreferences.getLong(getString(R.string.selected_track_key), + -1); readProfileAsync(); - } else if (key.equals(MyTracksSettings.METRIC_UNITS)) { + } else if (key.equals(getString(R.string.metric_units_key))) { metricUnits = - sharedPreferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + sharedPreferences.getBoolean(getString(R.string.metric_units_key), + true); cv.setMetricUnits(metricUnits); readProfileAsync(); - } else if (key.equals(MyTracksSettings.REPORT_SPEED)) { + } else if (key.equals(getString(R.string.report_speed_key))) { reportSpeed = - sharedPreferences.getBoolean(MyTracksSettings.REPORT_SPEED, true); + sharedPreferences.getBoolean(getString(R.string.report_speed_key), + true); cv.setReportSpeed(reportSpeed, this); readProfileAsync(); - } else if (key.equals(MyTracksSettings.RECORDING_TRACK)) { + } else if (key.equals(getString(R.string.recording_track_key))) { recordingTrackId = - sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); + sharedPreferences.getLong(getString(R.string.recording_track_key), + -1); runOnUiThread(updateChart); } } @@ -246,12 +250,14 @@ public class ChartActivity extends Activity implements getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (preferences != null) { selectedTrackId = - preferences.getLong(MyTracksSettings.SELECTED_TRACK, -1); + preferences.getLong(getString(R.string.selected_track_key), -1); recordingTrackId = - preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); - metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + preferences.getLong(getString(R.string.recording_track_key), -1); + metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), + true); cv.setMetricUnits(metricUnits); - reportSpeed = preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true); + reportSpeed = preferences.getBoolean(getString(R.string.report_speed_key), + true); cv.setReportSpeed(reportSpeed, this); preferences.registerOnSharedPreferenceChangeListener(this); } @@ -450,7 +456,7 @@ public class ChartActivity extends Activity implements * data[0] = the time or distance * data[1] = the elevation * data[2] = the speed - * + * * @param location a location * @return the data point */ diff --git a/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java b/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java index 07311fca2..cb582f089 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java @@ -109,7 +109,8 @@ public class ExportAllTracks { context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); long recordingTrackId = -1; if (prefs != null) { - recordingTrackId = prefs.getLong(MyTracksSettings.RECORDING_TRACK, -1); + recordingTrackId = + prefs.getLong(context.getString(R.string.recording_track_key), -1); } if (recordingTrackId != -1) { acquireWakeLock(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/MenuManager.java b/MyTracks/src/com/google/android/apps/mytracks/MenuManager.java new file mode 100644 index 000000000..e09011b86 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/MenuManager.java @@ -0,0 +1,93 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks; + +import com.google.android.maps.mytracks.R; + +import android.content.Intent; +import android.view.Menu; +import android.view.MenuItem; + +/** + * Manage the application menus. + * + * @author Sandor Dornbush + */ +class MenuManager { + + private MyTracks activity; + + public MenuManager(MyTracks activity) { + this.activity = activity; + } + + public boolean onCreateOptionsMenu(Menu menu) { + activity.getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + public void onPrepareOptionsMenu(Menu menu, boolean hasRecorded, + boolean isRecording, boolean hasSelectedTrack) { + menu.findItem(R.id.menu_list_tracks).setEnabled(hasRecorded); + menu.findItem(R.id.menu_list_markers) + .setEnabled(hasRecorded && hasSelectedTrack); + MenuItem startRecording = menu.findItem(R.id.menu_start_recording); + startRecording.setEnabled(!isRecording); + startRecording.setVisible(!isRecording); + MenuItem stopRecording = menu.findItem(R.id.menu_stop_recording); + stopRecording.setEnabled(isRecording); + stopRecording.setVisible(isRecording); + } + + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_start_recording: { + activity.startRecording(); + return true; + } + case R.id.menu_stop_recording: { + activity.stopRecording(); + return true; + } + case R.id.menu_list_tracks: { + Intent startIntent = new Intent(activity, MyTracksList.class); + activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_TRACK); + return true; + } + case R.id.menu_list_markers: { + Intent startIntent = new Intent(activity, MyTracksWaypointsList.class); + startIntent.putExtra("trackid", activity.getSelectedTrack()); + activity.startActivityForResult(startIntent, MyTracksConstants.SHOW_WAYPOINT); + return true; + } + case R.id.menu_settings: { + Intent startIntent = new Intent(activity, MyTracksSettings.class); + activity.startActivity(startIntent); + return true; + } + case R.id.menu_help: { + Intent startIntent = new Intent(activity, WelcomeActivity.class); + activity.startActivity(startIntent); + return true; + } + case MyTracksConstants.MENU_CLEAR_MAP: { + activity.setSelectedTrack(-1); + return true; + } + } + return false; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java index 7aae5c7f3..27fda84eb 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java @@ -134,15 +134,9 @@ public class MyTracks extends TabActivity implements OnTouchListener, private ChartSettingsDialog chartSettingsDialog; /* - * Menu items: + * Menu manager. */ - - private MenuItem startRecording; - private MenuItem stopRecording; - private MenuItem listTracks; - private MenuItem listMarkers; - private MenuItem settings; - private MenuItem help; + private MenuManager menuManager; /* * Information on upload success to MyMaps/Docs. @@ -283,6 +277,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, super.onCreate(savedInstanceState); instance = this; providerUtils = MyTracksProviderUtils.Factory.get(this); + menuManager = new MenuManager(this); // The volume we want to control is the Text-To-Speech volume setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM); @@ -321,8 +316,10 @@ public class MyTracks extends TabActivity implements OnTouchListener, SharedPreferences prefs = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (prefs != null) { - selectedTrackId = prefs.getLong(MyTracksSettings.SELECTED_TRACK, -1); - recordingTrackId = prefs.getLong(MyTracksSettings.RECORDING_TRACK, -1); + selectedTrackId = + prefs.getLong(getString(R.string.selected_track_key), -1); + recordingTrackId = + prefs.getLong(getString(R.string.recording_track_key), -1); prefs.registerOnSharedPreferenceChangeListener(this); } @@ -416,81 +413,21 @@ public class MyTracks extends TabActivity implements OnTouchListener, @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - startRecording = menu.add(0, MyTracksConstants.MENU_START_RECORDING, 0, - R.string.start_recording); - startRecording.setIcon(R.drawable.menu_record); - stopRecording = menu.add(0, MyTracksConstants.MENU_STOP_RECORDING, 0, - R.string.stop_recording); - stopRecording.setIcon(R.drawable.menu_stop); - listTracks = menu.add(0, MyTracksConstants.MENU_LIST_TRACKS, 0, - R.string.list_tracks); - listTracks.setIcon(R.drawable.menu_list_tracks); - listMarkers = menu.add(0, MyTracksConstants.MENU_LIST_MARKERS, 0, - R.string.list_markers); - listMarkers.setIcon(R.drawable.menu_marker); - settings = menu.add(0, MyTracksConstants.MENU_SETTINGS, 10001, - R.string.settings); - settings.setIcon(android.R.drawable.ic_menu_preferences); - help = menu.add(0, MyTracksConstants.MENU_HELP, 10002, R.string.help); - help.setIcon(android.R.drawable.ic_menu_info_details); - return true; + return menuManager.onCreateOptionsMenu(menu); } @Override public boolean onPrepareOptionsMenu(Menu menu) { - boolean hasRecorded = providerUtils.getLastTrack() != null; - boolean isRecording = isRecording(); - listTracks = menu.findItem(MyTracksConstants.MENU_LIST_TRACKS); - listTracks.setEnabled(hasRecorded); - listMarkers = menu.findItem(MyTracksConstants.MENU_LIST_MARKERS); - listMarkers.setEnabled(hasRecorded && selectedTrackId >= 0); - startRecording = menu.findItem(MyTracksConstants.MENU_START_RECORDING); - startRecording.setEnabled(!isRecording); - startRecording.setVisible(!isRecording); - stopRecording = menu.findItem(MyTracksConstants.MENU_STOP_RECORDING); - stopRecording.setEnabled(isRecording); - stopRecording.setVisible(isRecording); + menuManager.onPrepareOptionsMenu(menu, providerUtils.getLastTrack() != null, + isRecording(), selectedTrackId >= 0); return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case MyTracksConstants.MENU_START_RECORDING: { - startRecording(); - return true; - } - case MyTracksConstants.MENU_STOP_RECORDING: { - stopRecording(); - return true; - } - case MyTracksConstants.MENU_LIST_TRACKS: { - Intent startIntent = new Intent(this, MyTracksList.class); - startActivityForResult(startIntent, MyTracksConstants.SHOW_TRACK); - return true; - } - case MyTracksConstants.MENU_LIST_MARKERS: { - Intent startIntent = new Intent(this, MyTracksWaypointsList.class); - startIntent.putExtra("trackid", selectedTrackId); - startActivityForResult(startIntent, MyTracksConstants.SHOW_WAYPOINT); - return true; - } - case MyTracksConstants.MENU_SETTINGS: { - Intent startIntent = new Intent(this, MyTracksSettings.class); - startActivity(startIntent); - return true; - } - case MyTracksConstants.MENU_HELP: { - Intent startIntent = new Intent(this, WelcomeActivity.class); - startActivity(startIntent); - return true; - } - case MyTracksConstants.MENU_CLEAR_MAP: { - setSelectedTrack(-1); - return true; - } - } - return super.onOptionsItemSelected(item); + return (menuManager.onOptionsItemSelected(item) + ? true + : super.onOptionsItemSelected(item)); } /* @@ -787,8 +724,11 @@ public class MyTracks extends TabActivity implements OnTouchListener, case MyTracksConstants.SAVE_KML_FILE: if (exportFormat == null) { exportFormat = TrackFileFormat.KML; } //$FALL-THROUGH$ - case MyTracksConstants.SAVE_CSV_FILE: { + case MyTracksConstants.SAVE_CSV_FILE: if (exportFormat == null) { exportFormat = TrackFileFormat.CSV; } + //$FALL-THROUGH$ + case MyTracksConstants.SAVE_TCX_FILE: + if (exportFormat == null) { exportFormat = TrackFileFormat.TCX; } if (results != null && resultCode == Activity.RESULT_OK) { final long trackId = results.getLongExtra("trackid", selectedTrackId); @@ -797,7 +737,6 @@ public class MyTracks extends TabActivity implements OnTouchListener, } } break; - } case MyTracksConstants.SHARE_LINK: { Track selectedTrack = providerUtils.getTrack(selectedTrackId); if (selectedTrack != null) { @@ -811,15 +750,21 @@ public class MyTracks extends TabActivity implements OnTouchListener, break; } case MyTracksConstants.SHARE_GPX_FILE: - case MyTracksConstants.SHARE_KML_FILE: { - if (results != null && resultCode == RESULT_OK) { - long trackId = results.getLongExtra("trackid", selectedTrackId); + if (exportFormat == null) { exportFormat = TrackFileFormat.GPX; } + //$FALL-THROUGH$ + case MyTracksConstants.SHARE_KML_FILE: + if (exportFormat == null) { exportFormat = TrackFileFormat.KML; } + //$FALL-THROUGH$ + case MyTracksConstants.SHARE_CSV_FILE: + if (exportFormat == null) { exportFormat = TrackFileFormat.CSV; } + //$FALL-THROUGH$ + case MyTracksConstants.SHARE_TCX_FILE: { + if (exportFormat == null) { exportFormat = TrackFileFormat.TCX; } + + if (results != null && resultCode == Activity.RESULT_OK) { + final long trackId = results.getLongExtra("trackid", selectedTrackId); if (trackId >= 0) { - TrackFileFormat format = - (requestCode == MyTracksConstants.SHARE_GPX_FILE) - ? TrackFileFormat.GPX - : TrackFileFormat.KML; - sendTrack(trackId, format); + sendTrack(trackId, exportFormat); } } break; @@ -862,9 +807,9 @@ public class MyTracks extends TabActivity implements OnTouchListener, + "to shared preferences: ", e); } } - if (key != null && key.equals(MyTracksSettings.SELECTED_TRACK)) { + if (key != null && key.equals(getString(R.string.selected_track_key))) { selectedTrackId = - sharedPreferences.getLong(MyTracksSettings.SELECTED_TRACK, -1); + sharedPreferences.getLong(getString(R.string.selected_track_key), -1); } } @@ -1069,7 +1014,8 @@ public class MyTracks extends TabActivity implements OnTouchListener, getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); boolean shareUrlOnly = true; if (prefs != null) { - shareUrlOnly = prefs.getBoolean(MyTracksSettings.SHARE_URL_ONLY, false); + shareUrlOnly = + prefs.getBoolean(getString(R.string.share_url_only_key), false); } String url = MyMapsConstants.MAPSHOP_BASE_URL + "?msa=0&msid=" + mapId; @@ -1292,7 +1238,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, * Starts the track recording service (if not already running) and binds to * it. Starts recording a new track. */ - private void startRecording() { + public void startRecording() { if (trackRecordingService == null) { startNewTrackRequested = true; Intent startIntent = new Intent(this, TrackRecordingService.class); @@ -1318,7 +1264,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, * Stops the track recording service and unbinds from it. Will display a toast * "Stopped recording" and pop up the Track Details activity. */ - private void stopRecording() { + public void stopRecording() { if (trackRecordingService != null) { try { trackRecordingService.endCurrentTrack(); @@ -1377,18 +1323,22 @@ public class MyTracks extends TabActivity implements OnTouchListener, * * @param trackId the id of the track */ - private void setSelectedTrack(final long trackId) { + public void setSelectedTrack(final long trackId) { runOnUiThread(new Runnable() { public void run() { SharedPreferences prefs = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); - editor.putLong(MyTracksSettings.SELECTED_TRACK, trackId); + editor.putLong(getString(R.string.selected_track_key), trackId); editor.commit(); } }); } + protected long getSelectedTrack() { + return selectedTrackId; + } + /** * Writes the recording track id to the shared preferences. * Executed on the UI thread. @@ -1401,7 +1351,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, SharedPreferences prefs = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); - editor.putLong(MyTracksSettings.RECORDING_TRACK, trackId); + editor.putLong(getString(R.string.recording_track_key), trackId); editor.commit(); } }); @@ -1419,8 +1369,8 @@ public class MyTracks extends TabActivity implements OnTouchListener, getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (prefs != null) { SharedPreferences.Editor editor = prefs.edit(); - editor.putLong(MyTracksSettings.SELECTED_TRACK, theSelectedTrackId); - editor.putLong(MyTracksSettings.RECORDING_TRACK, theRecordingTrackId); + editor.putLong(getString(R.string.selected_track_key), theSelectedTrackId); + editor.putLong(getString(R.string.recording_track_key), theRecordingTrackId); editor.commit(); } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java index 226bd2ddb..be01ace79 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksConstants.java @@ -54,29 +54,25 @@ public abstract class MyTracksConstants { public static final int SHARE_GPX_FILE = 15; public static final int SHARE_KML_FILE = 16; public static final int SHARE_CSV_FILE = 17; - public static final int EDIT_DETAILS = 18; - public static final int SAVE_GPX_FILE = 19; - public static final int SAVE_KML_FILE = 20; - public static final int SAVE_CSV_FILE = 21; - public static final int CLEAR_MAP = 22; - public static final int SHOW_WAYPOINT = 23; - public static final int EDIT_WAYPOINT = 24; - public static final int WELCOME = 25; + public static final int SHARE_TCX_FILE = 18; + public static final int EDIT_DETAILS = 19; + public static final int SAVE_GPX_FILE = 20; + public static final int SAVE_KML_FILE = 21; + public static final int SAVE_CSV_FILE = 22; + public static final int SAVE_TCX_FILE = 23; + public static final int CLEAR_MAP = 24; + public static final int SHOW_WAYPOINT = 25; + public static final int EDIT_WAYPOINT = 26; + public static final int WELCOME = 27; /* * Menu ids: */ - public static final int MENU_START_RECORDING = 1; - public static final int MENU_STOP_RECORDING = 2; - public static final int MENU_LIST_TRACKS = 3; - public static final int MENU_LIST_MARKERS = 4; - public static final int MENU_SETTINGS = 5; - public static final int MENU_MY_LOCATION = 6; - public static final int MENU_TOGGLE_LAYERS = 7; - public static final int MENU_CHART_SETTINGS = 8; - public static final int MENU_HELP = 9; - public static final int MENU_CURRENT_SEGMENT = 10; + public static final int MENU_MY_LOCATION = 1; + public static final int MENU_TOGGLE_LAYERS = 2; + public static final int MENU_CHART_SETTINGS = 3; + public static final int MENU_CURRENT_SEGMENT = 4; /* * Context menu ids: @@ -91,11 +87,13 @@ public abstract class MyTracksConstants { public static final int MENU_SHARE_GPX_FILE = 201; public static final int MENU_SHARE_KML_FILE = 202; public static final int MENU_SHARE_CSV_FILE = 203; - public static final int MENU_WRITE_TO_SD_CARD = 204; - public static final int MENU_SAVE_GPX_FILE = 205; - public static final int MENU_SAVE_KML_FILE = 206; - public static final int MENU_SAVE_CSV_FILE = 207; - public static final int MENU_CLEAR_MAP = 208; + public static final int MENU_SHARE_TCX_FILE = 204; + public static final int MENU_WRITE_TO_SD_CARD = 205; + public static final int MENU_SAVE_GPX_FILE = 206; + public static final int MENU_SAVE_KML_FILE = 207; + public static final int MENU_SAVE_CSV_FILE = 208; + public static final int MENU_SAVE_TCX_FILE = 209; + public static final int MENU_CLEAR_MAP = 210; /** * The number of distance readings to smooth to get a stable signal. @@ -123,6 +121,12 @@ public abstract class MyTracksConstants { */ public static final int MAX_DISPLAYED_TRACK_POINTS = 10000; + /** + * Target number of track points displayed by the map overlay. + * We may display more than this number of points. + */ + public static final int TARGET_DISPLAYED_TRACK_POINTS = 5000; + /** * Maximum number of track points ever loaded at once from the provider into * memory. @@ -179,12 +183,16 @@ public abstract class MyTracksConstants { return MyTracksConstants.SHARE_GPX_FILE; case MyTracksConstants.MENU_SHARE_CSV_FILE: return MyTracksConstants.SHARE_CSV_FILE; + case MyTracksConstants.MENU_SHARE_TCX_FILE: + return MyTracksConstants.SHARE_TCX_FILE; case MyTracksConstants.MENU_SAVE_GPX_FILE: return MyTracksConstants.SAVE_GPX_FILE; case MyTracksConstants.MENU_SAVE_KML_FILE: return MyTracksConstants.SAVE_KML_FILE; case MyTracksConstants.MENU_SAVE_CSV_FILE: return MyTracksConstants.SAVE_CSV_FILE; + case MyTracksConstants.MENU_SAVE_TCX_FILE: + return MyTracksConstants.SAVE_TCX_FILE; case MyTracksConstants.MENU_CLEAR_MAP: return MyTracksConstants.CLEAR_MAP; default: diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java index 4762c840e..8a5f255f1 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java @@ -62,7 +62,7 @@ public class MyTracksDeleteAllTracks extends Handler { SharedPreferences prefs = context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); - editor.putLong(MyTracksSettings.SELECTED_TRACK, -1); + editor.putLong(context.getString(R.string.selected_track_key), -1); editor.commit(); if (done != null) { Handler h = new Handler(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksList.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksList.java index c2be23dce..f88421868 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksList.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksList.java @@ -90,6 +90,8 @@ public class MyTracksList extends ListActivity R.string.tracklist_share_kml_file); share.add(0, MyTracksConstants.MENU_SHARE_CSV_FILE, 0, R.string.tracklist_share_csv_file); + share.add(0, MyTracksConstants.MENU_SHARE_TCX_FILE, 0, + R.string.tracklist_share_tcx_file); SubMenu save = menu.addSubMenu(0, MyTracksConstants.MENU_WRITE_TO_SD_CARD, 0, R.string.tracklist_write_to_sd); @@ -99,6 +101,8 @@ public class MyTracksList extends ListActivity R.string.tracklist_save_as_kml); save.add(0, MyTracksConstants.MENU_SAVE_CSV_FILE, 0, R.string.tracklist_save_as_csv); + save.add(0, MyTracksConstants.MENU_SAVE_TCX_FILE, 0, + R.string.tracklist_save_as_tcx); menu.add(0, MyTracksConstants.MENU_DELETE, 0, R.string.tracklist_delete_track); } @@ -111,14 +115,16 @@ public class MyTracksList extends ListActivity if (key == null) { return; } - if (key.equals(MyTracksSettings.METRIC_UNITS)) { - metricUnits = sharedPreferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + if (key.equals(getString(R.string.metric_units_key))) { + metricUnits = sharedPreferences.getBoolean( + getString(R.string.metric_units_key), true); if (tracksCursor != null && !tracksCursor.isClosed()) { tracksCursor.requery(); } } - if (key.equals(MyTracksSettings.RECORDING_TRACK)) { - recordingTrackId = sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); + if (key.equals(getString(R.string.recording_track_key))) { + recordingTrackId = sharedPreferences.getLong( + getString(R.string.recording_track_key), -1); } } @@ -193,9 +199,10 @@ public class MyTracksList extends ListActivity SharedPreferences preferences = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); preferences.registerOnSharedPreferenceChangeListener(this); - metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + metricUnits = + preferences.getBoolean(getString(R.string.metric_units_key), true); recordingTrackId = - preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); + preferences.getLong(getString(R.string.recording_track_key), -1); tracksCursor = getContentResolver().query( TracksColumns.CONTENT_URI, null, null, null, "_id DESC"); diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java index c7b73f7d7..c60e9888e 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java @@ -136,6 +136,11 @@ public class MyTracksMap extends MapActivity private Handler updateTrackHandler; private MyTracksProviderUtils providerUtils; + + /** + * This value is used to decide how many points to display. + */ + private int samplingFrequency = 1; /** * A runnable that updates the track from the provider (looking for points @@ -147,35 +152,7 @@ public class MyTracksMap extends MapActivity if (selectedTrack == null) { return; } - Cursor cursor = null; - try { - cursor = providerUtils.getLocationsCursor( - recordingTrackId, - lastSeenLocationId + 1, - MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS - - selectedTrack.getLocations().size(), - true); - if (cursor != null) { - if (cursor.moveToLast()) { - final int idColumnIdx = - cursor.getColumnIndexOrThrow(TrackPointsColumns._ID); - do { - lastSeenLocationId = cursor.getLong(idColumnIdx); - Location location = providerUtils.createLocation(cursor); - if (location != null) { - selectedTrack.addLocation(location); - } - } while (cursor.moveToPrevious()); - } - } - } catch (RuntimeException e) { - Log.w(MyTracksConstants.TAG, "Caught an unexpected exception: ", e); - } finally { - if (cursor != null) { - cursor.close(); - } - mapView.postInvalidate(); - } + readAllNewTrackPoints(); } }; @@ -188,9 +165,9 @@ public class MyTracksMap extends MapActivity if (selectedTrack == null) { return; } - lastSeenLocationId = providerUtils.getTrackPoints(selectedTrack, - MyTracksConstants.MAX_DISPLAYED_TRACK_POINTS); - mapView.postInvalidate(); + mapOverlay.clearPoints(); + lastSeenLocationId = selectedTrack.getStartId(); + readAllNewTrackPoints(); } }; @@ -380,12 +357,12 @@ public class MyTracksMap extends MapActivity getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (preferences != null) { minRequiredAccuracy = preferences.getInt( - MyTracksSettings.MIN_REQUIRED_ACCURACY, + getString(R.string.min_required_accuracy_key), MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY); recordingTrackId = - preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); + preferences.getLong(getString(R.string.recording_track_key), -1); long selectedTrackId = - preferences.getLong(MyTracksSettings.SELECTED_TRACK, -1); + preferences.getLong(getString(R.string.selected_track_key), -1); if (selectedTrackId >= 0) { setSelectedTrack(selectedTrackId); } @@ -783,6 +760,8 @@ public class MyTracksMap extends MapActivity R.string.tracklist_share_kml_file); share.add(0, MyTracksConstants.MENU_SHARE_CSV_FILE, 0, R.string.tracklist_share_csv_file); + share.add(0, MyTracksConstants.MENU_SHARE_TCX_FILE, 0, + R.string.tracklist_share_tcx_file); SubMenu save = menu.addSubMenu(0, MyTracksConstants.MENU_WRITE_TO_SD_CARD, 0, R.string.tracklist_write_to_sd); @@ -792,6 +771,8 @@ public class MyTracksMap extends MapActivity R.string.tracklist_save_as_kml); save.add(0, MyTracksConstants.MENU_SAVE_CSV_FILE, 0, R.string.tracklist_save_as_csv); + save.add(0, MyTracksConstants.MENU_SAVE_TCX_FILE, 0, + R.string.tracklist_save_as_tcx); menu.add(0, MyTracksConstants.MENU_CLEAR_MAP, 0, R.string.tracklist_clear_map); menu.add(0, MyTracksConstants.MENU_DELETE, 0, @@ -888,17 +869,19 @@ public class MyTracksMap extends MapActivity public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key != null) { - if (key.equals(MyTracksSettings.MIN_REQUIRED_ACCURACY)) { + if (key.equals(getString(R.string.min_required_accuracy_key))) { minRequiredAccuracy = sharedPreferences.getInt( - MyTracksSettings.MIN_REQUIRED_ACCURACY, + getString(R.string.min_required_accuracy_key), MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY); - } else if (key.equals(MyTracksSettings.SELECTED_TRACK)) { + } else if (key.equals(getString(R.string.selected_track_key))) { long selectedTrackId = - sharedPreferences.getLong(MyTracksSettings.SELECTED_TRACK, -1); + sharedPreferences.getLong(getString(R.string.selected_track_key), + -1); setSelectedTrack(selectedTrackId); - } else if (key.equals(MyTracksSettings.RECORDING_TRACK)) { + } else if (key.equals(getString(R.string.recording_track_key))) { recordingTrackId = - sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); + sharedPreferences.getLong(getString(R.string.recording_track_key), + -1); if (selectedTrack != null) { mapOverlay.setShowEndMarker( selectedTrack.getId() != recordingTrackId); @@ -977,4 +960,63 @@ public class MyTracksMap extends MapActivity public void onAccuracyChanged(Sensor s, int accuracy) { // do nothing } + + /** + * Set the sampling frequency from the total number of points in the track. + * + * @param track The track to read the total number of points from. + */ + private void setSamplingFrequency(Track track) { + long totalLocations = track.getStopId() - track.getStartId(); + + // Limit the number of map points readings. + samplingFrequency = + (int) (1 + + totalLocations / MyTracksConstants.TARGET_DISPLAYED_TRACK_POINTS); + Log.i(MyTracksConstants.TAG, "Sampling locations: " + samplingFrequency); + } + + private void readAllNewTrackPoints() { + Cursor cursor = null; + // Refetch the track to get the latest StopId + selectedTrack = providerUtils.getTrack(selectedTrack.getId()); + long totalLocations = selectedTrack.getStopId() - + selectedTrack.getStartId(); + + setSamplingFrequency(selectedTrack); + int bufferSize = 1024; + int points = 0; + while (lastSeenLocationId < selectedTrack.getStopId()) { + cursor = providerUtils.getLocationsCursor( + selectedTrack.getId(), lastSeenLocationId, bufferSize, false); + if (cursor != null && cursor.moveToFirst()) { + final int idColumnIdx = cursor.getColumnIndexOrThrow( + TrackPointsColumns._ID); + while (cursor.moveToNext()) { + points++; + Location location = providerUtils.createLocation(cursor); + lastSeenLocationId = cursor.getLong(idColumnIdx); + // Include a point if it fits one of the following criteria: + // - Has the mod for the sampling frequency. + // - Is the first point. + // - Is the last point and we are not recording this track. + if (!MyTracksUtils.isValidLocation(location) || + points % samplingFrequency == 0 || + points == 0 || + (recordingTrackId != selectedTrack.getId() && + points == (totalLocations - 1))) { + mapOverlay.addLocation(location); + } + } + } else { + lastSeenLocationId += bufferSize; + } + cursor.close(); + cursor = null; + } + if (cursor != null) { + cursor.close(); + } + mapView.postInvalidate(); + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java index d24d45f7f..8441dabc7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java @@ -1,12 +1,12 @@ /* * Copyright 2008 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -55,6 +55,7 @@ public class MyTracksOverlay extends Overlay { private final Paint errorCirclePaint; private final Context context; private final ArrayList waypoints = new ArrayList(); + private final ArrayList points = new ArrayList(1024); private Track selectedTrack; private int lastHeading = 0; @@ -128,6 +129,17 @@ public class MyTracksOverlay extends Overlay { return selectedTrack; } + /** + * Add a location to the map overlay. + * + * NOTE: this method takes ownership of this location and may change it. + * + * @param l the location to add + */ + public void addLocation(Location l) { + points.add(l); + } + public void addWaypoint(Waypoint wpt) { waypoints.add(wpt); } @@ -136,6 +148,10 @@ public class MyTracksOverlay extends Overlay { waypoints.clear(); } + public void clearPoints() { + points.clear(); + } + public void setShowEndMarker(boolean showEndMarker) { this.showEndMarker = showEndMarker; } @@ -200,7 +216,6 @@ public class MyTracksOverlay extends Overlay { if (track == null) { return; } - ArrayList points = track.getLocations(); if (points.size() < 2) { return; } @@ -210,7 +225,7 @@ public class MyTracksOverlay extends Overlay { int h = mapView.getLatitudeSpan(); int cx = mapView.getMapCenter().getLongitudeE6(); int cy = mapView.getMapCenter().getLatitudeE6(); - Rect rect = new Rect(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2); + Rect rect = new Rect(cx - w, cy - h, cx + w, cy + h); Point pt = new Point(); GeoPoint geoPoint; @@ -226,14 +241,6 @@ public class MyTracksOverlay extends Overlay { int lastLocLon = (int) (points.get(0).getLongitude() * 1E6); int lastLocLat = (int) (points.get(0).getLatitude() * 1E6); - // Line decimation for dummies: - // Skip di additional points, where di depends on zoom level: - int di = 0; - int dl = 17 - mapView.getZoomLevel(); - if (dl > 0) { - di += (dl * dl); - } - // Loop over track points: path = new Path(); for (int i = 1; i < points.size(); i++) { @@ -245,23 +252,6 @@ public class MyTracksOverlay extends Overlay { locLon = (int) (loc.getLongitude() * 1E6); locLat = (int) (loc.getLatitude() * 1E6); - // Skip to every n-th point (depends on zoom level, see above): - for (int j = 0; j < di && i < points.size() - 1; j++) { - // TODO Check the thread synchronization. - // There is no reason that points.get(i + 1) should be null but users - // have reported it causing null pointer exceptions. - if ((locLat > 90E6) || - (lastLocLat > 90E6) || - (points.get(i + 1) == null) || - (points.get(i + 1).getLatitude() > 90)) { - break; - } - i++; - loc = points.get(i); - locLon = (int) (loc.getLongitude() * 1E6); - locLat = (int) (loc.getLatitude() * 1E6); - } - // Draw a line segment if it's inside the viewing window: if (locLat < 90E6 && lastLocLat < 90E6) { lastValidLocation = loc; diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java index 776b5612f..8615ca83e 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksSettings.java @@ -1,12 +1,12 @@ /* * Copyright 2008 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -37,26 +37,6 @@ public class MyTracksSettings extends PreferenceActivity { public static final String SETTINGS_NAME = "MyTracksSettings"; - public static final String ANNOUNCEMENT_FREQUENCY = "announcementFrequency"; - public static final String DEFAULT_MAP_PUBLIC = "defaultMapPublic"; - public static final String MAX_RECORDING_DISTANCE = "maxRecordingDistance"; - public static final String METRIC_UNITS = "metricUnits"; - public static final String MIN_RECORDING_DISTANCE = "minRecordingDistance"; - public static final String MIN_RECORDING_INTERVAL = "minRecordingInterval"; - public static final String MIN_REQUIRED_ACCURACY = "minRequiredAccuracy"; - public static final String PICK_EXISTING_MAP = "pickExistingMap"; - public static final String RECORDING_TRACK = "recordingTrack"; - public static final String REPORT_SPEED = "reportSpeed"; - public static final String SELECTED_TRACK = "selectedTrack"; - public static final String SEND_STATS_AND_POINTS = "sendStatsAndPoints"; - public static final String SEND_TO_DOCS = "sendToDocs"; - public static final String SEND_TO_MYMAPS = "sendToMyMaps"; - public static final String SHARE_URL_ONLY = "shareUrlOnly"; - public static final String SIGNAL_SAMPLING_FREQUENCY = - "signalSamplingFrequency"; - public static final String SPLIT_FREQUENCY = "splitFrequency"; - public static final String WRITE_TO_SD_CARD = "writeToSdCard"; - /* * Default values - keep in sync with those in preferences.xml. */ @@ -96,7 +76,7 @@ public class MyTracksSettings extends PreferenceActivity { // Hook up switching of displayed list entries between metric and imperial // units CheckBoxPreference metricUnitsPreference = - (CheckBoxPreference) findPreference(METRIC_UNITS); + (CheckBoxPreference) findPreference(getString(R.string.metric_units_key)); metricUnitsPreference.setOnPreferenceChangeListener( new OnPreferenceChangeListener() { @Override @@ -112,7 +92,8 @@ public class MyTracksSettings extends PreferenceActivity { // Disable TTS announcement preference if not available if (!mTTSAvailable) { IntegerListPreference announcementFrequency = - (IntegerListPreference) findPreference(ANNOUNCEMENT_FREQUENCY); + (IntegerListPreference) findPreference( + getString(R.string.announcement_frequency_key)); announcementFrequency.setEnabled(false); announcementFrequency.setValue("-1"); announcementFrequency.setSummary( @@ -128,13 +109,17 @@ public class MyTracksSettings extends PreferenceActivity { */ private void updatePreferenceUnits(boolean isMetric) { final ListPreference minRecordingDistance = - (ListPreference) findPreference(MIN_RECORDING_DISTANCE); + (ListPreference) findPreference( + getString(R.string.min_recording_distance_key)); final ListPreference maxRecordingDistance = - (ListPreference) findPreference(MAX_RECORDING_DISTANCE); + (ListPreference) findPreference( + getString(R.string.max_recording_distance_key)); final ListPreference minRequiredAccuracy = - (ListPreference) findPreference(MIN_REQUIRED_ACCURACY); + (ListPreference) findPreference( + getString(R.string.min_required_accuracy_key)); final ListPreference splitFrequency = - (ListPreference) findPreference(SPLIT_FREQUENCY); + (ListPreference) findPreference( + getString(R.string.split_frequency_key)); minRecordingDistance.setEntries(isMetric ? R.array.min_recording_distance_options diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java index 33b911762..82604320b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java @@ -66,11 +66,11 @@ public class MyTracksWaypointDetails extends Activity getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (preferences != null) { boolean useMetric = - preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + preferences.getBoolean(getString(R.string.metric_units_key), true); utils.setMetricUnits(useMetric); boolean displaySpeed = - preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true); + preferences.getBoolean(getString(R.string.report_speed_key), true); utils.setReportSpeed(displaySpeed); utils.updateWaypointUnits(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java index 0e9d6040d..594c9fb98 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java @@ -139,9 +139,9 @@ public class MyTracksWaypointsList extends ListActivity getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (preferences != null) { recordingTrackId = - preferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); + preferences.getLong(getString(R.string.recording_track_key), -1); selectedTrackId = - preferences.getLong(MyTracksSettings.SELECTED_TRACK, -1); + preferences.getLong(getString(R.string.selected_track_key), -1); } boolean selectedRecording = selectedTrackId > 0 && selectedTrackId == recordingTrackId; diff --git a/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java b/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java index 8e7847cb0..3f6ef6da9 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java +++ b/MyTracks/src/com/google/android/apps/mytracks/SendToGoogleDialog.java @@ -105,17 +105,25 @@ public class SendToGoogleDialog extends Dialog { getContext().getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (prefs != null) { sendToMyMapsCheckBox.setChecked( - prefs.getBoolean(MyTracksSettings.SEND_TO_MYMAPS, true)); + prefs.getBoolean( + getContext().getString(R.string.send_to_my_maps_key), true)); sendToDocsCheckBox.setChecked( - prefs.getBoolean(MyTracksSettings.SEND_TO_DOCS, true)); + prefs.getBoolean( + getContext().getString(R.string.send_to_docs_key), true)); pickMapRadioButton.setChecked( - prefs.getBoolean(MyTracksSettings.PICK_EXISTING_MAP, false)); + prefs.getBoolean( + getContext().getString(R.string.pick_existing_map_key), false)); createNewMapRadioButton.setChecked( - !prefs.getBoolean(MyTracksSettings.PICK_EXISTING_MAP, false)); + !prefs.getBoolean( + getContext().getString(R.string.pick_existing_map_key), false)); sendStatsAndPointsRadioButton.setChecked( - prefs.getBoolean(MyTracksSettings.SEND_STATS_AND_POINTS, false)); + prefs.getBoolean( + getContext().getString(R.string.send_stats_and_points_key), + false)); sendStatsRadioButton.setChecked( - !prefs.getBoolean(MyTracksSettings.SEND_STATS_AND_POINTS, false)); + !prefs.getBoolean( + getContext().getString(R.string.send_stats_and_points_key), + false)); } } @@ -127,13 +135,16 @@ public class SendToGoogleDialog extends Dialog { Editor editor = prefs.edit(); if (editor != null) { editor.putBoolean( - MyTracksSettings.SEND_TO_MYMAPS, sendToMyMapsCheckBox.isChecked()); + getContext().getString(R.string.send_to_my_maps_key), + sendToMyMapsCheckBox.isChecked()); editor.putBoolean( - MyTracksSettings.SEND_TO_DOCS, sendToDocsCheckBox.isChecked()); + getContext().getString(R.string.send_to_docs_key), + sendToDocsCheckBox.isChecked()); editor.putBoolean( - MyTracksSettings.PICK_EXISTING_MAP, pickMapRadioButton.isChecked()); + getContext().getString(R.string.pick_existing_map_key), + pickMapRadioButton.isChecked()); editor.putBoolean( - MyTracksSettings.SEND_STATS_AND_POINTS, + getContext().getString(R.string.send_stats_and_points_key), sendStatsAndPointsRadioButton.isChecked()); editor.commit(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java b/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java index c16bb69fa..a0deb9c4a 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java @@ -1,12 +1,12 @@ /* * Copyright 2008 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -15,6 +15,9 @@ */ package com.google.android.apps.mytracks; +import com.google.android.apps.mytracks.content.MyTracksProviderUtils; +import com.google.android.apps.mytracks.content.MyTracksProviderUtilsImpl; +import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.TracksColumns; import com.google.android.apps.mytracks.content.WaypointsColumns; import com.google.android.maps.mytracks.R; @@ -24,7 +27,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.database.ContentObserver; -import android.database.Cursor; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; @@ -48,23 +50,6 @@ import android.widget.Toast; public class StatsActivity extends Activity implements OnSharedPreferenceChangeListener, LocationListener { - /** - * Columns we need from the provider - */ - private static final String[] PROJECTION_COLUMNS = { - TracksColumns._ID, - TracksColumns.STARTTIME, - TracksColumns.TOTALTIME, - TracksColumns.MOVINGTIME, - TracksColumns.TOTALDISTANCE, - TracksColumns.AVGSPEED, - TracksColumns.AVGMOVINGSPEED, - TracksColumns.MAXSPEED, - TracksColumns.MINELEVATION, - TracksColumns.MAXELEVATION, - TracksColumns.ELEVATIONGAIN, - TracksColumns.MINGRADE, - TracksColumns.MAXGRADE }; private final StatsUtilities utils; private UIUpdateThread thread; @@ -109,6 +94,9 @@ public class StatsActivity extends Activity */ private boolean showCurrentSegment = false; + private final MyTracksProviderUtils providerUtils; + private Track track = null; + /** * A runnable for posting to the UI thread. Will update the total time field. */ @@ -147,7 +135,8 @@ public class StatsActivity extends Activity } public StatsActivity() { - this.utils = new StatsUtilities(this); + utils = new StatsUtilities(this); + providerUtils = new MyTracksProviderUtilsImpl(this); } /** Called when the activity is first created. */ @@ -176,20 +165,20 @@ public class StatsActivity extends Activity SharedPreferences preferences = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (preferences != null) { - selectedTrackId = preferences.getLong(MyTracksSettings.SELECTED_TRACK, - -1); - recordingTrackId = preferences.getLong(MyTracksSettings.RECORDING_TRACK, - -1); - metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, - true); + selectedTrackId = preferences.getLong( + getString(R.string.selected_track_key), -1); + recordingTrackId = preferences.getLong( + getString(R.string.recording_track_key), -1); + metricUnits = preferences.getBoolean( + getString(R.string.metric_units_key), true); displaySpeed = - preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true); + preferences.getBoolean(getString(R.string.report_speed_key), true); checkLiveTrack(); restoreStats(); updateLocation(null); preferences.registerOnSharedPreferenceChangeListener(this); } - locationManager = + locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); utils.setMetricUnits(metricUnits); utils.setReportSpeed(displaySpeed); @@ -231,27 +220,31 @@ public class StatsActivity extends Activity Log.d(MyTracksConstants.TAG, "StatsActivity: onSharedPreferences changed " + key); if (key != null) { - if (key.equals(MyTracksSettings.SELECTED_TRACK)) { + if (key.equals(getString(R.string.selected_track_key))) { selectedTrackId = - sharedPreferences.getLong(MyTracksSettings.SELECTED_TRACK, -1); + sharedPreferences.getLong(getString(R.string.selected_track_key), + -1); checkLiveTrack(); restoreStats(); updateLocation(null); - } else if (key.equals(MyTracksSettings.RECORDING_TRACK)) { + } else if (key.equals(getString(R.string.recording_track_key))) { recordingTrackId = - sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); + sharedPreferences.getLong(getString(R.string.recording_track_key), + -1); checkLiveTrack(); restoreStats(); updateLocation(null); - } else if (key.equals(MyTracksSettings.METRIC_UNITS)) { + } else if (key.equals(getString(R.string.metric_units_key))) { metricUnits = - sharedPreferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + sharedPreferences.getBoolean( + getString(R.string.metric_units_key), true); utils.setMetricUnits(metricUnits); utils.updateUnits(); restoreStats(); - } else if (key.equals(MyTracksSettings.REPORT_SPEED)) { + } else if (key.equals(getString(R.string.report_speed_key))) { displaySpeed = - sharedPreferences.getBoolean(MyTracksSettings.REPORT_SPEED, true); + sharedPreferences.getBoolean(getString(R.string.report_speed_key), + true); utils.setReportSpeed(displaySpeed); utils.updateUnits(); utils.setSpeedLabel( @@ -359,60 +352,13 @@ public class StatsActivity extends Activity return; } - Cursor cursor = null; - try { - cursor = getContentResolver().query( - showCurrentSegment - ? WaypointsColumns.CONTENT_URI - : TracksColumns.CONTENT_URI, - PROJECTION_COLUMNS, - showCurrentSegment - ? WaypointsColumns.TRACKID + "=" + selectedTrackId - : "_id=" + selectedTrackId, - null, - "_id LIMIT 1"); - - if (cursor != null && cursor.moveToFirst()) { - final int idxStartTime = - cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME); - final int idxTotalTime = - cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME); - final int idxMovingTime = - cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME); - final int idxTotalDistance = - cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE); - final int idxAverageSpeed = - cursor.getColumnIndexOrThrow(TracksColumns.AVGSPEED); - final int idxAverageMovingSpeed = - cursor.getColumnIndexOrThrow(TracksColumns.AVGMOVINGSPEED); - final int idxMaxSpeed = - cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED); - final int idxMinElevation = - cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION); - final int idxMaxElevation = - cursor.getColumnIndexOrThrow(TracksColumns.MAXELEVATION); - final int idxElevationGain = - cursor.getColumnIndexOrThrow(TracksColumns.ELEVATIONGAIN); - final int idxMinGrade = - cursor.getColumnIndexOrThrow(TracksColumns.MINGRADE); - final int idxMaxGrade = - cursor.getColumnIndexOrThrow(TracksColumns.MAXGRADE); - startTime = cursor.getLong(idxStartTime); - if (!selectedTrackIsRecording()) { - utils.setTime(R.id.total_time_register, cursor.getLong(idxTotalTime)); - } - utils.setAllStats(cursor.getLong(idxMovingTime), - cursor.getFloat(idxTotalDistance), cursor.getFloat(idxAverageSpeed), - cursor.getFloat(idxAverageMovingSpeed), - cursor.getFloat(idxMaxSpeed), cursor.getFloat(idxMinElevation), - cursor.getFloat(idxMaxElevation), cursor.getFloat(idxElevationGain), - cursor.getFloat(idxMinGrade), cursor.getFloat(idxMaxGrade)); - } - } finally { - if (cursor != null) { - cursor.close(); - } + track = providerUtils.getTrack(selectedTrackId); + startTime = track.getStatistics().getStartTime(); + if(!selectedTrackIsRecording()) { + utils.setTime(R.id.total_time_register, + track.getStatistics().getTotalTime()); } + utils.setAllStats(track.getStatistics()); } /** diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java index 163e29b03..448b820d1 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java @@ -112,7 +112,9 @@ public class SendToDocs { SharedPreferences preferences = activity.getSharedPreferences( MyTracksSettings.SETTINGS_NAME, 0); if (preferences != null) { - metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + metricUnits = + preferences.getBoolean(activity.getString(R.string.metric_units_key), + true); } else { metricUnits = true; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java index cfebe4539..a5a89564f 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java @@ -264,7 +264,9 @@ public class SendToMyMaps { MyTracksSettings.SETTINGS_NAME, 0); boolean metricUnits = true; if (preferences != null) { - metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + metricUnits = + preferences.getBoolean(context.getString(R.string.metric_units_key), + true); } // For the progress monitor: @@ -390,7 +392,7 @@ public class SendToMyMaps { boolean mapPublic = true; if (preferences != null) { mapPublic = preferences.getBoolean( - MyTracksSettings.DEFAULT_MAP_PUBLIC, true); + context.getString(R.string.default_map_public_key), true); } metaData.setSearchable(mapPublic); Entry entry = MyMapsGDataConverter.getMapEntryForMetadata(metaData); diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/TcxTrackWriter.java b/MyTracks/src/com/google/android/apps/mytracks/io/TcxTrackWriter.java new file mode 100644 index 000000000..0a8b912d6 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/io/TcxTrackWriter.java @@ -0,0 +1,191 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.io; + +import com.google.android.apps.mytracks.content.Track; +import com.google.android.apps.mytracks.content.Waypoint; +import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat; + +import android.location.Location; +import android.os.Build; +import android.os.Environment; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + * Write out a a track in the Garmin training center database, tcx format. + * As defined by: + * http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 + * + * @author Sandor Dornbush + */ +public class TcxTrackWriter implements TrackFormatWriter { + + private PrintWriter pw = null; + private Track track; + + static final SimpleDateFormat TIMESTAMP_FORMAT = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + { + TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + public TcxTrackWriter() { + } + + @Override + public void prepare(Track track, OutputStream out) { + this.track = track; + this.pw = new PrintWriter(out); + + } + + @Override + public void close() { + if (pw != null) { + pw.close(); + pw = null; + } + } + + @Override + public String getDefaultDirectory() { + return Environment.getExternalStorageDirectory() + + System.getProperty("file.separator") + getExtension(); + } + + @Override + public String getExtension() { + return TrackFileFormat.TCX.getExtension(); + } + + @Override + public void writeHeader() { + if (pw == null) { + return; + } + pw.println(""); + pw.print(""); + pw.println(); + } + + @Override + public void writeBeginTrack(Location firstPoint) { + if (pw == null) { + return; + } + pw.println(" "); + pw.print(" "); + pw.print(" "); + pw.print(TIMESTAMP_FORMAT.format(track.getStatistics().getStartTime())); + pw.println(""); + pw.println(" "); + pw.print(" "); + pw.print(track.getStatistics().getTotalTime() / 1000); + pw.println(""); + pw.print(" "); + pw.print(track.getStatistics().getTotalDistance()); + pw.println(""); + // TODO max speed etc. + // Calories are a required element just put in 0. + pw.print("0"); + pw.println("Active"); + pw.println("Manual)"); + } + + @Override + public void writeOpenSegment() { + if (pw != null) { + pw.println(" "); + } + } + + @Override + public void writeLocation(Location location) { + if (pw == null) { + return; + } + pw.println(" "); + Date d = new Date(location.getTime()); + pw.println(" "); + pw.println(" "); + + pw.print(" "); + pw.print(location.getLatitude()); + pw.println(""); + + pw.print(" "); + pw.print(location.getLongitude()); + pw.println(""); + + pw.println(" "); + pw.print(" "); + pw.print(location.getAltitude()); + pw.println(""); + + pw.println(" "); + } + + @Override + public void writeCloseSegment() { + if (pw != null) { + pw.println(" "); + } + } + + @Override + public void writeEndTrack(Location lastPoint) { + if (pw == null) { + return; + } + pw.println(" "); + pw.print(" "); + pw.print(""); + pw.print(Build.MODEL); + pw.print(""); + pw.println(")"); + pw.println(" "); + pw.println(" "); + } + + @Override + public void writeFooter() { + if (pw == null) { + return; + } + pw.print(" "); + pw.print("My Tracks by Google"); + pw.println(""); + pw.println(""); + } + + @Override + public void writeWaypoint(Waypoint waypoint) { + // TODO Write out the waypoints somewhere. + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/TrackWriterFactory.java b/MyTracks/src/com/google/android/apps/mytracks/io/TrackWriterFactory.java index 98e7c177b..bc3c15ddd 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/TrackWriterFactory.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/TrackWriterFactory.java @@ -49,6 +49,12 @@ public class TrackWriterFactory { public TrackFormatWriter newFormatWriter(Context context) { return new CsvTrackWriter(); } + }, + TCX { + @Override + public TrackFormatWriter newFormatWriter(Context context) { + return new TcxTrackWriter(); + } }; /** diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/PreferenceManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/PreferenceManager.java new file mode 100644 index 000000000..5128ceca0 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/PreferenceManager.java @@ -0,0 +1,142 @@ +/* + * 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.services; + +import android.content.SharedPreferences; +import android.util.Log; + +import com.google.android.apps.mytracks.MyTracksConstants; +import com.google.android.apps.mytracks.MyTracksSettings; +import com.google.android.maps.mytracks.R; + +/** + * A class that manages reading the shared preferences for the service. + * + * @author Sandor Dornbush + */ +public class PreferenceManager { + private TrackRecordingService service; + private String announcement_frequency_key; + private String max_recording_distance_key; + private String metric_units_key; + private String min_recording_distance_key; + private String min_recording_interval_key; + private String min_required_accuracy_key; + private String signal_sampling_frequency_key; + private String split_frequency_key; + + public PreferenceManager(TrackRecordingService service) { + this.service = service; + announcement_frequency_key = + service.getString(R.string.announcement_frequency_key); + max_recording_distance_key = + service.getString(R.string.max_recording_distance_key); + metric_units_key = + service.getString(R.string.metric_units_key); + min_recording_distance_key = + service.getString(R.string.min_recording_distance_key); + min_recording_interval_key = + service.getString(R.string.min_recording_interval_key); + min_required_accuracy_key = + service.getString(R.string.min_required_accuracy_key); + split_frequency_key = + service.getString(R.string.split_frequency_key); + signal_sampling_frequency_key = + service.getString(R.string.signal_sampling_frequency_key); + } + + /** + * Notifies that preferences have changed. + * Call this with key == null to update all preferences in one call. + * + * @param key the key that changed (may be null to update all preferences) + */ + 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(min_recording_distance_key)) { + service.setMinRecordingDistance( + sharedPreferences.getInt( + min_recording_distance_key, + MyTracksSettings.DEFAULT_MIN_RECORDING_DISTANCE)); + Log.d(MyTracksConstants.TAG, + "TrackRecordingService: minRecordingDistance = " + + service.getMinRecordingDistance()); + } + if (key == null || key.equals(max_recording_distance_key)) { + service.setMaxRecordingDistance( + sharedPreferences.getInt( + max_recording_distance_key, + MyTracksSettings.DEFAULT_MAX_RECORDING_DISTANCE)); + } + if (key == null || key.equals(min_recording_interval_key)) { + int minRecordingInterval = sharedPreferences.getInt( + min_recording_interval_key, + MyTracksSettings.DEFAULT_MIN_RECORDING_INTERVAL); + switch (minRecordingInterval) { + case -2: + // Battery Miser + // min: 30 seconds + // max: 5 minutes + // minDist: 5 meters Choose battery life over moving time accuracy. + service.setLocationListenerPolicy( + new AdaptiveLocationListenerPolicy(30000, 300000, 5)); + break; + case -1: + // High Accuracy + // min: 1 second + // max: 30 seconds + // minDist: 0 meters get all updates to properly measure moving time. + service.setLocationListenerPolicy( + new AdaptiveLocationListenerPolicy(1000, 30000, 0)); + break; + default: + service.setLocationListenerPolicy( + new AbsoluteLocationListenerPolicy(minRecordingInterval * 1000)); + } + } + if (key == null || key.equals(min_required_accuracy_key)) { + service.setMinRequiredAccuracy( + sharedPreferences.getInt( + min_required_accuracy_key, + MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY)); + } + if (key == null || key.equals(announcement_frequency_key)) { + service.setAnnouncementFrequency( + sharedPreferences.getInt(announcement_frequency_key, + -1)); + } + if (key == null || key.equals(split_frequency_key)) { + service.getSplitManager().setSplitFrequency( + sharedPreferences.getInt(split_frequency_key, 0)); + } + if (key == null || key.equals(signal_sampling_frequency_key)) { + service.getSignalManager().setFrequency( + sharedPreferences.getInt( + signal_sampling_frequency_key, -1), service); + } + if (key == null || key.equals(metric_units_key)) { + service.getSplitManager().setMetricUnits(sharedPreferences.getBoolean( + metric_units_key, true)); + } + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java b/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java index d0ec9a41e..ba0f0cf52 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/StatusAnnouncerTask.java @@ -116,10 +116,12 @@ public class StatusAnnouncerTask boolean metricUnits = true; boolean reportSpeed = true; if (preferences != null) { - metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, - true); - reportSpeed = preferences.getBoolean(MyTracksSettings.REPORT_SPEED, - true); + metricUnits = + preferences.getBoolean(context.getString(R.string.metric_units_key), + true); + reportSpeed = + preferences.getBoolean(context.getString(R.string.report_speed_key), + true); } double d = stats.getTotalDistance() / 1000; diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index fc952387d..65eebf418 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -1,12 +1,12 @@ /* * Copyright 2008 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -56,7 +56,7 @@ import java.util.TimerTask; /** * A background service that registers a location listener and records track * points. Track points are saved to the MyTracksProvider. - * + * * @author Leif Hendrik Wilden */ public class TrackRecordingService extends Service implements LocationListener { @@ -68,8 +68,6 @@ public class TrackRecordingService extends Service implements LocationListener { private LocationManager locationManager; private WakeLock wakeLock; - private int minRecordingInterval = - MyTracksSettings.DEFAULT_MIN_RECORDING_INTERVAL; private int minRecordingDistance = MyTracksSettings.DEFAULT_MIN_RECORDING_DISTANCE; private int maxRecordingDistance = @@ -110,8 +108,10 @@ public class TrackRecordingService extends Service implements LocationListener { private TaskExecuterManager signalManager; private SplitManager splitManager; + private PreferenceManager prefManager; + /** - * The interval in milliseconds that we have requested to be notfied of gps + * The interval in milliseconds that we have requested to be notified of gps * readings. */ private long currentRecordingInterval = 0; @@ -126,7 +126,7 @@ public class TrackRecordingService extends Service implements LocationListener { * Task invoked by a timer periodically to make sure the location listener is * still registered. */ - private TimerTask checkLocationListener = new TimerTask() { + private final TimerTask checkLocationListener = new TimerTask() { @Override public void run() { if (!onCreateWasCalled) { @@ -198,7 +198,7 @@ public class TrackRecordingService extends Service implements LocationListener { /** * Inserts a new location in the track points db and updates the corresponding * track in the track db. - * + * * @param recordingTrack the track that is currently being recorded * @param location the location to be inserted * @param lastRecordedLocation the last recorded location before this one (or @@ -475,8 +475,7 @@ public class TrackRecordingService extends Service implements LocationListener { } // This should never happen, but just in case (we really don't want the - // service - // to crash): + // service to crash): if (location == null) { Log.w(MyTracksConstants.TAG, "Location changed, but location is null."); @@ -559,8 +558,7 @@ public class TrackRecordingService extends Service implements LocationListener { } // If separation from last recorded point is too large insert a - // separator - // to indicate end of a segment: + // separator to indicate end of a segment: boolean startNewSegment = lastRecordedLocation != null && lastRecordedLocation.getLatitude() < 90 @@ -620,7 +618,7 @@ public class TrackRecordingService extends Service implements LocationListener { */ /** - * Notifies that preferences have changed. + * Notifies that preferences have changed. * Call this with key == null to update all preferences in one call. * * @param key the key that changed (may be null to update all preferences) @@ -628,93 +626,7 @@ public class TrackRecordingService extends Service implements LocationListener { public void onSharedPreferenceChanged(String key) { Log.d(MyTracksConstants.TAG, "TrackRecordingService.onSharedPreferenceChanged"); - SharedPreferences sharedPreferences = - getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); - if (sharedPreferences == null) { - Log.w(MyTracksConstants.TAG, - "TrackRecordingService: Couldn't get shared preferences."); - return; - } - - if (key == null || key.equals(MyTracksSettings.MIN_RECORDING_DISTANCE)) { - minRecordingDistance = sharedPreferences.getInt( - MyTracksSettings.MIN_RECORDING_DISTANCE, - MyTracksSettings.DEFAULT_MIN_RECORDING_DISTANCE); - Log.d(MyTracksConstants.TAG, - "TrackRecordingService: minRecordingDistance = " - + minRecordingDistance); - } - if (key == null || key.equals(MyTracksSettings.MAX_RECORDING_DISTANCE)) { - maxRecordingDistance = sharedPreferences.getInt( - MyTracksSettings.MAX_RECORDING_DISTANCE, - MyTracksSettings.DEFAULT_MAX_RECORDING_DISTANCE); - } - if (key == null || key.equals(MyTracksSettings.MIN_RECORDING_INTERVAL)) { - minRecordingInterval = sharedPreferences.getInt( - MyTracksSettings.MIN_RECORDING_INTERVAL, - MyTracksSettings.DEFAULT_MIN_RECORDING_INTERVAL); - switch (minRecordingInterval) { - case -2: - // Battery Miser - // min: 30 seconds - // max: 5 minutes - // minDist: 5 meters Choose battery life over moving time accuracy. - locationListenerPolicy = - new AdaptiveLocationListenerPolicy(30000, 300000, 5); - break; - case -1: - // High Accuracy - // min: 1 second - // max: 30 seconds - // minDist: 0 meters get all updates to properly measure moving time. - locationListenerPolicy = - new AdaptiveLocationListenerPolicy(1000, 30000, 0); - break; - default: - locationListenerPolicy = - new AbsoluteLocationListenerPolicy(minRecordingInterval * 1000); - } - } - if (key == null || key.equals(MyTracksSettings.MIN_REQUIRED_ACCURACY)) { - minRequiredAccuracy = sharedPreferences.getInt( - MyTracksSettings.MIN_REQUIRED_ACCURACY, - MyTracksSettings.DEFAULT_MIN_REQUIRED_ACCURACY); - } - if (key == null || key.equals(MyTracksSettings.RECORDING_TRACK)) { - recordingTrackId = - sharedPreferences.getLong(MyTracksSettings.RECORDING_TRACK, -1); - } - if (key == null || key.equals(MyTracksSettings.ANNOUNCEMENT_FREQUENCY)) { - announcementFrequency = - sharedPreferences.getInt(MyTracksSettings.ANNOUNCEMENT_FREQUENCY, -1); - if (mTTSAvailable) { - if (announcementFrequency == -1) { - if (executer != null) { - executer.shutdown(); - executer = null; - } - } else { - if (executer == null) { - SafeStatusAnnouncerTask announcer = - new SafeStatusAnnouncerTask(this); - executer = new PeriodicTaskExecuter(announcer, this); - } - executer.scheduleTask(announcementFrequency * 60000); - } - } - } - if (key == null || key.equals(MyTracksSettings.SPLIT_FREQUENCY)) { - splitManager.setSplitFrequency( - sharedPreferences.getInt(MyTracksSettings.SPLIT_FREQUENCY, 0)); - } - if (key == null || key.equals(MyTracksSettings.SIGNAL_SAMPLING_FREQUENCY)) { - signalManager.setFrequency(sharedPreferences.getInt( - MyTracksSettings.SIGNAL_SAMPLING_FREQUENCY, -1), this); - } - if (key == null || key.equals(MyTracksSettings.METRIC_UNITS)) { - splitManager.setMetricUnits( - sharedPreferences.getBoolean(MyTracksSettings.METRIC_UNITS, true)); - } + prefManager.onSharedPreferenceChanged(key); if (isRecording) { registerLocationListener(); @@ -730,7 +642,6 @@ public class TrackRecordingService extends Service implements LocationListener { Log.d(MyTracksConstants.TAG, "TrackRecordingService.onCreate"); super.onCreate(); onCreateWasCalled = true; - setForeground(true); providerUtils = MyTracksProviderUtils.Factory.get(this); notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); @@ -748,6 +659,7 @@ public class TrackRecordingService extends Service implements LocationListener { signalManager = new TaskExecuterManager(-1, new SignalStrengthTask(this), this); } + prefManager = new PreferenceManager(this); onSharedPreferenceChanged(null); restoreStats(); registerLocationListener(); @@ -815,7 +727,7 @@ public class TrackRecordingService extends Service implements LocationListener { /** * Inserts a statistics marker. A statistics marker holds the stats for the * last segment up to this marker. - * + * * @param location the location where to insert * @return the unique id of the inserted marker */ @@ -872,7 +784,7 @@ public class TrackRecordingService extends Service implements LocationListener { public boolean hasRecorded() { return providerUtils.getLastTrackId() >= 0; } - + @Override public long startNewTrack() { Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack"); @@ -903,11 +815,11 @@ public class TrackRecordingService extends Service implements LocationListener { signalManager.restore(); return trackId; } - + /** * Insert the given waypoint marker. Users can insert waypoint markers * to tag locations with a name, description, category etc. - * + * * @param waypoint a waypoint * @return the unique id of the inserted marker */ @@ -915,11 +827,11 @@ public class TrackRecordingService extends Service implements LocationListener { public long insertWaypointMarker(Waypoint waypoint) { return TrackRecordingService.this.insertWaypointMarker(waypoint); } - + /** * Insert a statistics marker. A statistics marker holds the stats for * the last segment up to this marker. - * + * * @param location the location where to insert * @return the unique id of the inserted marker */ @@ -927,7 +839,7 @@ public class TrackRecordingService extends Service implements LocationListener { public long insertStatisticsMarker(Location location) { return TrackRecordingService.this.insertStatisticsMarker(location); } - + @Override public void endCurrentTrack() { Log.d(MyTracksConstants.TAG, "TrackRecordingService.endCurrentTrack"); @@ -952,18 +864,18 @@ public class TrackRecordingService extends Service implements LocationListener { showNotification(); recordingTrackId = -1; } - + @Override public void deleteAllTracks() { endCurrentTrack(); providerUtils.deleteAllTracks(); } - + @Override public void recordLocation(Location loc) { onLocationChanged(loc); } - + @Override public void sharedPreferenceChanged(String key) { Log.d(MyTracksConstants.TAG, @@ -983,4 +895,68 @@ public class TrackRecordingService extends Service implements LocationListener { long getRecordingTrackId() { return recordingTrackId; } + + public int getAnnouncementFrequency() { + return announcementFrequency; + } + + public void setAnnouncementFrequency(int announcementFrequency) { + this.announcementFrequency = announcementFrequency; + if (mTTSAvailable) { + if (announcementFrequency == -1) { + if (executer != null) { + executer.shutdown(); + executer = null; + } + } else { + if (executer == null) { + SafeStatusAnnouncerTask announcer = + new SafeStatusAnnouncerTask(this); + executer = new PeriodicTaskExecuter(announcer, this); + } + executer.scheduleTask(announcementFrequency * 60000); + } + } + } + + public int getMaxRecordingDistance() { + return maxRecordingDistance; + } + + public void setMaxRecordingDistance(int maxRecordingDistance) { + this.maxRecordingDistance = maxRecordingDistance; + } + + public int getMinRecordingDistance() { + return minRecordingDistance; + } + + public void setMinRecordingDistance(int minRecordingDistance) { + this.minRecordingDistance = minRecordingDistance; + } + + public int getMinRequiredAccuracy() { + return minRequiredAccuracy; + } + + public void setMinRequiredAccuracy(int minRequiredAccuracy) { + this.minRequiredAccuracy = minRequiredAccuracy; + } + + public LocationListenerPolicy getLocationListenerPolicy() { + return locationListenerPolicy; + } + + public void setLocationListenerPolicy( + LocationListenerPolicy locationListenerPolicy) { + this.locationListenerPolicy = locationListenerPolicy; + } + + public SplitManager getSplitManager() { + return splitManager; + } + + public TaskExecuterManager getSignalManager() { + return signalManager; + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java index 9724ba727..f0107e832 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java +++ b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java @@ -347,6 +347,8 @@ public class TripStatisticsBuilder { * Returns the amount of time the user has been idle or 0 if they are moving. */ public long getIdleTime() { + if (lastLocation == null || lastMovingLocation == null) + return 0; return lastLocation.getTime() - lastMovingLocation.getTime(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java b/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java index f232dbf51..bd721512a 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java @@ -53,7 +53,8 @@ public class ChartURLGenerator { context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); boolean metricUnits = true; if (preferences != null) { - metricUnits = preferences.getBoolean(MyTracksSettings.METRIC_UNITS, true); + metricUnits = preferences.getBoolean( + context.getString(R.string.metric_units_key), true); } return getChartUrl(distances, elevations, track, diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java index d5a8ed34b..c4c0b2317 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java @@ -173,7 +173,7 @@ public class StringUtils implements DescriptionGenerator { context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); if (preferences != null) { displaySpeed = - preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true); + preferences.getBoolean(context.getString(R.string.report_speed_key), true); } TripStatistics trackStats = track.getStatistics(); diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/TcxTrackWriterTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/TcxTrackWriterTest.java new file mode 100644 index 000000000..ec132e0db --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/TcxTrackWriterTest.java @@ -0,0 +1,66 @@ +// Copyright 2010 Google Inc. All Rights Reserved. +package com.google.android.apps.mytracks.io; + +import android.location.Location; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.Date; +import java.util.List; + +/** + * Tests for the GPX track exporter. + * + * @author Sandor Dornbush + */ +public class TcxTrackWriterTest extends TrackFormatWriterTest { + + public void testXmlOutput() throws Exception { + TrackFormatWriter writer = new TcxTrackWriter(); + String result = writeTrack(writer); + Document doc = parseXmlDocument(result); + + Element root = getChildElement(doc, "TrainingCenterDatabase"); + Element activitiesTag = getChildElement(root, "Activities"); + Element activityTag = getChildElement(activitiesTag, "Activity"); + Element lapTag = getChildElement(activityTag, "Lap"); + + List segmentTags = getChildElements(lapTag, "Track", 2); + Element segment1Tag = segmentTags.get(0); + Element segment2Tag = segmentTags.get(1); + List seg1PointTags = getChildElements(segment1Tag, "Trackpoint", 2); + List seg2PointTags = getChildElements(segment2Tag, "Trackpoint", 2); + assertTagsMatchPoints(seg1PointTags, location1, location2); + assertTagsMatchPoints(seg2PointTags, location3, location4); + } + + /** + * Asserts that the given tags describe the given points, in the same order. + */ + private void assertTagsMatchPoints(List tags, Location... locs) { + assertEquals(locs.length, tags.size()); + for (int i = 0; i < locs.length; i++) { + Element tag = tags.get(i); + Location loc = locs[i]; + + assertTagMatchesLocation(tag, loc); + } + } + + /** + * Asserts that the given tag describes the given location. + */ + private void assertTagMatchesLocation(Element tag, Location loc) { + Element posTag = getChildElement(tag, "Position"); + assertEquals(Double.toString(loc.getLatitude()), + getChildTextValue(posTag, "LatitudeDegrees")); + assertEquals(Double.toString(loc.getLongitude()), + getChildTextValue(posTag, "LongitudeDegrees")); + assertEquals( + TcxTrackWriter.TIMESTAMP_FORMAT.format(new Date(loc.getTime())), + getChildTextValue(tag, "Time")); + assertEquals(Double.toString(loc.getAltitude()), + getChildTextValue(tag, "AltitudeMeters")); + } +} diff --git a/eclipse/mytracks-style.xml b/eclipse/mytracks-style.xml new file mode 100644 index 000000000..45afba5bc --- /dev/null +++ b/eclipse/mytracks-style.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eclipse/mytracks.importorder b/eclipse/mytracks.importorder new file mode 100644 index 000000000..794a97650 --- /dev/null +++ b/eclipse/mytracks.importorder @@ -0,0 +1,7 @@ +#Organize Import Order +#Fri May 28 03:20:44 BRT 2010 +4=org +3=javax +2=java +1=android +0=com