Merging to head.

This commit is contained in:
Rodrigo Damazio
2011-04-30 02:42:35 -03:00
24 changed files with 238 additions and 92 deletions
-1
View File
@@ -154,7 +154,6 @@ limitations under the License.
<string name="tracklist_clear_map">マップのクリア</string> <string name="tracklist_clear_map">マップのクリア</string>
<!-- Import All --> <!-- Import All -->
<string name="tracklist_btn_import_all">全てインポート</string> <string name="tracklist_btn_import_all">全てインポート</string>
<string name="import_done">インポート終了</string>
<!-- Format: <!-- Format:
$1 : Successful imports $1 : Successful imports
$2 : Attempted imports $2 : Attempted imports
-1
View File
@@ -157,7 +157,6 @@ limitations under the License.
<string name="tracklist_clear_map">Очистить карту</string> <string name="tracklist_clear_map">Очистить карту</string>
<!-- Import All --> <!-- Import All -->
<string name="tracklist_btn_import_all">Импорт всех</string> <string name="tracklist_btn_import_all">Импорт всех</string>
<string name="import_done">Импорт закончен</string>
<!-- Format: <!-- Format:
$1 : Successful imports $1 : Successful imports
$2 : Attempted imports $2 : Attempted imports
+1 -1
View File
@@ -45,6 +45,7 @@ limitations under the License.
<string name="error_invalid_gpx_format">File does not have a valid GPX format.</string> <string name="error_invalid_gpx_format">File does not have a valid GPX format.</string>
<string name="error_generic">An error occurred. Please try again.</string> <string name="error_generic">An error occurred. Please try again.</string>
<string name="error_track_does_not_exist">Track does not exist.</string> <string name="error_track_does_not_exist">Track does not exist.</string>
<string name="error_operation_cancelled">The operation was cancelled.</string>
<string name="error_out_of_memory">This file is too big (must be less than 500 KB). Aborting.</string> <string name="error_out_of_memory">This file is too big (must be less than 500 KB). Aborting.</string>
<string name="error_unable_to_insert_marker">Unable to insert marker. No location fix available.</string> <string name="error_unable_to_insert_marker">Unable to insert marker. No location fix available.</string>
@@ -157,7 +158,6 @@ limitations under the License.
<string name="tracklist_clear_map">Clear map</string> <string name="tracklist_clear_map">Clear map</string>
<!-- Import All --> <!-- Import All -->
<string name="tracklist_btn_import_all">Import all</string> <string name="tracklist_btn_import_all">Import all</string>
<string name="import_done">Import finished, imported</string>
<!-- Format: <!-- Format:
$1 : Successful imports $1 : Successful imports
$2 : Attempted imports $2 : Attempted imports
@@ -528,7 +528,7 @@ public class ChartView extends View {
if (nearestWaypoint != null && dmin < 100) { if (nearestWaypoint != null && dmin < 100) {
Intent intent = Intent intent =
new Intent(getContext(), WaypointDetails.class); new Intent(getContext(), WaypointDetails.class);
intent.putExtra("waypointid", nearestWaypoint.getId()); intent.putExtra(WaypointDetails.WAYPOINT_ID_EXTRA, nearestWaypoint.getId());
getContext().startActivity(intent); getContext().startActivity(intent);
return true; return true;
} }
@@ -728,7 +728,7 @@ public class ChartView extends View {
} }
} }
float density = getContext().getResources().getDisplayMetrics().density; float density = getContext().getResources().getDisplayMetrics().density;
// TODO: This should be function of the number of variables displayed. maxLength = Math.max(maxLength, 1);
leftBorder = (int) (density * (4 + 8 * maxLength)); leftBorder = (int) (density * (4 + 8 * maxLength));
effectiveWidth = w - leftBorder - RIGHT_BORDER; effectiveWidth = w - leftBorder - RIGHT_BORDER;
bottomBorder = (int) (density * BOTTOM_BORDER); bottomBorder = (int) (density * BOTTOM_BORDER);
@@ -187,6 +187,14 @@ public abstract class Constants {
public static final String RESUME_TRACK_EXTRA_NAME = public static final String RESUME_TRACK_EXTRA_NAME =
"com.google.android.apps.mytracks.RESUME_TRACK"; "com.google.android.apps.mytracks.RESUME_TRACK";
/**
* The name of the extra intent property that holds the track information
* when sharing with friends
*/
public static final String TRACK_EXTRA =
"com.google.android.apps.mytracks.TRACK";
public static int getActionFromMenuId(int menuId) { public static int getActionFromMenuId(int menuId) {
switch (menuId) { switch (menuId) {
case Constants.MENU_SEND_TO_GOOGLE: case Constants.MENU_SEND_TO_GOOGLE:
@@ -461,7 +461,7 @@ public class MapOverlay extends Overlay {
if (waypoint != null && if (waypoint != null &&
dmin < 15000000 / Math.pow(2, mapView.getZoomLevel())) { dmin < 15000000 / Math.pow(2, mapView.getZoomLevel())) {
Intent intent = new Intent(context, WaypointDetails.class); Intent intent = new Intent(context, WaypointDetails.class);
intent.putExtra("waypointid", waypoint.getId()); intent.putExtra(WaypointDetails.WAYPOINT_ID_EXTRA, waypoint.getId());
context.startActivity(intent); context.startActivity(intent);
return true; return true;
} }
@@ -16,6 +16,7 @@
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
import static com.google.android.apps.mytracks.Constants.TRACK_EXTRA;
import static com.google.android.apps.mytracks.DialogManager.DIALOG_IMPORT_PROGRESS; import static com.google.android.apps.mytracks.DialogManager.DIALOG_IMPORT_PROGRESS;
import static com.google.android.apps.mytracks.DialogManager.DIALOG_PROGRESS; import static com.google.android.apps.mytracks.DialogManager.DIALOG_PROGRESS;
import static com.google.android.apps.mytracks.DialogManager.DIALOG_SEND_TO_GOOGLE; import static com.google.android.apps.mytracks.DialogManager.DIALOG_SEND_TO_GOOGLE;
@@ -1159,9 +1160,9 @@ public class MyTracks extends TabActivity implements OnTouchListener,
return false; return false;
} }
protected String getFusionTablesUrl(long sendToTrackId2) { protected String getFusionTablesUrl(long trackId) {
Track track = providerUtils.getTrack(sendToTrackId); Track track = providerUtils.getTrack(trackId);
return SendToFusionTables.getMapVisualizationUrl(track); return track == null ? "" : SendToFusionTables.getMapVisualizationUrl(track);
} }
/** /**
@@ -1245,7 +1246,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
* *
* @param trackId The id of the track to be sent * @param trackId The id of the track to be sent
*/ */
public void sendTrack(long trackId, final TrackFileFormat format) { public void sendTrack(final long trackId, final TrackFileFormat format) {
TrackWriter writer = TrackWriterFactory.newWriter(this, providerUtils, trackId, format); TrackWriter writer = TrackWriterFactory.newWriter(this, providerUtils, trackId, format);
FileUtils fileUtils = new FileUtils(); FileUtils fileUtils = new FileUtils();
@@ -1273,6 +1274,8 @@ public class MyTracks extends TabActivity implements OnTouchListener,
shareIntent.setType(format.getMimeType()); shareIntent.setType(format.getMimeType());
Uri u = Uri.fromFile(new File(writer.getAbsolutePath())); Uri u = Uri.fromFile(new File(writer.getAbsolutePath()));
shareIntent.putExtra(Intent.EXTRA_STREAM, u); shareIntent.putExtra(Intent.EXTRA_STREAM, u);
Track track = providerUtils.getTrack(trackId);
shareIntent.putExtra(TRACK_EXTRA, track);
startActivity(Intent.createChooser(shareIntent, startActivity(Intent.createChooser(shareIntent,
getResources().getText(R.string.share_track).toString())); getResources().getText(R.string.share_track).toString()));
} }
@@ -197,8 +197,13 @@ public class TrackList extends ListActivity
listView = getListView(); listView = getListView();
listView.setOnCreateContextMenuListener(contextMenuListener); listView.setOnCreateContextMenuListener(contextMenuListener);
findViewById(R.id.tracklist_btn_delete_all).setOnClickListener(this); View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
findViewById(R.id.tracklist_btn_export_all).setOnClickListener(this); View exportAll = findViewById(R.id.tracklist_btn_export_all);
boolean notRecording = !MyTracks.getInstance().isRecording();
deleteAll.setOnClickListener(this);
deleteAll.setEnabled(notRecording);
exportAll.setOnClickListener(this);
exportAll.setEnabled(notRecording);
findViewById(R.id.tracklist_btn_import_all).setOnClickListener(this); findViewById(R.id.tracklist_btn_import_all).setOnClickListener(this);
SharedPreferences preferences = SharedPreferences preferences =
@@ -20,6 +20,7 @@ import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.DialogInterface;
/** /**
* Given a {@link TrackWriter}, this class manages the process of writing the * Given a {@link TrackWriter}, this class manages the process of writing the
@@ -66,6 +67,7 @@ class WriteProgressController {
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMessage(activity.getString(R.string.write_progress_message)); dialog.setMessage(activity.getString(R.string.write_progress_message));
dialog.setIndeterminate(true); dialog.setIndeterminate(true);
dialog.setOnCancelListener(dialogCancelListener);
writer.setOnCompletionListener(writerCompleteListener); writer.setOnCompletionListener(writerCompleteListener);
writer.setOnWriteListener(writerWriteListener); writer.setOnWriteListener(writerWriteListener);
@@ -87,6 +89,14 @@ class WriteProgressController {
return dialog; return dialog;
} }
private final DialogInterface.OnCancelListener dialogCancelListener =
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
writer.stopWriteTrack();
}
};
private final TrackWriter.OnCompletionListener writerCompleteListener = private final TrackWriter.OnCompletionListener writerCompleteListener =
new TrackWriter.OnCompletionListener() { new TrackWriter.OnCompletionListener() {
@Override @Override
@@ -1,12 +1,12 @@
/* /*
* Copyright 2010 Google Inc. * Copyright 2010 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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 * use this file except in compliance with the License. You may obtain a copy of
* the License at * the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -54,7 +54,7 @@ public interface TrackFormatWriter {
void prepare(Track track, OutputStream out); void prepare(Track track, OutputStream out);
/** /**
* @return The file extentsion (i.e. gpx, kml, ...) * @return The file extension (i.e. gpx, kml, ...)
*/ */
String getExtension(); String getExtension();
@@ -77,7 +77,7 @@ public interface TrackFormatWriter {
* *
* @param location the location to write * @param location the location to write
*/ */
void writeLocation(Location location); void writeLocation(Location location) throws InterruptedException;
/** /**
* Write a way point. * Write a way point.
@@ -75,6 +75,11 @@ public interface TrackWriter {
*/ */
void writeTrack(); void writeTrack();
/**
* Stop any in-progress writes
*/
void stopWriteTrack();
/** /**
* Returns true if the write completed successfully. * Returns true if the write completed successfully.
*/ */
@@ -57,6 +57,7 @@ class TrackWriterImpl implements TrackWriter {
private File file = null; private File file = null;
private OnCompletionListener onCompletionListener; private OnCompletionListener onCompletionListener;
private OnWriteListener onWriteListener; private OnWriteListener onWriteListener;
private Thread writeThread;
TrackWriterImpl(Context context, MyTracksProviderUtils providerUtils, TrackWriterImpl(Context context, MyTracksProviderUtils providerUtils,
Track track, TrackFormatWriter writer) { Track track, TrackFormatWriter writer) {
@@ -89,28 +90,60 @@ class TrackWriterImpl implements TrackWriter {
@Override @Override
public void writeTrackAsync() { public void writeTrackAsync() {
Thread t = new Thread() { writeThread = new Thread() {
@Override @Override
public void run() { public void run() {
writeTrack(); doWriteTrack();
} }
}; };
t.start(); writeThread.start();
} }
@Override @Override
public void writeTrack() { public void writeTrack() {
writeTrackAsync();
try {
writeThread.join();
} catch (InterruptedException e) {
Log.e(Constants.TAG, "Interrupted waiting for write to complete", e);
}
}
private void doWriteTrack() {
// Open the input and output // Open the input and output
success = false; success = false;
errorMessage = R.string.error_track_does_not_exist; errorMessage = R.string.error_track_does_not_exist;
if (track != null) { if (track != null) {
if (openFile()) { if (openFile()) {
writeDocument(); try {
writeDocument();
} catch (InterruptedException e) {
Log.i(Constants.TAG, "The track write was interrupted");
if (file != null) {
file.delete();
}
success = false;
errorMessage = R.string.error_operation_cancelled;
}
} }
} }
finished(); finished();
} }
public void stopWriteTrack() {
if (writeThread != null && writeThread.isAlive()) {
Log.i(Constants.TAG, "Attempting to stop track write");
writeThread.interrupt();
try {
writeThread.join();
Log.i(Constants.TAG, "Track write stopped");
} catch (InterruptedException e) {
Log.e(Constants.TAG, "Failed to wait for writer to stop", e);
}
}
}
@Override @Override
public int getErrorMessage() { public int getErrorMessage() {
return errorMessage; return errorMessage;
@@ -253,7 +286,7 @@ class TrackWriterImpl implements TrackWriter {
/** /**
* Does the actual work of writing the track to the now open file. * Does the actual work of writing the track to the now open file.
*/ */
void writeDocument() { void writeDocument() throws InterruptedException {
Log.d(Constants.TAG, "Started writing track."); Log.d(Constants.TAG, "Started writing track.");
writer.writeHeader(); writer.writeHeader();
writeWaypoints(track.getId()); writeWaypoints(track.getId());
@@ -265,7 +298,7 @@ class TrackWriterImpl implements TrackWriter {
errorMessage = R.string.io_write_finished; errorMessage = R.string.io_write_finished;
} }
private void writeLocations() { private void writeLocations() throws InterruptedException {
boolean wroteFirst = false; boolean wroteFirst = false;
boolean segmentOpen = false; boolean segmentOpen = false;
boolean isLastValid = false; boolean isLastValid = false;
@@ -303,6 +336,9 @@ class TrackWriterImpl implements TrackWriter {
int pointNumber = 0; int pointNumber = 0;
while (it.hasNext()) { while (it.hasNext()) {
Location loc = it.next(); Location loc = it.next();
if (Thread.interrupted()) {
throw new InterruptedException();
}
pointNumber++; pointNumber++;
@@ -16,6 +16,8 @@
package com.google.android.apps.mytracks.services.sensors; package com.google.android.apps.mytracks.services.sensors;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.services.sensors.ant.AntDirectSensorManager;
import com.google.android.apps.mytracks.services.sensors.ant.AntSRMSensorManager;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
@@ -13,14 +13,12 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.services.sensors; package com.google.android.apps.mytracks.services.sensors.ant;
import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.services.sensors.ant.AntChannelIdMessage;
import com.google.android.apps.mytracks.services.sensors.ant.AntChannelResponseMessage;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import com.dsi.ant.AntDefine; import com.dsi.ant.AntDefine;
@@ -13,15 +13,17 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.services.sensors; package com.google.android.apps.mytracks.services.sensors.ant;
import com.google.android.apps.mytracks.Constants; import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.maps.mytracks.R;
import com.dsi.ant.AntDefine; import com.dsi.ant.AntDefine;
import com.dsi.ant.AntMesg; import com.dsi.ant.AntMesg;
import com.dsi.ant.exception.AntInterfaceException; import com.dsi.ant.exception.AntInterfaceException;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@@ -84,7 +86,7 @@ public class AntSRMSensorManager extends AntSensorManager {
byte recievedChannel = (byte) (antMessage[AntMesg.MESG_DATA_OFFSET] byte recievedChannel = (byte) (antMessage[AntMesg.MESG_DATA_OFFSET]
& AntDefine.CHANNEL_NUMBER_MASK); & AntDefine.CHANNEL_NUMBER_MASK);
if (recievedChannel != channel) { if (recievedChannel != channel) {
Log.d(Constants.TAG, "Unexpected channel: " + recievedChannel); Log.d(TAG, "Unexpected channel: " + recievedChannel);
return; return;
} }
switch (antMessage[AntMesg.MESG_ID_OFFSET]) { switch (antMessage[AntMesg.MESG_ID_OFFSET]) {
@@ -98,18 +100,17 @@ public class AntSRMSensorManager extends AntSensorManager {
handleChannelId(antMessage); handleChannelId(antMessage);
break; break;
default: default:
Log.e(Constants.TAG, Log.e(TAG, "Unexpected message id: " + antMessage[3]);
"Unexpected message id: " + antMessage[3]);
} }
} }
private void handleBroadcastData(byte[] antMessage) { private void handleBroadcastData(byte[] antMessage) {
if (deviceId == WILDCARD) { if (deviceId == WILDCARD) {
try { try {
Log.d(Constants.TAG, "Requesting channel id id."); Log.d(TAG, "Requesting channel id id.");
getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID); getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
Log.e(Constants.TAG, "Failed to request channel id id", e); Log.e(TAG, "Failed to request channel id id", e);
} }
} }
setSensorState(Sensor.SensorState.CONNECTED); setSensorState(Sensor.SensorState.CONNECTED);
@@ -121,15 +122,14 @@ public class AntSRMSensorManager extends AntSensorManager {
parseSensorData(antMessage); parseSensorData(antMessage);
break; break;
default: default:
Log.e(Constants.TAG, Log.e(TAG, "Unexpected message type: " + antMessage[MESSAGE_TYPE_INDEX]);
"Unexpected message type: " + antMessage[MESSAGE_TYPE_INDEX]);
} }
} }
private void handleChannelId(byte[] antMessage) { private void handleChannelId(byte[] antMessage) {
// Store the device id. // Store the device id.
deviceId = antMessage[3]; deviceId = antMessage[3];
Log.i(Constants.TAG, "Found device id: " + deviceId); Log.i(TAG, "Found device id: " + deviceId);
SharedPreferences prefs = context.getSharedPreferences( SharedPreferences prefs = context.getSharedPreferences(
Constants.SETTINGS_NAME, Context.MODE_PRIVATE); Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
@@ -142,38 +142,34 @@ public class AntSRMSensorManager extends AntSensorManager {
if (antMessage[3] == AntMesg.MESG_EVENT_ID if (antMessage[3] == AntMesg.MESG_EVENT_ID
&& antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) { && antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) {
// Search timeout // Search timeout
Log.w(Constants.TAG, "Search timed out. Unassigning channel."); Log.w(TAG, "Search timed out. Unassigning channel.");
try { try {
getAntReceiver().ANTUnassignChannel(channel); getAntReceiver().ANTUnassignChannel(channel);
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
Log.e(Constants.TAG, "Failed to unassign ANT channel", e); Log.e(TAG, "Failed to unassign ANT channel", e);
} }
setSensorState(Sensor.SensorState.DISCONNECTED); setSensorState(Sensor.SensorState.DISCONNECTED);
} else if (antMessage[3] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) { } else if (antMessage[3] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) {
setSensorState(Sensor.SensorState.DISCONNECTED); setSensorState(Sensor.SensorState.DISCONNECTED);
Log.i(Constants.TAG, Log.i(TAG, "Disconnected from the sensor: " + getSensorState());
"Disconnected from the sensor: " + getSensorState());
} }
} }
private void parseSensorData(byte[] antMessage) { private void parseSensorData(byte[] antMessage) {
if (antMessage.length != 11) { if (antMessage.length != 11) {
Log.e(Constants.TAG, Log.e(TAG, "Unexpected ant message length: " + antMessage.length);
"Unexpected ant message length: " + antMessage.length);
return; return;
} }
int newMessageId = antMessage[MESSAGE_ID_INDEX] & 0xFF; int newMessageId = antMessage[MESSAGE_ID_INDEX] & 0xFF;
if (lastMessageId == newMessageId) { if (lastMessageId == newMessageId) {
// Repeated message. // Repeated message.
Log.i(Constants.TAG, Log.i(TAG, String.format("SRM ignoring repeat: 0x%X", newMessageId));
String.format("SRM ignoring repeat: 0x%X", newMessageId));
return; return;
} }
if (newMessageId < lastMessageId) { if (newMessageId < lastMessageId) {
if (!(newMessageId < 20 && lastMessageId > 200)) { if (!(newMessageId < 20 && lastMessageId > 200)) {
Log.i(Constants.TAG, Log.i(TAG, String.format("SRM ignoring repeat: 0x%X", newMessageId));
String.format("SRM ignoring repeat: 0x%X", newMessageId));
return; return;
} // else assume the byte overflowed to 0. } // else assume the byte overflowed to 0.
} }
@@ -0,0 +1,46 @@
/*
* Copyright (C) 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.ant;
/**
* A enum which stores static data about ANT sensors.
*
* @author Matthew Simmons
*/
public enum AntSensor {
SENSOR_HEART_RATE (Constants.ANT_DEVICE_TYPE_HRM),
SENSOR_CADENCE (Constants.ANT_DEVICE_TYPE_CADENCE),
SENSOR_SPEED (Constants.ANT_DEVICE_TYPE_SPEED),
SENSOR_POWER (Constants.ANT_DEVICE_TYPE_POWER);
private static class Constants {
public static byte ANT_DEVICE_TYPE_POWER = 11;
public static byte ANT_DEVICE_TYPE_HRM = 120;
public static byte ANT_DEVICE_TYPE_CADENCE = 122;
public static byte ANT_DEVICE_TYPE_SPEED = 123;
};
private final byte deviceType;
private AntSensor(byte deviceType) {
this.deviceType = deviceType;
}
public byte getDeviceType() {
return deviceType;
}
}
@@ -13,18 +13,18 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package com.google.android.apps.mytracks.services.sensors; package com.google.android.apps.mytracks.services.sensors.ant;
import com.google.android.apps.mytracks.Constants; import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.services.sensors.ant.AntStartupMessage;
import com.dsi.ant.AntDefine; import com.dsi.ant.AntDefine;
import com.dsi.ant.AntInterface; import com.dsi.ant.AntInterface;
import com.dsi.ant.AntInterfaceIntent; import com.dsi.ant.AntInterfaceIntent;
import com.dsi.ant.AntMesg; import com.dsi.ant.AntMesg;
import com.dsi.ant.exception.AntInterfaceException; import com.dsi.ant.exception.AntInterfaceException;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.services.sensors.SensorManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@@ -84,7 +84,7 @@ public abstract class AntSensorManager extends SensorManager {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String antAction = intent.getAction(); String antAction = intent.getAction();
Log.i(Constants.TAG, "enter status onReceive" + antAction); Log.i(TAG, "enter status onReceive" + antAction);
} }
}; };
@@ -93,12 +93,12 @@ public abstract class AntSensorManager extends SensorManager {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String antAction = intent.getAction(); String antAction = intent.getAction();
Log.i(Constants.TAG, "enter data onReceive" + antAction); Log.i(TAG, "enter data onReceive" + antAction);
if (antAction.equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) { if (antAction.equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) {
byte[] antMessage = intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE); byte[] antMessage = intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE);
if (DEBUGGING) { if (DEBUGGING) {
Log.d(Constants.TAG, "Received RX message " + messageToString(antMessage)); Log.d(TAG, "Received RX message " + messageToString(antMessage));
} }
handleMessage(antMessage); handleMessage(antMessage);
@@ -119,7 +119,7 @@ public abstract class AntSensorManager extends SensorManager {
@Override @Override
public void onServiceDisconnected() { public void onServiceDisconnected() {
Log.d(Constants.TAG, "ANT interface reports disconnection"); Log.d(TAG, "ANT interface reports disconnection");
} }
}; };
@@ -129,14 +129,14 @@ public abstract class AntSensorManager extends SensorManager {
@Override @Override
public void onDestroy() { public void onDestroy() {
Log.i(Constants.TAG, "destroying AntSensorManager"); Log.i(TAG, "destroying AntSensorManager");
context.unregisterReceiver(statusReceiver); context.unregisterReceiver(statusReceiver);
context.unregisterReceiver(dataReceiver); context.unregisterReceiver(dataReceiver);
try { try {
antReceiver.releaseInterface(); antReceiver.releaseInterface();
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
Log.e(Constants.TAG, "failed to release ANT interface", e); Log.e(TAG, "failed to release ANT interface", e);
} }
antReceiver.destroy(); antReceiver.destroy();
@@ -177,7 +177,7 @@ public abstract class AntSensorManager extends SensorManager {
antReceiver = AntInterface.getInstance(context, antServiceListener); antReceiver = AntInterface.getInstance(context, antServiceListener);
if (antReceiver == null) { if (antReceiver == null) {
Log.e(Constants.TAG, "Failed to get ANT Receiver"); Log.e(TAG, "Failed to get ANT Receiver");
return; return;
} }
@@ -190,11 +190,11 @@ public abstract class AntSensorManager extends SensorManager {
* to initiate any ANT commands. * to initiate any ANT commands.
*/ */
private synchronized void serviceConnected() { private synchronized void serviceConnected() {
Log.d(Constants.TAG, "ANT service connected"); Log.d(TAG, "ANT service connected");
try { try {
if (!antReceiver.claimInterface()) { if (!antReceiver.claimInterface()) {
Log.e(Constants.TAG, "failed to claim ANT interface"); Log.e(TAG, "failed to claim ANT interface");
return; return;
} }
@@ -202,14 +202,14 @@ public abstract class AntSensorManager extends SensorManager {
// Make sure not to call AntInterface.enable() again, if it has been // Make sure not to call AntInterface.enable() again, if it has been
// already called before // already called before
if (antInterrupted == false) { if (antInterrupted == false) {
Log.i(Constants.TAG, "Powering on Radio"); Log.i(TAG, "Powering on Radio");
antReceiver.enable(); antReceiver.enable();
} }
} else { } else {
Log.i(Constants.TAG, "Radio already enabled"); Log.i(TAG, "Radio already enabled");
} }
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
Log.e(Constants.TAG, "failed to enable ANT", e); Log.e(TAG, "failed to enable ANT", e);
} }
try { try {
@@ -220,7 +220,7 @@ public abstract class AntSensorManager extends SensorManager {
// that message. // that message.
antReceiver.ANTResetSystem(); antReceiver.ANTResetSystem();
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
Log.e(Constants.TAG, "failed to reset ANT (expected exception)", e); Log.e(TAG, "failed to reset ANT (expected exception)", e);
} }
} }
@@ -232,7 +232,7 @@ public abstract class AntSensorManager extends SensorManager {
protected void handleMessage(byte[] antMessage) { protected void handleMessage(byte[] antMessage) {
int len = antMessage[0]; int len = antMessage[0];
if (len != antMessage.length - 2 || antMessage.length <= 2) { if (len != antMessage.length - 2 || antMessage.length <= 2) {
Log.e(Constants.TAG, "Invalid message: " + messageToString(antMessage)); Log.e(TAG, "Invalid message: " + messageToString(antMessage));
return; return;
} }
@@ -253,7 +253,7 @@ public abstract class AntSensorManager extends SensorManager {
*/ */
protected boolean handleMessage(byte messageId, byte[] messageData) { protected boolean handleMessage(byte messageId, byte[] messageData) {
if (messageId == AntMesg.MESG_STARTUP_MESG_ID) { if (messageId == AntMesg.MESG_STARTUP_MESG_ID) {
Log.d(Constants.TAG, String.format( Log.d(TAG, String.format(
"Received startup message (reason %02x); initializing channel", "Received startup message (reason %02x); initializing channel",
new AntStartupMessage(messageData).getMessage())); new AntStartupMessage(messageData).getMessage()));
setupAntSensorChannels(); setupAntSensorChannels();
@@ -305,13 +305,13 @@ public abstract class AntSensorManager extends SensorManager {
return true; return true;
} catch (AntInterfaceException e) { } catch (AntInterfaceException e) {
Log.e(Constants.TAG, "failed to setup ANT channel", e); Log.e(TAG, "failed to setup ANT channel", e);
return false; return false;
} }
} }
private void registerForAntIntents() { private void registerForAntIntents() {
Log.i(Constants.TAG, "Registering for ant intents."); Log.i(TAG, "Registering for ant intents.");
// Register for ANT intent broadcasts. // Register for ANT intent broadcasts.
IntentFilter statusIntentFilter = new IntentFilter(); IntentFilter statusIntentFilter = new IntentFilter();
statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION); statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION);
@@ -51,10 +51,6 @@ public class Track implements Parcelable {
track.stats = source.readParcelable(classLoader); track.stats = source.readParcelable(classLoader);
track.numberOfPoints = source.readInt(); track.numberOfPoints = source.readInt();
for (int i = 0; i < track.numberOfPoints; ++i) {
Location loc = source.readParcelable(classLoader);
track.locations.add(loc);
}
track.tableId = source.readString(); track.tableId = source.readString();
return track; return track;
@@ -103,9 +99,6 @@ public class Track implements Parcelable {
dest.writeParcelable(stats, 0); dest.writeParcelable(stats, 0);
dest.writeInt(numberOfPoints); dest.writeInt(numberOfPoints);
for (int i = 0; i < numberOfPoints; ++i) {
dest.writeParcelable(locations.get(i), 0);
}
dest.writeString(tableId); dest.writeString(tableId);
} }
@@ -59,6 +59,11 @@ public class MockTrackWriter implements TrackWriter {
throw new UnsupportedOperationException("not implemented"); throw new UnsupportedOperationException("not implemented");
} }
@Override
public void stopWriteTrack() {
throw new UnsupportedOperationException("not implemented");
}
@Override @Override
public boolean wasSuccess() { public boolean wasSuccess() {
return false; return false;
@@ -5,18 +5,9 @@ import com.google.android.apps.mytracks.content.MyTracksLocation;
import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.file.TrackFormatWriter;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -29,6 +20,14 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/** /**
* Base class for track format writer tests, which sets up a fake track and * Base class for track format writer tests, which sets up a fake track and
* gives auxiliary methods for verifying XML output. * gives auxiliary methods for verifying XML output.
@@ -105,9 +104,9 @@ public abstract class TrackFormatWriterTest extends AndroidTestCase {
* track in {@link #track}. * track in {@link #track}.
* *
* @param writer the writer to write to * @param writer the writer to write to
* @return the written contents * @return the written contents
*/ */
protected String writeTrack(TrackFormatWriter writer) { protected String writeTrack(TrackFormatWriter writer) throws Exception {
OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE); OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE);
writer.prepare(track, output); writer.prepare(track, output);
writer.writeHeader(); writer.writeHeader();
@@ -9,10 +9,9 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory; import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.file.TrackFormatWriter;
import com.google.android.apps.mytracks.io.file.TrackWriterImpl;
import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext; import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext;
import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory; import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
import android.location.Location; import android.location.Location;
@@ -25,6 +24,7 @@ import java.io.FileNotFoundException;
import java.io.OutputStream; import java.io.OutputStream;
import org.easymock.EasyMock; import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.easymock.IArgumentMatcher; import org.easymock.IArgumentMatcher;
import org.easymock.IMocksControl; import org.easymock.IMocksControl;
@@ -180,6 +180,43 @@ public class TrackWriterTest extends AndroidTestCase {
mocksControl.verify(); mocksControl.verify();
} }
public void testWriteTrack_cancelled() throws Exception {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
writer = new OpenFileTrackWriter(
getContext(), providerUtils, track, formatWriter, stream, true);
formatWriter.prepare(track, stream);
final Location[] locs = {
new Location("fake0"),
new Location("fake1"),
};
fillLocations(locs);
assertEquals(locs.length, providerUtils.bulkInsertTrackPoints(locs, locs.length, TRACK_ID));
formatWriter.writeHeader();
formatWriter.writeBeginTrack(locEq(locs[0]));
formatWriter.writeOpenSegment();
formatWriter.writeLocation(locEq(locs[0]));
//EasyMock.expectLastCall().andThrow(new InterruptedException());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
throw new InterruptedException();
}
});
mocksControl.replay();
writer.writeTrack();
mocksControl.verify();
assertFalse(writer.wasSuccess());
assertEquals(R.string.error_operation_cancelled, writer.getErrorMessage());
}
public void testWriteTrack_openFails() { public void testWriteTrack_openFails() {
writer = new WriteTracksTrackWriter(getContext(), providerUtils, track, writer = new WriteTracksTrackWriter(getContext(), providerUtils, track,
formatWriter, false); formatWriter, false);
@@ -229,7 +266,7 @@ public class TrackWriterTest extends AndroidTestCase {
mocksControl.verify(); mocksControl.verify();
} }
public void testWriteDocument_emptyTrack() { public void testWriteDocument_emptyTrack() throws Exception {
writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter); writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter);
// Set expected mock behavior // Set expected mock behavior
@@ -244,7 +281,7 @@ public class TrackWriterTest extends AndroidTestCase {
mocksControl.verify(); mocksControl.verify();
} }
public void testWriteDocument() { public void testWriteDocument() throws Exception {
writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter); writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter);
final Location[] locs = { final Location[] locs = {
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.services.sensors;
import com.dsi.ant.AntMesg; import com.dsi.ant.AntMesg;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.services.sensors.ant.AntDirectSensorManager;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@@ -15,6 +15,8 @@
*/ */
package com.google.android.apps.mytracks.services.sensors; package com.google.android.apps.mytracks.services.sensors;
import com.google.android.apps.mytracks.services.sensors.ant.AntSensorManager;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.MoreAsserts; import android.test.MoreAsserts;
@@ -1,6 +1,8 @@
package com.google.android.apps.mytracks.services.sensors; package com.google.android.apps.mytracks.services.sensors;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.services.sensors.ant.AntDirectSensorManager;
import com.google.android.apps.mytracks.services.sensors.ant.AntSRMSensorManager;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.content.SharedPreferences; import android.content.SharedPreferences;