forked from upstream-mirrors/OpenTracks
Merging extra head.
This commit is contained in:
@@ -17,8 +17,8 @@ limitations under the License.
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.maps.mytracks"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="31"
|
||||
android:versionName="1.1.8">
|
||||
android:versionCode="32"
|
||||
android:versionName="1.1.9">
|
||||
<application android:icon="@drawable/arrow_icon"
|
||||
android:label="@string/app_name"
|
||||
android:backupAgent="com.google.android.apps.mytracks.io.backup.MyTracksBackupAgent">
|
||||
@@ -79,6 +79,17 @@ limitations under the License.
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.google.android.apps.mytracks.DeleteTrack"
|
||||
android:label="@string/deletetrack_title"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:noHistory="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.DELETE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="vnd.android.cursor.item/vnd.google.track" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.google.android.apps.mytracks.WaypointDetails"
|
||||
android:label="@string/waypointdetails_title">
|
||||
<intent-filter>
|
||||
|
||||
@@ -83,7 +83,8 @@ limitations under the License.
|
||||
<string name="write_progress_message">Writing file to SD card. This may take a while.</string>
|
||||
<string name="progress_message_updating_track">Updating track statistics...</string>
|
||||
|
||||
<!-- Used by DeleteAllDataHandler -->
|
||||
<!-- Used by deletion actions -->
|
||||
<string name="deletetrack_title">Delete track</string>
|
||||
<string name="are_you_sure_question">Are you sure?</string>
|
||||
<string name="all_data_will_be_permanently_deleted">All tracks and markers will be deleted from this phone.</string>
|
||||
<string name="track_will_be_permanently_deleted">The track and its markers will be permanently deleted from this phone.</string>
|
||||
@@ -509,5 +510,5 @@ limitations under the License.
|
||||
<string name="ant_bridge_sensor_option">SRM ANT+ Bridge</string>
|
||||
<string name="polar_bt_sensor_option">Polar Bluetooth</string>
|
||||
<string name="zephyr_bt_sensor_option">Zephyr Bluetooth</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -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
|
||||
@@ -26,7 +26,7 @@ public abstract class Constants {
|
||||
* Should be used by all log statements
|
||||
*/
|
||||
public static final String TAG = "MyTracks";
|
||||
|
||||
|
||||
/**
|
||||
* Name of the top-level directory inside the SD card where our files will
|
||||
* be read from/written to.
|
||||
@@ -39,32 +39,21 @@ public abstract class Constants {
|
||||
|
||||
public static final int GET_LOGIN = 0;
|
||||
public static final int GET_MAP = 1;
|
||||
public static final int CREATE_MAP = 2;
|
||||
public static final int SHOW_TRACK = 3;
|
||||
public static final int ADD_LIST = 4;
|
||||
public static final int FEATURE_DETAILS = 5;
|
||||
public static final int START_RECORDING = 6;
|
||||
public static final int STOP_RECORDING = 7;
|
||||
public static final int AUTHENTICATE_TO_MY_MAPS = 8;
|
||||
public static final int AUTHENTICATE_TO_FUSION_TABLES = 9;
|
||||
public static final int AUTHENTICATE_TO_DOCLIST = 10;
|
||||
public static final int AUTHENTICATE_TO_TRIX = 11;
|
||||
public static final int DELETE_TRACK = 13;
|
||||
public static final int SEND_TO_GOOGLE = 14;
|
||||
public static final int SHARE_LINK = 15;
|
||||
public static final int SHARE_GPX_FILE = 16;
|
||||
public static final int SHARE_KML_FILE = 17;
|
||||
public static final int SHARE_CSV_FILE = 18;
|
||||
public static final int SHARE_TCX_FILE = 19;
|
||||
public static final int EDIT_DETAILS = 20;
|
||||
public static final int SAVE_GPX_FILE = 21;
|
||||
public static final int SAVE_KML_FILE = 22;
|
||||
public static final int SAVE_CSV_FILE = 23;
|
||||
public static final int SAVE_TCX_FILE = 24;
|
||||
public static final int CLEAR_MAP = 25;
|
||||
public static final int SHOW_WAYPOINT = 26;
|
||||
public static final int EDIT_WAYPOINT = 27;
|
||||
public static final int WELCOME = 28;
|
||||
public static final int SHOW_TRACK = 2;
|
||||
public static final int AUTHENTICATE_TO_MY_MAPS = 3;
|
||||
public static final int AUTHENTICATE_TO_FUSION_TABLES = 4;
|
||||
public static final int AUTHENTICATE_TO_DOCLIST = 5;
|
||||
public static final int AUTHENTICATE_TO_TRIX = 6;
|
||||
public static final int SHARE_GPX_FILE = 7;
|
||||
public static final int SHARE_KML_FILE = 8;
|
||||
public static final int SHARE_CSV_FILE = 9;
|
||||
public static final int SHARE_TCX_FILE = 10;
|
||||
public static final int SAVE_GPX_FILE = 11;
|
||||
public static final int SAVE_KML_FILE = 12;
|
||||
public static final int SAVE_CSV_FILE = 13;
|
||||
public static final int SAVE_TCX_FILE = 14;
|
||||
public static final int SHOW_WAYPOINT = 15;
|
||||
public static final int WELCOME = 16;
|
||||
|
||||
/*
|
||||
* Menu ids:
|
||||
@@ -179,7 +168,7 @@ public abstract class Constants {
|
||||
* The type of account that we can use for gdata uploads.
|
||||
*/
|
||||
public static final String ACCOUNT_TYPE = "com.google";
|
||||
|
||||
|
||||
/**
|
||||
* The name of extra intent property to indicate whether we want to resume
|
||||
* a previously recorded track.
|
||||
@@ -189,14 +178,6 @@ public abstract class Constants {
|
||||
|
||||
public static int getActionFromMenuId(int menuId) {
|
||||
switch (menuId) {
|
||||
case Constants.MENU_SEND_TO_GOOGLE:
|
||||
return Constants.SEND_TO_GOOGLE;
|
||||
case Constants.MENU_EDIT:
|
||||
return Constants.EDIT_DETAILS;
|
||||
case Constants.MENU_DELETE:
|
||||
return Constants.DELETE_TRACK;
|
||||
case Constants.MENU_SHARE_LINK:
|
||||
return Constants.SHARE_LINK;
|
||||
case Constants.MENU_SHARE_KML_FILE:
|
||||
return Constants.SHARE_KML_FILE;
|
||||
case Constants.MENU_SHARE_GPX_FILE:
|
||||
@@ -213,8 +194,6 @@ public abstract class Constants {
|
||||
return Constants.SAVE_CSV_FILE;
|
||||
case Constants.MENU_SAVE_TCX_FILE:
|
||||
return Constants.SAVE_TCX_FILE;
|
||||
case Constants.MENU_CLEAR_MAP:
|
||||
return Constants.CLEAR_MAP;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentUris;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Activity used to delete a track.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class DeleteTrack extends Activity
|
||||
implements DialogInterface.OnClickListener, OnCancelListener {
|
||||
private static final int CONFIRM_DIALOG = 1;
|
||||
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
|
||||
private long deleteTrackId;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Uri data = intent.getData();
|
||||
if (!Intent.ACTION_DELETE.equals(action) ||
|
||||
!UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
|
||||
Log.e(TAG, "Got bad delete intent: " + intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
deleteTrackId = ContentUris.parseId(data);
|
||||
|
||||
showDialog(CONFIRM_DIALOG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != CONFIRM_DIALOG) {
|
||||
Log.e(TAG, "Unknown dialog " + id);
|
||||
return null;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(getString(R.string.track_will_be_permanently_deleted));
|
||||
builder.setTitle(getString(R.string.are_you_sure_question));
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
builder.setPositiveButton(getString(R.string.yes), this);
|
||||
builder.setNegativeButton(getString(R.string.no), this);
|
||||
builder.setOnCancelListener(this);
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int which) {
|
||||
dialogInterface.dismiss();
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
deleteTrack();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
|
||||
}
|
||||
|
||||
private void deleteTrack() {
|
||||
providerUtils.deleteTrack(deleteTrackId);
|
||||
|
||||
// If the track we just deleted was selected, unselect it.
|
||||
String selectedKey = getString(R.string.selected_track_key);
|
||||
SharedPreferences preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||
if (preferences.getLong(selectedKey, -1) == deleteTrackId) {
|
||||
Editor editor = preferences.edit().putLong(selectedKey, -1);
|
||||
ApiFeatures.getInstance().getApiPlatformAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType;
|
||||
import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendActivity;
|
||||
@@ -35,8 +36,10 @@ import com.google.android.maps.MapController;
|
||||
import com.google.android.maps.MapView;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
@@ -412,6 +415,23 @@ public class MapActivity extends com.google.android.maps.MapActivity
|
||||
SaveActivity.handleExportTrackAction(this, dataHub.getSelectedTrackId(),
|
||||
Constants.getActionFromMenuId(item.getItemId()));
|
||||
return true;
|
||||
case Constants.MENU_EDIT: {
|
||||
Intent intent = new Intent(this, TrackDetails.class);
|
||||
// TODO: Pass in a content URI
|
||||
intent.putExtra("trackid", dataHub.getSelectedTrackId());
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_DELETE: {
|
||||
Uri uri = ContentUris.withAppendedId(
|
||||
TracksColumns.CONTENT_URI, dataHub.getSelectedTrackId());
|
||||
Intent intent = new Intent(Intent.ACTION_DELETE, uri);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_CLEAR_MAP:
|
||||
dataHub.unloadCurrentTrack();
|
||||
return true;
|
||||
default:
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -24,7 +24,7 @@ import android.view.MenuItem;
|
||||
|
||||
/**
|
||||
* Manage the application menus.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
class MenuManager {
|
||||
@@ -34,7 +34,7 @@ class MenuManager {
|
||||
public MenuManager(MyTracks activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
activity.getMenuInflater().inflate(R.menu.main, menu);
|
||||
return true;
|
||||
@@ -52,7 +52,7 @@ class MenuManager {
|
||||
.setEnabled(isRecording)
|
||||
.setVisible(isRecording);
|
||||
}
|
||||
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_start_recording: {
|
||||
@@ -86,10 +86,6 @@ class MenuManager {
|
||||
case R.id.menu_help: {
|
||||
return startActivity(WelcomeActivity.class);
|
||||
}
|
||||
case Constants.MENU_CLEAR_MAP: {
|
||||
activity.clearSelectedTrack();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,10 +33,8 @@ import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.TabActivity;
|
||||
import android.content.ContentUris;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
@@ -61,6 +59,7 @@ import android.widget.Toast;
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
private TrackDataHub dataHub;
|
||||
|
||||
@@ -314,7 +313,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
dataHub.loadTrack(trackId);
|
||||
|
||||
// The track list passed the requested action as result code. Hand
|
||||
// it off to the onAcitivtyResult for further processing:
|
||||
// it off to the onActivityResult for further processing:
|
||||
if (resultCode != Constants.SHOW_TRACK) {
|
||||
onActivityResult(resultCode, Activity.RESULT_OK, results);
|
||||
}
|
||||
@@ -336,24 +335,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Constants.DELETE_TRACK: {
|
||||
if (results != null && resultCode == RESULT_OK) {
|
||||
deleteTrack(trackId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Constants.EDIT_DETAILS: {
|
||||
if (results != null && resultCode == RESULT_OK) {
|
||||
Intent intent = new Intent(this, TrackDetails.class);
|
||||
intent.putExtra("trackid", trackId);
|
||||
startActivity(intent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Constants.CLEAR_MAP: {
|
||||
dataHub.unloadCurrentTrack();
|
||||
break;
|
||||
}
|
||||
case Constants.WELCOME: {
|
||||
CheckUnits.check(this);
|
||||
break;
|
||||
@@ -373,36 +354,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the track with the given id.
|
||||
* Prompts the user if he want to really delete the track first.
|
||||
* If the selected track is deleted, the selection will be removed.
|
||||
*/
|
||||
public void deleteTrack(final long trackId) {
|
||||
AlertDialog dialog = null;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(getString(R.string.track_will_be_permanently_deleted));
|
||||
builder.setTitle(getString(R.string.are_you_sure_question));
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
builder.setPositiveButton(getString(R.string.yes),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
dialogInterface.dismiss();
|
||||
providerUtils.deleteTrack(trackId);
|
||||
if (trackId == dataHub.getSelectedTrackId()) {
|
||||
dataHub.unloadCurrentTrack();
|
||||
}
|
||||
}});
|
||||
builder.setNegativeButton(getString(R.string.no),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
dialogInterface.dismiss();
|
||||
}
|
||||
});
|
||||
dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a waypoint marker.
|
||||
*
|
||||
@@ -493,10 +444,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
}
|
||||
}
|
||||
|
||||
void clearSelectedTrack() {
|
||||
dataHub.unloadCurrentTrack();
|
||||
}
|
||||
|
||||
long getSelectedTrackId() {
|
||||
return dataHub.getSelectedTrackId();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendActivity;
|
||||
@@ -25,11 +27,14 @@ import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MenuItem;
|
||||
@@ -182,14 +187,16 @@ public class TrackList extends ListActivity
|
||||
SaveActivity.handleExportTrackAction(this, trackId,
|
||||
Constants.getActionFromMenuId(item.getItemId()));
|
||||
return true;
|
||||
default: {
|
||||
Intent result = new Intent();
|
||||
result.putExtra("trackid", trackId);
|
||||
setResult(
|
||||
Constants.getActionFromMenuId(item.getItemId()), result);
|
||||
finish();
|
||||
case Constants.MENU_DELETE: {
|
||||
Intent intent = new Intent(Intent.ACTION_DELETE);
|
||||
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
|
||||
intent.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
Log.w(TAG, "Unknown menu item: " + item.getItemId() + "(" + item.getTitle() + ")");
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,11 +96,7 @@ public class WaypointsList extends ListActivity
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
Intent result = new Intent();
|
||||
result.putExtra("trackid", trackId);
|
||||
result.putExtra(WaypointDetails.WAYPOINT_ID_EXTRA, id);
|
||||
setResult(Constants.EDIT_WAYPOINT, result);
|
||||
finish();
|
||||
editWaypoint(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,14 +104,15 @@ public class WaypointsList extends ListActivity
|
||||
if (!super.onMenuItemSelected(featureId, item)) {
|
||||
switch (item.getItemId()) {
|
||||
case Constants.MENU_SHOW: {
|
||||
onListItemClick(null, null, 0, selectedWaypointId);
|
||||
Intent result = new Intent();
|
||||
result.putExtra("trackid", trackId);
|
||||
result.putExtra(WaypointDetails.WAYPOINT_ID_EXTRA, selectedWaypointId);
|
||||
setResult(RESULT_OK, result);
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_EDIT: {
|
||||
Intent intent = new Intent(this, WaypointDetails.class);
|
||||
intent.putExtra("trackid", trackId);
|
||||
intent.putExtra(WaypointDetails.WAYPOINT_ID_EXTRA, selectedWaypointId);
|
||||
startActivity(intent);
|
||||
editWaypoint(selectedWaypointId);
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_DELETE: {
|
||||
@@ -126,6 +123,13 @@ public class WaypointsList extends ListActivity
|
||||
return false;
|
||||
}
|
||||
|
||||
private void editWaypoint(long waypointId) {
|
||||
Intent intent = new Intent(this, WaypointDetails.class);
|
||||
intent.putExtra("trackid", trackId);
|
||||
intent.putExtra(WaypointDetails.WAYPOINT_ID_EXTRA, waypointId);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@@ -412,10 +412,10 @@ public class TrackDataHub {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the selection to memory and flash.
|
||||
// Save the selection to memory and flush.
|
||||
selectedTrackId = trackId;
|
||||
ApiFeatures.getInstance().getApiPlatformAdapter().applyPreferenceChanges(
|
||||
preferences.edit().putLong(SELECTED_TRACK_KEY, trackId));
|
||||
selectedTrackId = trackId;
|
||||
|
||||
// Force it to reload data from the beginning.
|
||||
Log.d(TAG, "Loading track");
|
||||
@@ -642,6 +642,9 @@ public class TrackDataHub {
|
||||
} else if (SPEED_REPORTING_KEY.equals(key)) {
|
||||
reportSpeed = preferences.getBoolean(SPEED_REPORTING_KEY, true);
|
||||
notifySpeedReportingChanged();
|
||||
} else if (SELECTED_TRACK_KEY.equals(key)) {
|
||||
long trackId = preferences.getLong(SELECTED_TRACK_KEY, -1);
|
||||
loadTrack(trackId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.ProgressIndicator;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
@@ -50,7 +51,6 @@ import android.util.Log;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -695,7 +695,7 @@ public class SendToFusionTables implements Runnable {
|
||||
if (success) {
|
||||
byte[] result = new byte[1024];
|
||||
int read = response.getContent().read(result);
|
||||
String s = new String(result, 0, read, Charset.forName("UTF8"));
|
||||
String s = new String(result, 0, read, "UTF8");
|
||||
String[] lines = s.split(Strings.LINE_SEPARATOR);
|
||||
if (lines[0].equals("tableid")) {
|
||||
tableId = lines[1];
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.util.TimeZone;
|
||||
* Garmin Training Center 3.5.3.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Dominik Ršttsches
|
||||
*/
|
||||
public class TcxTrackWriter implements TrackFormatWriter {
|
||||
protected static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
@@ -64,6 +65,9 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
||||
private PrintWriter pw = null;
|
||||
private Track track;
|
||||
|
||||
// Determines whether to encode cadence value as running or cycling cadence.
|
||||
private boolean sportIsCycling;
|
||||
|
||||
public TcxTrackWriter(Context context) {
|
||||
this.context = context;
|
||||
|
||||
@@ -76,7 +80,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
||||
public void prepare(Track track, OutputStream out) {
|
||||
this.track = track;
|
||||
this.pw = new PrintWriter(out);
|
||||
|
||||
this.sportIsCycling = categoryToTcxSport(track.getCategory()).equals(TCX_SPORT_BIKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,41 +179,38 @@ public class TcxTrackWriter implements TrackFormatWriter {
|
||||
pw.println("</HeartRateBpm>");
|
||||
}
|
||||
|
||||
boolean cadenceAsCyclingCadence = categoryToTcxSport(track.getCategory()).equals(TCX_SPORT_BIKING);
|
||||
|
||||
boolean cadenceValueAvailable = sensorData.hasCadence()
|
||||
boolean cadenceAvailable = sensorData.hasCadence()
|
||||
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getCadence().hasValue();
|
||||
|
||||
// TCX Trackpoint_t contains a sequence,
|
||||
// legacy XML element <Cadence> needs to be put before <Extensions>
|
||||
// Only for the case that activity was marked as biking.
|
||||
// Otherwise cadence we interpret it as running cadence data
|
||||
// and put it into <Extensions>.
|
||||
if (cadenceAsCyclingCadence && cadenceValueAvailable) {
|
||||
// TCX Trackpoint_t contains a sequence. Thus, the legacy XML element
|
||||
// <Cadence> needs to be put before <Extensions>.
|
||||
// This field should only be used for the case that activity was marked as biking.
|
||||
// Otherwise cadence is interpreted as running cadence data which
|
||||
// is written in the <Extensions> as <RunCadence>.
|
||||
if (sportIsCycling && cadenceAvailable) {
|
||||
pw.print(" <Cadence>");
|
||||
pw.print(Math.min(254, sensorData.getCadence().getValue()));
|
||||
pw.println("</Cadence>");
|
||||
}
|
||||
|
||||
boolean powerValueAvailable = sensorData.hasPower()
|
||||
boolean powerAvailable = sensorData.hasPower()
|
||||
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorData.getPower().hasValue();
|
||||
|
||||
if(powerValueAvailable
|
||||
|| (!cadenceAsCyclingCadence && cadenceValueAvailable)) {
|
||||
if(powerAvailable || (!sportIsCycling && cadenceAvailable)) {
|
||||
pw.print(" <Extensions>");
|
||||
pw.print("<TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">");
|
||||
|
||||
// RunCadence needs to be put before power in order to be understood
|
||||
// by Garmin Training Center.
|
||||
if (!cadenceAsCyclingCadence && cadenceValueAvailable) {
|
||||
if (!sportIsCycling && cadenceAvailable) {
|
||||
pw.print("<RunCadence>");
|
||||
pw.print(sensorData.getCadence().getValue());
|
||||
pw.print(Math.min(254, sensorData.getCadence().getValue()));
|
||||
pw.print("</RunCadence>");
|
||||
}
|
||||
|
||||
if (powerValueAvailable) {
|
||||
if (powerAvailable) {
|
||||
pw.print("<Watts>");
|
||||
pw.print(sensorData.getPower().getValue());
|
||||
pw.print("</Watts>");
|
||||
|
||||
+32
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
* Copyright 2011 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
|
||||
@@ -19,61 +19,62 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Storage of a history of readings of the Zephyr stride counter,
|
||||
* in order to derive a correct cadence value from it,
|
||||
* working around an issue with the HxM's firmware.
|
||||
* A history of Zephyr stride counter reading.
|
||||
* These can be used as an alternate method to calculate the correct cadence.
|
||||
* This is a work around for an issue with some HxM firmware.
|
||||
*
|
||||
* @author Dominik Ršttsches
|
||||
*/
|
||||
public class StrideReadings {
|
||||
private static class StrideReading {
|
||||
// TODO: Check whether 1Hz assumption is okay for cadence calculation
|
||||
// otherwise use timeMs, which is taken from heart beat timestamp.
|
||||
@SuppressWarnings("unused")
|
||||
public int timeMs;
|
||||
public int numStrides;
|
||||
|
||||
StrideReading(int newTimeMs, int newNumStrides) {
|
||||
timeMs = newTimeMs;
|
||||
numStrides = newNumStrides;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int NUM_READINGS_FOR_AVERAGE = 10;
|
||||
private static final int MIN_READINGS_FOR_AVERAGE = 5;
|
||||
// visible for testing
|
||||
protected static final int NUM_READINGS_FOR_AVERAGE = 10;
|
||||
protected static final int MIN_READINGS_FOR_AVERAGE = 5;
|
||||
|
||||
protected static final int CADENCE_NOT_AVAILABLE = -1;
|
||||
|
||||
private List<StrideReading> strideReadingsHistory;
|
||||
// TODO: Check whether 1Hz assumption is okay for cadence calculation
|
||||
// otherwise add heart beat timestamp to this list and compute
|
||||
// cadence from these timestamps.
|
||||
private final List<Integer> strideReadingsHistory;
|
||||
|
||||
public StrideReadings() {
|
||||
strideReadingsHistory = new LinkedList<StrideReading>();
|
||||
strideReadingsHistory = new LinkedList<Integer>();
|
||||
}
|
||||
|
||||
public void updateStrideReading(int timeInMs, int numStrides) {
|
||||
public void updateStrideReading(int numStrides) {
|
||||
// HRM/HxM documentation says, transmission frequency is 1 Hz,
|
||||
// let's keep last NUM_READINGS_FOR_AVERAGE readings.
|
||||
// TODO: Calibrate this using a reliable footpod / cadence sensor,
|
||||
// otherwise use heartbeat timestamp for calculation.
|
||||
strideReadingsHistory.add(0, new StrideReading(timeInMs, numStrides));
|
||||
while(strideReadingsHistory.size() > NUM_READINGS_FOR_AVERAGE) {
|
||||
strideReadingsHistory.add(0, numStrides);
|
||||
while (strideReadingsHistory.size() > NUM_READINGS_FOR_AVERAGE) {
|
||||
strideReadingsHistory.remove(strideReadingsHistory.size()-1);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCadence() {
|
||||
if(strideReadingsHistory.size() < MIN_READINGS_FOR_AVERAGE) {
|
||||
if (strideReadingsHistory.size() < MIN_READINGS_FOR_AVERAGE) {
|
||||
// Bail out if we cannot really get a meaningful average yet.
|
||||
return CADENCE_NOT_AVAILABLE;
|
||||
}
|
||||
// compute assuming 1 stride reading/second
|
||||
// Compute assuming 1 stride reading/second.
|
||||
int timeSinceOldestReadingSecs = strideReadingsHistory.size() - 1;
|
||||
int stridesThen = strideReadingsHistory
|
||||
.get(strideReadingsHistory.size()-1).numStrides;
|
||||
int stridesNow = strideReadingsHistory
|
||||
.get(0).numStrides;
|
||||
// Contrary to documentation stride value seems to roll over every 127 strides.
|
||||
return Math.round( (float)((stridesNow - stridesThen) % 127) /
|
||||
int stridesThen = strideReadingsHistory.get(strideReadingsHistory.size()-1);
|
||||
int stridesNow = strideReadingsHistory.get(0);
|
||||
// Contrary to documentation stride value seems to roll over at 128.
|
||||
return Math.round( (float)(mod((stridesNow - stridesThen), 128)) /
|
||||
timeSinceOldestReadingSecs * 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modulo operation with positive return values, Java's remainder operator doesn't change sign.
|
||||
*
|
||||
* @return x mod y
|
||||
*/
|
||||
private static int mod(int x, int y)
|
||||
{
|
||||
int result = x % y;
|
||||
return result < 0 ? result + y : result;
|
||||
}
|
||||
}
|
||||
|
||||
+22
-38
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import android.util.Log;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An implementation of a Sensor MessageParser for Zephyr.
|
||||
@@ -32,27 +31,12 @@ public class ZephyrMessageParser implements MessageParser {
|
||||
public static final int ZEPHYR_HXM_BYTE_CRC = 58;
|
||||
public static final int ZEPHYR_HXM_BYTE_ETX = 59;
|
||||
|
||||
private static final String CADENCE_BUG_FW_ID = "1A00316550003162";
|
||||
private static final byte[] CADENCE_BUG_FW_ID = {0x1A, 0x00, 0x31, 0x65, 0x50, 0x00, 0x31, 0x62};
|
||||
|
||||
private StrideReadings strideReadings;
|
||||
|
||||
@Override
|
||||
public Sensor.SensorDataSet parseBuffer(byte[] buffer) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
sb.append(String.format("%02X", buffer[i]));
|
||||
}
|
||||
Log.w(Constants.TAG, "Got zephyr data: " + sb);
|
||||
|
||||
// Device Firmware ID, Firmware Version, Hardware ID, Hardware Version
|
||||
// 0x1A00316550003162 produces erroneous values for Cadence and needs
|
||||
// a workaround based on the stride counter.
|
||||
// Firmware values range from field 3 to 11 of the byte buffer,
|
||||
// since in hex there are two characters for each byte, doubling the values.
|
||||
String hardwareFirmwareId = sb.substring(6, 22);
|
||||
boolean computeCadenceFromStrides = hardwareFirmwareId.equals(CADENCE_BUG_FW_ID);
|
||||
Log.d(Constants.TAG, "FW & HW Ids & Version " + hardwareFirmwareId + " needs workaround: " + computeCadenceFromStrides);
|
||||
|
||||
Sensor.SensorDataSet.Builder sds =
|
||||
Sensor.SensorDataSet.newBuilder()
|
||||
.setCreationTime(System.currentTimeMillis());
|
||||
@@ -60,42 +44,42 @@ public class ZephyrMessageParser implements MessageParser {
|
||||
Sensor.SensorData.Builder heartrate = Sensor.SensorData.newBuilder()
|
||||
.setValue(buffer[12] & 0xFF)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
sds = sds.setHeartRate(heartrate);
|
||||
sds.setHeartRate(heartrate);
|
||||
|
||||
Sensor.SensorData.Builder batteryLevel = Sensor.SensorData.newBuilder()
|
||||
.setValue(buffer[11])
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
sds = sds.setBatteryLevel(batteryLevel);
|
||||
sds.setBatteryLevel(batteryLevel);
|
||||
|
||||
// Appends cadence to SensorDataSet builder if available.
|
||||
parseOrComputeCadence(sds, buffer, computeCadenceFromStrides);
|
||||
setCadence(sds, buffer);
|
||||
|
||||
return sds.build();
|
||||
}
|
||||
|
||||
private void parseOrComputeCadence(Sensor.SensorDataSet.Builder sds, byte[] buffer, boolean computeFromStrides) {
|
||||
|
||||
private void setCadence(Sensor.SensorDataSet.Builder sds, byte[] buffer) {
|
||||
// Device Firmware ID, Firmware Version, Hardware ID, Hardware Version
|
||||
// 0x1A00316550003162 produces erroneous values for Cadence and needs
|
||||
// a workaround based on the stride counter.
|
||||
// Firmware values range from field 3 to 10 (inclusive) of the byte buffer.
|
||||
byte[] hardwareFirmwareId = Arrays.copyOfRange(buffer, 3, 11);
|
||||
|
||||
Sensor.SensorData.Builder cadence = Sensor.SensorData.newBuilder();
|
||||
|
||||
if(!computeFromStrides) {
|
||||
cadence = cadence
|
||||
.setValue(SensorUtils.unsignedShortToIntLittleEndian(buffer, 56) / 16)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
sds.setCadence(cadence);
|
||||
} else {
|
||||
if(strideReadings == null) {
|
||||
if (Arrays.equals(hardwareFirmwareId, CADENCE_BUG_FW_ID)) {
|
||||
if (strideReadings == null) {
|
||||
strideReadings = new StrideReadings();
|
||||
}
|
||||
strideReadings.updateStrideReading(
|
||||
SensorUtils.unsignedShortToIntLittleEndian(buffer, 14),
|
||||
buffer[54] & 0xFF);
|
||||
strideReadings.updateStrideReading(buffer[54] & 0xFF);
|
||||
|
||||
if(strideReadings.getCadence() != StrideReadings.CADENCE_NOT_AVAILABLE) {
|
||||
cadence = cadence.setValue(strideReadings.getCadence())
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
sds.setCadence(cadence);
|
||||
if (strideReadings.getCadence() != StrideReadings.CADENCE_NOT_AVAILABLE) {
|
||||
cadence.setValue(strideReadings.getCadence()).setState(Sensor.SensorState.SENDING);
|
||||
}
|
||||
} else {
|
||||
cadence
|
||||
.setValue(SensorUtils.unsignedShortToIntLittleEndian(buffer, 56) / 16)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
}
|
||||
sds.setCadence(cadence);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+44
-10
@@ -1,29 +1,63 @@
|
||||
/*
|
||||
* Copyright 2011 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.sensors;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author Dominik Ršttsches
|
||||
*/
|
||||
public class StrideReadingsTest extends TestCase {
|
||||
|
||||
public void testNoReadingOnStartup() {
|
||||
StrideReadings strideReadings = new StrideReadings();
|
||||
assertTrue(strideReadings.getCadence() == StrideReadings.CADENCE_NOT_AVAILABLE);
|
||||
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
|
||||
}
|
||||
|
||||
public void testAverageCadenceAvailable() {
|
||||
StrideReadings strideReadings = new StrideReadings();
|
||||
for(int i=0;i<30;i++) {
|
||||
strideReadings.updateStrideReading(i*1000, i*2);
|
||||
// 2 steps / second => Cadence is 120 / minute
|
||||
for (int i = 1; i <= 30; i++) {
|
||||
strideReadings.updateStrideReading(i*2);
|
||||
if (i >= StrideReadings.MIN_READINGS_FOR_AVERAGE) {
|
||||
assertEquals(120, strideReadings.getCadence());
|
||||
} else {
|
||||
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
|
||||
}
|
||||
}
|
||||
assertTrue(strideReadings.getCadence() > 0);
|
||||
}
|
||||
|
||||
/* testing rollover at 127, just like the HxM seems to do it */
|
||||
/**
|
||||
* Tests for correct calculation after rolling over at 128 strides,
|
||||
* just like the HxM seems to do it.
|
||||
*/
|
||||
public void testRollover() {
|
||||
StrideReadings strideReadings = new StrideReadings();
|
||||
for(int i=0;i<=10;i++) {
|
||||
strideReadings.updateStrideReading(i*1000, (120 + i*2) );
|
||||
}
|
||||
assertTrue(strideReadings.getCadence() > 0);
|
||||
}
|
||||
// 1 step per second => Cadence is 60 / minute
|
||||
// Updating readings counting upwards from initialStrides -
|
||||
// initialStrides set to a value below 128 to ensure rollover.
|
||||
int initialStrides = 128 - StrideReadings.NUM_READINGS_FOR_AVERAGE - 5;
|
||||
|
||||
for (int i = 1; i <= StrideReadings.NUM_READINGS_FOR_AVERAGE + 10; i++) {
|
||||
strideReadings.updateStrideReading((initialStrides + i) % 128);
|
||||
if (i >= StrideReadings.MIN_READINGS_FOR_AVERAGE) {
|
||||
assertEquals(60, strideReadings.getCadence());
|
||||
} else {
|
||||
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user