forked from upstream-mirrors/OpenTracks
Upgrade ANT support libraries
Rework AntSensorManager (and subclasses) to use asynchronous ANT interfaces Encapsulate message byte arrays with per-message classes Add tests
This commit is contained in:
@@ -28,14 +28,6 @@ package com.dsi.ant;
|
|||||||
*/
|
*/
|
||||||
public class AntDefine {
|
public class AntDefine {
|
||||||
|
|
||||||
//////////////////////////////////////////////
|
|
||||||
// Library Version Information
|
|
||||||
//////////////////////////////////////////////
|
|
||||||
public static final int ANT_LIBRARY_VERSION_CODE = 5;
|
|
||||||
public static final int ANT_LIBRARY_VERSION_MAJOR = 1;
|
|
||||||
public static final int ANT_LIBRARY_VERSION_MINOR = 4;
|
|
||||||
public static final String ANT_LIBRARY_VERSION_NAME = String.valueOf(ANT_LIBRARY_VERSION_MAJOR) + "." + String.valueOf(ANT_LIBRARY_VERSION_MINOR);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
// Valid Configuration Values
|
// Valid Configuration Values
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -22,13 +22,16 @@ package com.dsi.ant;
|
|||||||
*/
|
*/
|
||||||
public interface AntInterfaceIntent {
|
public interface AntInterfaceIntent {
|
||||||
|
|
||||||
public static final String STATUS = "com.dsi.ant.rx.intent.STATUS";
|
public static final String STATUS = "com.dsi.ant.rx.intent.STATUS";
|
||||||
public static final String ANT_MESSAGE = "com.dsi.ant.intent.ANT_MESSAGE";
|
public static final String ANT_MESSAGE = "com.dsi.ant.intent.ANT_MESSAGE";
|
||||||
|
public static final String ANT_INTERFACE_CLAIMED_PID = "com.dsi.ant.intent.ANT_INTERFACE_CLAIMED_PID";
|
||||||
|
|
||||||
public static final String ANT_ENABLED_ACTION = "com.dsi.ant.intent.action.ANT_ENABLED";
|
public static final String ANT_ENABLED_ACTION = "com.dsi.ant.intent.action.ANT_ENABLED";
|
||||||
public static final String ANT_DISABLED_ACTION = "com.dsi.ant.intent.action.ANT_DISABLED";
|
public static final String ANT_DISABLED_ACTION = "com.dsi.ant.intent.action.ANT_DISABLED";
|
||||||
public static final String ANT_RESET_ACTION = "com.dsi.ant.intent.action.ANT_RESET";
|
public static final String ANT_RESET_ACTION = "com.dsi.ant.intent.action.ANT_RESET";
|
||||||
|
|
||||||
|
public static final String ANT_INTERFACE_CLAIMED_ACTION = "com.dsi.ant.intent.action.ANT_INTERFACE_CLAIMED_ACTION";
|
||||||
|
|
||||||
public static final String ANT_RX_MESSAGE_ACTION = "com.dsi.ant.intent.action.ANT_RX_MESSAGE_ACTION";
|
public static final String ANT_RX_MESSAGE_ACTION = "com.dsi.ant.intent.action.ANT_RX_MESSAGE_ACTION";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Dynastream Innovations 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.dsi.ant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Android Ant API is not finalized, and *will* be updated and expanded.
|
||||||
|
* This is the base level ANT messaging API and gives any application full
|
||||||
|
* control over the ANT radio HW. Caution should be exercised when using
|
||||||
|
* this interface.
|
||||||
|
*
|
||||||
|
* Public API for controlling the Ant Service.
|
||||||
|
*
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
interface IAnt_6 {
|
||||||
|
// Since version 1 (1.0):
|
||||||
|
boolean enable();
|
||||||
|
boolean disable();
|
||||||
|
boolean isEnabled();
|
||||||
|
|
||||||
|
boolean ANTTxMessage(in byte[] message);
|
||||||
|
|
||||||
|
boolean ANTResetSystem();
|
||||||
|
boolean ANTUnassignChannel(byte channelNumber);
|
||||||
|
boolean ANTAssignChannel(byte channelNumber, byte channelType, byte networkNumber);
|
||||||
|
boolean ANTSetChannelId(byte channelNumber, int deviceNumber, byte deviceType, byte txType);
|
||||||
|
boolean ANTSetChannelPeriod(byte channelNumber, int channelPeriod);
|
||||||
|
boolean ANTSetChannelRFFreq(byte channelNumber, byte radioFrequency);
|
||||||
|
boolean ANTSetChannelSearchTimeout(byte channelNumber, byte searchTimeout);
|
||||||
|
boolean ANTSetLowPriorityChannelSearchTimeout(byte channelNumber, byte searchTimeout);
|
||||||
|
boolean ANTSetProximitySearch(byte channelNumber, byte searchThreshold);
|
||||||
|
boolean ANTSetChannelTxPower(byte channelNumber, byte txPower);
|
||||||
|
boolean ANTAddChannelId(byte channelNumber, int deviceNumber, byte deviceType, byte txType, byte listIndex);
|
||||||
|
boolean ANTConfigList(byte channelNumber, byte listSize, byte exclude);
|
||||||
|
|
||||||
|
boolean ANTOpenChannel(byte channelNumber);
|
||||||
|
boolean ANTCloseChannel(byte channelNumber);
|
||||||
|
|
||||||
|
boolean ANTRequestMessage(byte channelNumber, byte messageID);
|
||||||
|
boolean ANTSendBroadcastData(byte channelNumber, in byte[] txBuffer);
|
||||||
|
boolean ANTSendAcknowledgedData(byte channelNumber, in byte[] txBuffer);
|
||||||
|
|
||||||
|
boolean ANTSendBurstTransferPacket(byte control, in byte[] txBuffer);
|
||||||
|
|
||||||
|
int ANTSendBurstTransfer(byte channelNumber, in byte[] txBuffer);
|
||||||
|
int ANTTransmitBurst(byte channelNumber, in byte[] txBuffer, int initialPacket, boolean containsEndOfBurst);
|
||||||
|
|
||||||
|
// Since version 4 (1.3):
|
||||||
|
boolean ANTConfigEventBuffering(int screenOnFlushTimerInterval, int screenOnFlushBufferThreshold, int screenOffFlushTimerInterval, int screenOffFlushBufferThreshold);
|
||||||
|
|
||||||
|
// Since version 2 (1.1):
|
||||||
|
boolean ANTDisableEventBuffering();
|
||||||
|
|
||||||
|
// Since version 3 (1.2):
|
||||||
|
int getServiceLibraryVersionCode();
|
||||||
|
String getServiceLibraryVersionName();
|
||||||
|
|
||||||
|
// Since version 6 (1.5):
|
||||||
|
boolean claimInterface();
|
||||||
|
boolean requestForceClaimInterface(String appName);
|
||||||
|
boolean stopRequestForceClaimInterface();
|
||||||
|
boolean releaseInterface();
|
||||||
|
boolean hasClaimedInterface();
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Dynastream Innovations 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.dsi.ant;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines version numbers
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class Version {
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// Library Version Information
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
public static final int ANT_LIBRARY_VERSION_CODE = 6;
|
||||||
|
public static final int ANT_LIBRARY_VERSION_MAJOR = 2;
|
||||||
|
public static final int ANT_LIBRARY_VERSION_MINOR = 0;
|
||||||
|
public static final String ANT_LIBRARY_VERSION_NAME = String.valueOf(ANT_LIBRARY_VERSION_MAJOR) + "." + String.valueOf(ANT_LIBRARY_VERSION_MINOR);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.dsi.ant.exception;
|
||||||
|
|
||||||
|
public class AntInterfaceException extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -7278855366167722274L;
|
||||||
|
|
||||||
|
public AntInterfaceException()
|
||||||
|
{
|
||||||
|
this("Unknown ANT Interface error");
|
||||||
|
}
|
||||||
|
|
||||||
|
public AntInterfaceException(String string)
|
||||||
|
{
|
||||||
|
super(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.dsi.ant.exception;
|
||||||
|
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
public class AntRemoteException extends AntInterfaceException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 8950974759973459561L;
|
||||||
|
|
||||||
|
public AntRemoteException(RemoteException e)
|
||||||
|
{
|
||||||
|
this("ANT Interface error, ANT Radio Service remote error: "+e.toString(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AntRemoteException(String string, RemoteException e)
|
||||||
|
{
|
||||||
|
super(string);
|
||||||
|
|
||||||
|
this.initCause(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.dsi.ant.exception;
|
||||||
|
|
||||||
|
public class AntServiceNotConnectedException extends AntInterfaceException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -2085081032170129309L;
|
||||||
|
|
||||||
|
public AntServiceNotConnectedException()
|
||||||
|
{
|
||||||
|
this("ANT Interface error, ANT Radio Service not connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public AntServiceNotConnectedException(String string)
|
||||||
|
{
|
||||||
|
super(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
-44
@@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 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
|
||||||
@@ -18,10 +18,14 @@ package com.google.android.apps.mytracks.services.sensors;
|
|||||||
import static com.google.android.apps.mytracks.MyTracksConstants.TAG;
|
import static com.google.android.apps.mytracks.MyTracksConstants.TAG;
|
||||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||||
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;
|
||||||
import com.dsi.ant.AntMesg;
|
import com.dsi.ant.AntMesg;
|
||||||
|
import com.dsi.ant.exception.AntInterfaceException;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -49,62 +53,70 @@ public class AntDirectSensorManager extends AntSensorManager {
|
|||||||
|
|
||||||
public AntDirectSensorManager(Context context) {
|
public AntDirectSensorManager(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
deviceNumberHRM = WILDCARD;
|
deviceNumberHRM = WILDCARD;
|
||||||
|
|
||||||
// First read the the device id that we will be pairing with.
|
// First read the the device id that we will be pairing with.
|
||||||
SharedPreferences prefs = context.getSharedPreferences(
|
SharedPreferences prefs = context.getSharedPreferences(
|
||||||
MyTracksSettings.SETTINGS_NAME, 0);
|
MyTracksSettings.SETTINGS_NAME, 0);
|
||||||
if (prefs != null) {
|
if (prefs != null) {
|
||||||
deviceNumberHRM =
|
deviceNumberHRM =
|
||||||
(short) prefs.getInt(context.getString(R.string.ant_heart_rate_sensor_id_key), 0);
|
(short) prefs.getInt(context.getString(R.string.ant_heart_rate_sensor_id_key), 0);
|
||||||
}
|
}
|
||||||
Log.i(TAG, "Pairing with heart rate monitor: " + deviceNumberHRM);
|
Log.i(TAG, "Will pair with heart rate monitor: " + deviceNumberHRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(byte[] antMessage) {
|
protected boolean handleMessage(byte messageId, byte[] messageData) {
|
||||||
int channel =
|
if (super.handleMessage(messageId, messageData)) {
|
||||||
antMessage[AntMesg.MESG_DATA_OFFSET] & AntDefine.CHANNEL_NUMBER_MASK;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int channel = messageData[0] & AntDefine.CHANNEL_NUMBER_MASK;
|
||||||
switch (channel) {
|
switch (channel) {
|
||||||
case HRM_CHANNEL:
|
case HRM_CHANNEL:
|
||||||
antDecodeHRM(antMessage);
|
antDecodeHRM(messageId, messageData);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.d(TAG, "Unhandled message: " + channel);
|
Log.d(TAG, "Unhandled message: " + channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode an ant heart rate monitor message.
|
* Decode an ant heart rate monitor message.
|
||||||
* @param antMessage The byte array received from the heart rate monitor.
|
* @param messageData The byte array received from the heart rate monitor.
|
||||||
*/
|
*/
|
||||||
private void antDecodeHRM(byte[] antMessage) {
|
private void antDecodeHRM(int messageId, byte[] messageData) {
|
||||||
// message ID == broadcast
|
switch (messageId) {
|
||||||
switch (antMessage[AntMesg.MESG_ID_OFFSET]) {
|
|
||||||
case AntMesg.MESG_BROADCAST_DATA_ID:
|
case AntMesg.MESG_BROADCAST_DATA_ID:
|
||||||
handleBroadcastData(antMessage);
|
handleBroadcastData(messageData);
|
||||||
break;
|
break;
|
||||||
case AntMesg.MESG_RESPONSE_EVENT_ID:
|
case AntMesg.MESG_RESPONSE_EVENT_ID:
|
||||||
handleMessageResponse(antMessage);
|
handleMessageResponse(messageData);
|
||||||
break;
|
break;
|
||||||
case AntMesg.MESG_CHANNEL_ID_ID:
|
case AntMesg.MESG_CHANNEL_ID_ID:
|
||||||
handleChannelId(antMessage);
|
handleChannelId(messageData);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Unexpected message id: " + antMessage[3]);
|
Log.e(TAG, "Unexpected message id: " + messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBroadcastData(byte[] antMessage) {
|
private void handleBroadcastData(byte[] antMessage) {
|
||||||
if (deviceNumberHRM == WILDCARD) {
|
if (deviceNumberHRM == WILDCARD) {
|
||||||
getAntReceiver().ANTRequestMessage(HRM_CHANNEL,
|
try {
|
||||||
AntMesg.MESG_CHANNEL_ID_ID);
|
getAntReceiver().ANTRequestMessage(HRM_CHANNEL,
|
||||||
|
AntMesg.MESG_CHANNEL_ID_ID);
|
||||||
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(TAG, "ANT error handling broadcast data", e);
|
||||||
|
}
|
||||||
Log.d(TAG, "Requesting channel id id.");
|
Log.d(TAG, "Requesting channel id id.");
|
||||||
}
|
}
|
||||||
|
|
||||||
setSensorState(Sensor.SensorState.CONNECTED);
|
setSensorState(Sensor.SensorState.CONNECTED);
|
||||||
int bpm = (int) antMessage[10] & 0xFF;
|
int bpm = (int) antMessage[8] & 0xFF;
|
||||||
Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder()
|
Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder()
|
||||||
.setValue(bpm)
|
.setValue(bpm)
|
||||||
.setState(Sensor.SensorState.SENDING);
|
.setState(Sensor.SensorState.SENDING);
|
||||||
@@ -115,11 +127,9 @@ public class AntDirectSensorManager extends AntSensorManager {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleChannelId(byte[] antMessage) {
|
void handleChannelId(byte[] rawMessage) {
|
||||||
// Store the device id.
|
AntChannelIdMessage message = new AntChannelIdMessage(rawMessage);
|
||||||
deviceNumberHRM =
|
deviceNumberHRM = message.getDeviceNumber();
|
||||||
(short) (((int) antMessage[3] & 0xFF |
|
|
||||||
((int) (antMessage[4] & 0xFF) << 8)) & 0xFFFF);
|
|
||||||
Log.i(TAG, "Found device id: " + deviceNumberHRM);
|
Log.i(TAG, "Found device id: " + deviceNumberHRM);
|
||||||
|
|
||||||
SharedPreferences prefs = context.getSharedPreferences(
|
SharedPreferences prefs = context.getSharedPreferences(
|
||||||
@@ -129,26 +139,31 @@ public class AntDirectSensorManager extends AntSensorManager {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMessageResponse(byte[] antMessage) {
|
private void handleMessageResponse(byte[] rawMessage) {
|
||||||
if (antMessage[3] == AntMesg.MESG_EVENT_ID &&
|
AntChannelResponseMessage message = new AntChannelResponseMessage(rawMessage);
|
||||||
antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) {
|
switch (message.getMessageId()) {
|
||||||
// Search timeout
|
case AntMesg.MESG_EVENT_ID:
|
||||||
Log.w(TAG, "Search timed out. Unassigning channel.");
|
if (message.getMessageCode() == AntDefine.EVENT_RX_SEARCH_TIMEOUT) {
|
||||||
getAntReceiver().ANTUnassignChannel((byte) 0);
|
// Search timeout
|
||||||
setSensorState(Sensor.SensorState.DISCONNECTED);
|
Log.w(TAG, "Search timed out. Unassigning channel.");
|
||||||
} else if (antMessage[3] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) {
|
try {
|
||||||
setSensorState(Sensor.SensorState.DISCONNECTED);
|
getAntReceiver().ANTUnassignChannel((byte) 0);
|
||||||
Log.i(TAG, "Disconnected from the sensor: " + getSensorState());
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(TAG, "ANT error unassigning channel", e);
|
||||||
|
}
|
||||||
|
setSensorState(Sensor.SensorState.DISCONNECTED);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AntMesg.MESG_UNASSIGN_CHANNEL_ID:
|
||||||
|
setSensorState(Sensor.SensorState.DISCONNECTED);
|
||||||
|
Log.i(TAG, "Disconnected from the sensor: " + getSensorState());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override protected void setupAntSensorChannels() {
|
||||||
* Open a channel to the SRM head unit.
|
setupAntSensorChannel(NETWORK_NUMBER,
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setupChannel() {
|
|
||||||
super.setupChannel();
|
|
||||||
antChannelSetup(NETWORK_NUMBER,
|
|
||||||
HRM_CHANNEL,
|
HRM_CHANNEL,
|
||||||
deviceNumberHRM,
|
deviceNumberHRM,
|
||||||
HEART_RATE_DEVICE_TYPE,
|
HEART_RATE_DEVICE_TYPE,
|
||||||
@@ -156,7 +171,6 @@ public class AntDirectSensorManager extends AntSensorManager {
|
|||||||
CHANNEL_PERIOD,
|
CHANNEL_PERIOD,
|
||||||
RF_FREQUENCY,
|
RF_FREQUENCY,
|
||||||
(byte) 0);
|
(byte) 0);
|
||||||
setSensorState(Sensor.SensorState.CONNECTING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getDeviceNumberHRM() {
|
public short getDeviceNumberHRM() {
|
||||||
|
|||||||
+19
-13
@@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 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
|
||||||
@@ -22,6 +22,8 @@ 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 android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -104,8 +106,12 @@ public class AntSRMSensorManager extends AntSensorManager {
|
|||||||
|
|
||||||
private void handleBroadcastData(byte[] antMessage) {
|
private void handleBroadcastData(byte[] antMessage) {
|
||||||
if (deviceId == WILDCARD) {
|
if (deviceId == WILDCARD) {
|
||||||
getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID);
|
try {
|
||||||
Log.d(MyTracksConstants.TAG, "Requesting channel id id.");
|
Log.d(MyTracksConstants.TAG, "Requesting channel id id.");
|
||||||
|
getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID);
|
||||||
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "Failed to request channel id id", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setSensorState(Sensor.SensorState.CONNECTED);
|
setSensorState(Sensor.SensorState.CONNECTED);
|
||||||
switch(antMessage[MESSAGE_TYPE_INDEX]) {
|
switch(antMessage[MESSAGE_TYPE_INDEX]) {
|
||||||
@@ -138,7 +144,11 @@ public class AntSRMSensorManager extends AntSensorManager {
|
|||||||
&& antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) {
|
&& antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) {
|
||||||
// Search timeout
|
// Search timeout
|
||||||
Log.w(MyTracksConstants.TAG, "Search timed out. Unassigning channel.");
|
Log.w(MyTracksConstants.TAG, "Search timed out. Unassigning channel.");
|
||||||
getAntReceiver().ANTUnassignChannel(channel);
|
try {
|
||||||
|
getAntReceiver().ANTUnassignChannel(channel);
|
||||||
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(MyTracksConstants.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);
|
||||||
@@ -207,12 +217,10 @@ public class AntSRMSensorManager extends AntSensorManager {
|
|||||||
sensorData = builder.build();
|
sensorData = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a channel to the SRM head unit.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setupChannel() {
|
protected void setupAntSensorChannels() {
|
||||||
antChannelSetup(NETWORK_NUMBER,
|
lastMessageId = 0;
|
||||||
|
setupAntSensorChannel(NETWORK_NUMBER,
|
||||||
channel,
|
channel,
|
||||||
deviceId,
|
deviceId,
|
||||||
DEVICE_TYPE,
|
DEVICE_TYPE,
|
||||||
@@ -220,7 +228,5 @@ public class AntSRMSensorManager extends AntSensorManager {
|
|||||||
CHANNEL_PERIOD,
|
CHANNEL_PERIOD,
|
||||||
RF_FREQUENCY,
|
RF_FREQUENCY,
|
||||||
(byte) 0);
|
(byte) 0);
|
||||||
lastMessageId = 0;
|
|
||||||
setSensorState(Sensor.SensorState.CONNECTING);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+246
-139
@@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 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
|
||||||
@@ -16,37 +16,56 @@
|
|||||||
package com.google.android.apps.mytracks.services.sensors;
|
package com.google.android.apps.mytracks.services.sensors;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||||
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
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.exception.AntInterfaceException;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class for ANT+ based sensors.
|
* This is the common superclass for ANT-based sensors. It handles tasks which
|
||||||
|
* apply to the ANT framework as a whole, such as framework initialization and
|
||||||
|
* destruction. Subclasses are expected to handle the initialization and
|
||||||
|
* management of individual sensors.
|
||||||
|
*
|
||||||
|
* Implementation:
|
||||||
|
*
|
||||||
|
* The initialization process is somewhat complicated. This is due in part to
|
||||||
|
* the asynchronous nature of ANT Radio Service initialization, and in part due
|
||||||
|
* to an apparent bug in that service. The following is an overview of the
|
||||||
|
* initialization process.
|
||||||
|
*
|
||||||
|
* Initialization begins in {@link #setupChannel}, which is invoked by the
|
||||||
|
* {@link SensorManager} when track recording begins. {@link #setupChannel}
|
||||||
|
* asks the ANT Radio Service (via {@link AntInterface}) to start, using a
|
||||||
|
* {@link AntInterface.ServiceListener} to indicate when the service has
|
||||||
|
* connected. {@link #serviceConnected} claims and enables the Radio Service,
|
||||||
|
* and then resets it to a known state for our use. Completion of reset is
|
||||||
|
* indicated by receipt of a startup message (see {@link AntStartupMessage}).
|
||||||
|
* Once we've received that message, the ANT service is ready for use, and we
|
||||||
|
* can start sensor-specific initialization using
|
||||||
|
* {@link #setupAntSensorChannels}. The initialization of each sensor will
|
||||||
|
* usually result in a call to {@link #setupAntSensorChannel}.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public abstract class AntSensorManager extends SensorManager {
|
public abstract class AntSensorManager extends SensorManager {
|
||||||
|
|
||||||
// ANT Service
|
|
||||||
public static final int EVENT_ANT_ENABLED = 1;
|
|
||||||
public static final int EVENT_ANT_DISABLED = 2;
|
|
||||||
|
|
||||||
// Pairing
|
// Pairing
|
||||||
protected static final short WILDCARD = 0;
|
protected static final short WILDCARD = 0;
|
||||||
|
|
||||||
private AntInterface antReceiver;
|
private AntInterface antReceiver;
|
||||||
|
|
||||||
private boolean status;
|
|
||||||
|
|
||||||
// Flag to know if the ANT App was interrupted
|
// Flag to know if the ANT App was interrupted
|
||||||
// TODO: This code path is not used but probably should be.
|
// TODO: This code path is not used but probably should be.
|
||||||
private boolean antInterrupted;
|
private boolean antInterrupted;
|
||||||
@@ -57,40 +76,29 @@ public abstract class AntSensorManager extends SensorManager {
|
|||||||
protected SensorDataSet sensorData;
|
protected SensorDataSet sensorData;
|
||||||
|
|
||||||
protected Context context;
|
protected Context context;
|
||||||
|
|
||||||
private static final boolean DEBUGGING = false;
|
private static final boolean DEBUGGING = false;
|
||||||
|
|
||||||
/**
|
/** Receives and logs all status ANT intents. */
|
||||||
* Receives all of the ANT intents and dispatches to the proper handler.
|
private final BroadcastReceiver statusReceiver = new BroadcastReceiver() {
|
||||||
*/
|
|
||||||
private final BroadcastReceiver receiver = new BroadcastReceiver() {
|
|
||||||
@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(MyTracksConstants.TAG, "enter status onReceive" + antAction);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Log.i(MyTracksConstants.TAG, "enter onReceive" + antAction);
|
/** Receives all data ANT intents. */
|
||||||
if (antAction.equals(AntInterfaceIntent.ANT_ENABLED_ACTION)) {
|
private final BroadcastReceiver dataReceiver = new BroadcastReceiver() {
|
||||||
Log.i(MyTracksConstants.TAG, "enter onReceive ANT_ENABLED_ACTION "
|
@Override
|
||||||
+ antAction);
|
public void onReceive(Context context, Intent intent) {
|
||||||
handler.sendMessage(handler.obtainMessage(EVENT_ANT_ENABLED, 0));
|
String antAction = intent.getAction();
|
||||||
}
|
Log.i(MyTracksConstants.TAG, "enter data onReceive" + antAction);
|
||||||
if (antAction.equals(AntInterfaceIntent.ANT_DISABLED_ACTION)) {
|
|
||||||
Log.i(MyTracksConstants.TAG, "enter onReceive ANT_DISABLED_ACTION "
|
|
||||||
+ antAction);
|
|
||||||
handler.sendMessage(handler.obtainMessage(EVENT_ANT_DISABLED, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (antAction.equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) {
|
if (antAction.equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) {
|
||||||
byte[] antMessage =
|
byte[] antMessage = intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE);
|
||||||
intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE);
|
|
||||||
if (DEBUGGING) {
|
if (DEBUGGING) {
|
||||||
StringBuilder sb =
|
Log.d(MyTracksConstants.TAG, "Received RX message " + messageToString(antMessage));
|
||||||
new StringBuilder("ANT_RX_MESSAGE_ACTION Rx:");
|
|
||||||
|
|
||||||
for (int i = 0; i < antMessage.length; i++)
|
|
||||||
sb.append(String.format("[%X]]", (antMessage[i] & 0xFF)));
|
|
||||||
|
|
||||||
Log.d(MyTracksConstants.TAG, sb.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessage(antMessage);
|
handleMessage(antMessage);
|
||||||
@@ -99,60 +107,39 @@ public abstract class AntSensorManager extends SensorManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for all the ANT related events. The events will be handled only
|
* ANT uses this listener to tell us when it has bound to the ANT Radio
|
||||||
* when we are in the ANT application main menu
|
* Service. We can't start sending ANT commands until we've been notified
|
||||||
**/
|
* (via this listener) that the Radio Service has connected.
|
||||||
private Handler handler = new Handler() {
|
*/
|
||||||
|
private AntInterface.ServiceListener antServiceListener = new AntInterface.ServiceListener() {
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void onServiceConnected() {
|
||||||
switch (msg.what) {
|
serviceConnected();
|
||||||
case EVENT_ANT_ENABLED:
|
}
|
||||||
Log.i(MyTracksConstants.TAG,
|
|
||||||
"enter handleMessage ----EVENT_ANT_ENABLED");
|
@Override
|
||||||
break;
|
public void onServiceDisconnected() {
|
||||||
case EVENT_ANT_DISABLED:
|
Log.d(MyTracksConstants.TAG, "ANT interface reports disconnection");
|
||||||
Log.i(MyTracksConstants.TAG,
|
|
||||||
"enter handleMessage ----EVENT_ANT_DISABLED");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public AntSensorManager(Context context) {
|
public AntSensorManager(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
// Need to enable the ANT if it is not enabled earlier
|
|
||||||
antReceiver = AntInterface.getInstance(context, null);
|
|
||||||
if (antReceiver == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = antReceiver.isEnabled();
|
|
||||||
if (!status) {
|
|
||||||
// Make sure not to call AntInterface.enable() again, if it has been
|
|
||||||
// already called before
|
|
||||||
if (antInterrupted == false) {
|
|
||||||
status = antReceiver.enable();
|
|
||||||
if (!status) {
|
|
||||||
Log.e(MyTracksConstants.TAG, "Can not enable ANT interface");
|
|
||||||
} else {
|
|
||||||
Log.i(MyTracksConstants.TAG, "Powering on Radio");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.i(MyTracksConstants.TAG, "Radio already enabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setupChannel() {
|
public void onDestroy() {
|
||||||
Log.i(MyTracksConstants.TAG, "Registering for ant intents.");
|
Log.i(MyTracksConstants.TAG, "destroying AntSensorManager");
|
||||||
// Register for ANT intent broadcasts.
|
context.unregisterReceiver(statusReceiver);
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
context.unregisterReceiver(dataReceiver);
|
||||||
intentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION);
|
|
||||||
intentFilter.addAction(AntInterfaceIntent.ANT_DISABLED_ACTION);
|
try {
|
||||||
intentFilter.addAction(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION);
|
antReceiver.releaseInterface();
|
||||||
context.registerReceiver(receiver, intentFilter);
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "failed to release ANT interface", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
antReceiver.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -165,64 +152,184 @@ public abstract class AntSensorManager extends SensorManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void handleMessage(byte[] antMessage);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
Log.i(MyTracksConstants.TAG, "Unregistering for ant intents.");
|
|
||||||
context.unregisterReceiver(receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up an ANT+ channel.
|
|
||||||
*
|
|
||||||
* @return True on success.
|
|
||||||
*/
|
|
||||||
protected boolean antChannelSetup(byte networkNumber, byte channelNumber,
|
|
||||||
short deviceNumber, byte deviceType, byte txType, short channelPeriod,
|
|
||||||
byte radioFreq, byte proxSearch) {
|
|
||||||
|
|
||||||
// Assign as slave channel on selected network (0 = public, 1 = ANT+, 2 =
|
|
||||||
// ANTFS)
|
|
||||||
if (!antReceiver.ANTAssignChannel(channelNumber,
|
|
||||||
AntDefine.PARAMETER_RX_NOT_TX, networkNumber)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!antReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType,
|
|
||||||
txType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!antReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!antReceiver.ANTSetChannelRFFreq(channelNumber, radioFreq)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable high priority search
|
|
||||||
if (!antReceiver.ANTSetChannelSearchTimeout(channelNumber, (byte) 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set search timeout to 30 seconds (low priority search))
|
|
||||||
if (!antReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, (byte) 12)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deviceNumber == WILDCARD) {
|
|
||||||
// Configure proximity search, if using wild card search
|
|
||||||
if (!antReceiver.ANTSetProximitySearch(channelNumber, proxSearch)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !antReceiver.ANTOpenChannel(channelNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AntInterface getAntReceiver() {
|
public AntInterface getAntReceiver() {
|
||||||
return antReceiver;
|
return antReceiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the interface used by the {@link SensorManager} to tell this
|
||||||
|
* class when to start. It handles initialization of the ANT framework,
|
||||||
|
* eventually resulting in sensor-specific initialization via
|
||||||
|
* {@link #setupAntSensorChannels}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected final void setupChannel() {
|
||||||
|
// We handle this unpleasantly because the UI should've checked for ANT
|
||||||
|
// support before it even instantiated this class.
|
||||||
|
if (!AntInterface.hasAntSupport(context)) {
|
||||||
|
throw new IllegalStateException("device does not have ANT support");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We register for ANT intents early because we want to have a record of
|
||||||
|
// the status intents in the log as we start up.
|
||||||
|
registerForAntIntents();
|
||||||
|
|
||||||
|
antReceiver = AntInterface.getInstance(context, antServiceListener);
|
||||||
|
|
||||||
|
if (antReceiver == null) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "Failed to get ANT Receiver");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSensorState(Sensor.SensorState.CONNECTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is invoked via the ServiceListener when we're connected to
|
||||||
|
* the ANT service. If we're just starting up, this is our first opportunity
|
||||||
|
* to initiate any ANT commands.
|
||||||
|
*/
|
||||||
|
private synchronized void serviceConnected() {
|
||||||
|
Log.d(MyTracksConstants.TAG, "ANT service connected");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!antReceiver.claimInterface()) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "failed to claim ANT interface");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!antReceiver.isEnabled()) {
|
||||||
|
// Make sure not to call AntInterface.enable() again, if it has been
|
||||||
|
// already called before
|
||||||
|
if (antInterrupted == false) {
|
||||||
|
Log.i(MyTracksConstants.TAG, "Powering on Radio");
|
||||||
|
antReceiver.enable();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.i(MyTracksConstants.TAG, "Radio already enabled");
|
||||||
|
}
|
||||||
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "failed to enable ANT", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// We expect this call to throw an exception due to a bug in the ANT
|
||||||
|
// Radio Service. It won't actually fail, though, as we'll get the
|
||||||
|
// startup message (see {@link AntStartupMessage}) one normally expects
|
||||||
|
// after a reset. Channel initialization can proceed once we receive
|
||||||
|
// that message.
|
||||||
|
antReceiver.ANTResetSystem();
|
||||||
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "failed to reset ANT (expected exception)", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a raw ANT message.
|
||||||
|
* @param antMessage the ANT message, including the size and message ID bytes
|
||||||
|
* @deprecated Use {@link #handleMessage(int, byte[])} instead.
|
||||||
|
*/
|
||||||
|
protected void handleMessage(byte[] antMessage) {
|
||||||
|
int len = antMessage[0];
|
||||||
|
if (len != antMessage.length - 2 || antMessage.length <= 2) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "Invalid message: " + messageToString(antMessage));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte messageId = antMessage[1];
|
||||||
|
// Arrays#copyOfRange doesn't exist??
|
||||||
|
byte[] messageData = new byte[antMessage.length - 2];
|
||||||
|
System.arraycopy(antMessage, 2, messageData, 0, antMessage.length - 2);
|
||||||
|
handleMessage(messageId, messageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a raw ANT message.
|
||||||
|
* @param messageId the message ID. See the ANT Message Protocol and Usage
|
||||||
|
* guide, section 9.3.
|
||||||
|
* @param messageData the ANT message, without the size and message ID bytes.
|
||||||
|
* @return true if this method has taken responsibility for the passed
|
||||||
|
* message; false otherwise.
|
||||||
|
*/
|
||||||
|
protected boolean handleMessage(byte messageId, byte[] messageData) {
|
||||||
|
if (messageId == AntMesg.MESG_STARTUP_MESG_ID) {
|
||||||
|
Log.d(MyTracksConstants.TAG, String.format(
|
||||||
|
"Received startup message (reason %02x); initializing channel",
|
||||||
|
new AntStartupMessage(messageData).getMessage()));
|
||||||
|
setupAntSensorChannels();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses define this method to perform sensor-specific initialization.
|
||||||
|
* When this method is called, the ANT framework has been enabled, and is
|
||||||
|
* ready for use.
|
||||||
|
*/
|
||||||
|
protected abstract void setupAntSensorChannels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by subclasses to set up an ANT channel for a single sensor. A given
|
||||||
|
* subclass may invoke this method multiple times if the subclass is
|
||||||
|
* responsible for more than one sensor.
|
||||||
|
*
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
protected boolean setupAntSensorChannel(byte networkNumber, byte channelNumber,
|
||||||
|
short deviceNumber, byte deviceType, byte txType, short channelPeriod,
|
||||||
|
byte radioFreq, byte proxSearch) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Assign as slave channel on selected network (0 = public, 1 = ANT+, 2 =
|
||||||
|
// ANTFS)
|
||||||
|
antReceiver.ANTAssignChannel(channelNumber, AntDefine.PARAMETER_RX_NOT_TX, networkNumber);
|
||||||
|
|
||||||
|
antReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType, txType);
|
||||||
|
antReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod);
|
||||||
|
antReceiver.ANTSetChannelRFFreq(channelNumber, radioFreq);
|
||||||
|
|
||||||
|
// Disable high priority search
|
||||||
|
antReceiver.ANTSetChannelSearchTimeout(channelNumber, (byte) 0);
|
||||||
|
|
||||||
|
// Set search timeout to 30 seconds (low priority search))
|
||||||
|
antReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, (byte) 12);
|
||||||
|
|
||||||
|
if (deviceNumber == WILDCARD) {
|
||||||
|
// Configure proximity search, if using wild card search
|
||||||
|
antReceiver.ANTSetProximitySearch(channelNumber, proxSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
antReceiver.ANTOpenChannel(channelNumber);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (AntInterfaceException e) {
|
||||||
|
Log.e(MyTracksConstants.TAG, "failed to setup ANT channel", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerForAntIntents() {
|
||||||
|
Log.i(MyTracksConstants.TAG, "Registering for ant intents.");
|
||||||
|
// Register for ANT intent broadcasts.
|
||||||
|
IntentFilter statusIntentFilter = new IntentFilter();
|
||||||
|
statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION);
|
||||||
|
statusIntentFilter.addAction(AntInterfaceIntent.ANT_DISABLED_ACTION);
|
||||||
|
statusIntentFilter.addAction(AntInterfaceIntent.ANT_INTERFACE_CLAIMED_ACTION);
|
||||||
|
statusIntentFilter.addAction(AntInterfaceIntent.ANT_RESET_ACTION);
|
||||||
|
context.registerReceiver(statusReceiver, statusIntentFilter);
|
||||||
|
|
||||||
|
IntentFilter dataIntentFilter = new IntentFilter();
|
||||||
|
dataIntentFilter.addAction(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION);
|
||||||
|
context.registerReceiver(dataReceiver, dataIntentFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String messageToString(byte[] message) {
|
||||||
|
StringBuilder out = new StringBuilder();
|
||||||
|
for (byte b : message) {
|
||||||
|
out.append(String.format("%s%02x", (out.length() == 0 ? "" : " "), b));
|
||||||
|
}
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public abstract class SensorManager {
|
|||||||
public static final int RETRY_PERIOD = 30000;
|
public static final int RETRY_PERIOD = 30000;
|
||||||
|
|
||||||
private Sensor.SensorState sensorState = Sensor.SensorState.NONE;
|
private Sensor.SensorState sensorState = Sensor.SensorState.NONE;
|
||||||
|
|
||||||
private long sensorStateTimestamp = 0;
|
private long sensorStateTimestamp = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,7 +89,11 @@ public abstract class SensorManager {
|
|||||||
timer.schedule(checkSensorManager, RETRY_PERIOD, RETRY_PERIOD);
|
timer.schedule(checkSensorManager, RETRY_PERIOD, RETRY_PERIOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void setupChannel();
|
/**
|
||||||
|
* This method is used to set up any necessary connections to underlying
|
||||||
|
* sensor hardware.
|
||||||
|
*/
|
||||||
|
protected abstract void setupChannel();
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
|
|||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class decodes and encapsulates an ANT Channel ID message.
|
||||||
|
* (ANT Message ID 0x51, Protocol & Usage guide v4.2 section 9.5.7.2)
|
||||||
|
*
|
||||||
|
* @author Matthew Simmons
|
||||||
|
*/
|
||||||
|
public class AntChannelIdMessage extends AntMessage {
|
||||||
|
private byte channelNumber;
|
||||||
|
private short deviceNumber;
|
||||||
|
private byte deviceTypeId;
|
||||||
|
private byte transmissionType;
|
||||||
|
|
||||||
|
public AntChannelIdMessage(byte[] messageData) {
|
||||||
|
channelNumber = messageData[0];
|
||||||
|
deviceNumber = decodeShort(messageData[1], messageData[2]);
|
||||||
|
deviceTypeId = messageData[3];
|
||||||
|
transmissionType = messageData[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the channel number */
|
||||||
|
public byte getChannelNumber() {
|
||||||
|
return channelNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the device number */
|
||||||
|
public short getDeviceNumber() {
|
||||||
|
return deviceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the device type */
|
||||||
|
public byte getDeviceTypeId() {
|
||||||
|
return deviceTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the transmission type */
|
||||||
|
public byte getTransmissionType() {
|
||||||
|
return transmissionType;
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class decodes and encapsulates an ANT Channel Response / Event message.
|
||||||
|
* (ANT Message ID 0x40, Protocol & Usage guide v4.2 section 9.5.6.1)
|
||||||
|
*
|
||||||
|
* @author Matthew Simmons
|
||||||
|
*/
|
||||||
|
public class AntChannelResponseMessage extends AntMessage {
|
||||||
|
private byte channelNumber;
|
||||||
|
private byte messageId;
|
||||||
|
private byte messageCode;
|
||||||
|
|
||||||
|
public AntChannelResponseMessage(byte[] messageData) {
|
||||||
|
channelNumber = messageData[0];
|
||||||
|
messageId = messageData[1];
|
||||||
|
messageCode = messageData[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the channel number */
|
||||||
|
public byte getChannelNumber() {
|
||||||
|
return channelNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the ID of the message being responded to */
|
||||||
|
public byte getMessageId() {
|
||||||
|
return messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the code for a specific response or event */
|
||||||
|
public byte getMessageCode() {
|
||||||
|
return messageCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a common superclass for ANT message subclasses.
|
||||||
|
*
|
||||||
|
* @author Matthew Simmons
|
||||||
|
*/
|
||||||
|
public class AntMessage {
|
||||||
|
protected AntMessage() {}
|
||||||
|
|
||||||
|
/** Build a short value from its constituent bytes */
|
||||||
|
protected static short decodeShort(byte b0, byte b1) {
|
||||||
|
short value = b0;
|
||||||
|
value |= ((short) b1) << 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class decodes and encapsulates an ANT Startup message.
|
||||||
|
* (ANT Message ID 0x6f, Protocol & Usage guide v4.2 section 9.5.3.1)
|
||||||
|
*
|
||||||
|
* @author Matthew Simmons
|
||||||
|
*/
|
||||||
|
public class AntStartupMessage extends AntMessage {
|
||||||
|
private byte message;
|
||||||
|
|
||||||
|
public AntStartupMessage(byte[] messageData) {
|
||||||
|
message = messageData[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the cause of the startup */
|
||||||
|
public byte getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-19
@@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 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
|
||||||
@@ -28,7 +28,7 @@ public class AntDirectSensorManagerTest extends AndroidTestCase {
|
|||||||
|
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
private AntDirectSensorManager manager;
|
private AntDirectSensorManager manager;
|
||||||
|
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
sharedPreferences = getContext().getSharedPreferences(
|
sharedPreferences = getContext().getSharedPreferences(
|
||||||
MyTracksSettings.SETTINGS_NAME, 0);
|
MyTracksSettings.SETTINGS_NAME, 0);
|
||||||
@@ -36,14 +36,16 @@ public class AntDirectSensorManagerTest extends AndroidTestCase {
|
|||||||
sharedPreferences.edit().clear().commit();
|
sharedPreferences.edit().clear().commit();
|
||||||
manager = new AntDirectSensorManager(getContext());
|
manager = new AntDirectSensorManager(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testBroadcastData() {
|
public void testBroadcastData() {
|
||||||
manager.setDeviceNumberHRM((short) 42);
|
manager.setDeviceNumberHRM((short) 42);
|
||||||
byte[] buff = new byte[11];
|
byte[] buff = new byte[11];
|
||||||
|
buff[0] = 9;
|
||||||
buff[1] = AntMesg.MESG_BROADCAST_DATA_ID;
|
buff[1] = AntMesg.MESG_BROADCAST_DATA_ID;
|
||||||
buff[2] = 0; // HRM CHANNEL
|
buff[2] = 0; // HRM CHANNEL
|
||||||
buff[10] = (byte) 220;
|
buff[10] = (byte) 220;
|
||||||
manager.handleMessage(buff);
|
manager.handleMessage(buff);
|
||||||
|
|
||||||
Sensor.SensorDataSet sds = manager.getSensorDataSet();
|
Sensor.SensorDataSet sds = manager.getSensorDataSet();
|
||||||
@@ -52,24 +54,16 @@ public class AntDirectSensorManagerTest extends AndroidTestCase {
|
|||||||
assertEquals(Sensor.SensorState.SENDING,
|
assertEquals(Sensor.SensorState.SENDING,
|
||||||
sds.getHeartRate().getState());
|
sds.getHeartRate().getState());
|
||||||
assertEquals(220, sds.getHeartRate().getValue());
|
assertEquals(220, sds.getHeartRate().getValue());
|
||||||
|
|
||||||
assertFalse(sds.hasCadence());
|
assertFalse(sds.hasCadence());
|
||||||
assertFalse(sds.hasPower());
|
assertFalse(sds.hasPower());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SmallTest
|
@SuppressWarnings("deprecation")
|
||||||
public void testHandleChannelId() {
|
|
||||||
assertEquals(0, manager.getDeviceNumberHRM());
|
|
||||||
short id = 42;
|
|
||||||
byte[] buff = new byte[5];
|
|
||||||
buff[3] = 42;
|
|
||||||
manager.handleChannelId(buff);
|
|
||||||
assertEquals(id, manager.getDeviceNumberHRM());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testChannelId() {
|
public void testChannelId() {
|
||||||
byte[] buff = new byte[11];
|
byte[] buff = new byte[11];
|
||||||
|
buff[0] = 9;
|
||||||
buff[1] = AntMesg.MESG_CHANNEL_ID_ID;
|
buff[1] = AntMesg.MESG_CHANNEL_ID_ID;
|
||||||
buff[3] = 42;
|
buff[3] = 42;
|
||||||
manager.handleMessage(buff);
|
manager.handleMessage(buff);
|
||||||
@@ -81,17 +75,21 @@ public class AntDirectSensorManagerTest extends AndroidTestCase {
|
|||||||
assertNull(manager.getSensorDataSet());
|
assertNull(manager.getSensorDataSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testResponseEvent() {
|
public void testResponseEvent() {
|
||||||
assertEquals(Sensor.SensorState.NONE, manager.getSensorState());
|
assertEquals(Sensor.SensorState.NONE, manager.getSensorState());
|
||||||
byte[] buff = new byte[11];
|
byte[] buff = new byte[5];
|
||||||
|
buff[0] = 3; // length
|
||||||
buff[1] = AntMesg.MESG_RESPONSE_EVENT_ID;
|
buff[1] = AntMesg.MESG_RESPONSE_EVENT_ID;
|
||||||
|
buff[2] = 0; // channel
|
||||||
buff[3] = AntMesg.MESG_UNASSIGN_CHANNEL_ID;
|
buff[3] = AntMesg.MESG_UNASSIGN_CHANNEL_ID;
|
||||||
|
buff[4] = 0; // code
|
||||||
manager.handleMessage(buff);
|
manager.handleMessage(buff);
|
||||||
|
|
||||||
assertEquals(Sensor.SensorState.DISCONNECTED, manager.getSensorState());
|
assertEquals(Sensor.SensorState.DISCONNECTED, manager.getSensorState());
|
||||||
assertNull(manager.getSensorDataSet());
|
assertNull(manager.getSensorDataSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test timeout too.
|
// TODO: Test timeout too.
|
||||||
}
|
}
|
||||||
|
|||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.test.AndroidTestCase;
|
||||||
|
import android.test.MoreAsserts;
|
||||||
|
|
||||||
|
public class AntSensorManagerTest extends AndroidTestCase {
|
||||||
|
private class TestAntSensorManager extends AntSensorManager {
|
||||||
|
public byte messageId;
|
||||||
|
public byte[] messageData;
|
||||||
|
|
||||||
|
public TestAntSensorManager() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setupAntSensorChannels() {}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public void handleMessage(byte[] rawMessage) {
|
||||||
|
super.handleMessage(rawMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMessage(byte messageId, byte[] messageData) {
|
||||||
|
this.messageId = messageId;
|
||||||
|
this.messageData = messageData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final TestAntSensorManager sensorManager = new TestAntSensorManager();
|
||||||
|
|
||||||
|
public void testSimple() {
|
||||||
|
byte[] rawMessage = {
|
||||||
|
0x03, // length
|
||||||
|
0x12, // message id
|
||||||
|
0x11, 0x22, 0x33, // body
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] expectedBody = { 0x11, 0x22, 0x33 };
|
||||||
|
|
||||||
|
sensorManager.handleMessage(rawMessage);
|
||||||
|
assertEquals((byte) 0x12, sensorManager.messageId);
|
||||||
|
MoreAsserts.assertEquals(expectedBody, sensorManager.messageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testTooShort() {
|
||||||
|
byte[] rawMessage = {
|
||||||
|
0x53, // length
|
||||||
|
0x12 // message id
|
||||||
|
};
|
||||||
|
|
||||||
|
sensorManager.handleMessage(rawMessage);
|
||||||
|
assertEquals(0, sensorManager.messageId);
|
||||||
|
assertNull(sensorManager.messageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLengthWrong() {
|
||||||
|
byte[] rawMessage = {
|
||||||
|
0x53, // length
|
||||||
|
0x12, // message id
|
||||||
|
0x34, // body
|
||||||
|
};
|
||||||
|
|
||||||
|
sensorManager.handleMessage(rawMessage);
|
||||||
|
assertEquals(0, sensorManager.messageId);
|
||||||
|
assertNull(sensorManager.messageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
public class AntChannelIdMessageTest extends AndroidTestCase {
|
||||||
|
public void testParse() {
|
||||||
|
byte[] rawMessage = {
|
||||||
|
0, // channel number
|
||||||
|
0x34, 0x12, // device number
|
||||||
|
(byte) 0xaa, // device type id
|
||||||
|
(byte) 0xbb, // transmission type
|
||||||
|
};
|
||||||
|
|
||||||
|
AntChannelIdMessage message = new AntChannelIdMessage(rawMessage);
|
||||||
|
assertEquals(0, message.getChannelNumber());
|
||||||
|
assertEquals(0x1234, message.getDeviceNumber());
|
||||||
|
assertEquals((byte) 0xaa, message.getDeviceTypeId());
|
||||||
|
assertEquals((byte) 0xbb, message.getTransmissionType());
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
import com.dsi.ant.AntDefine;
|
||||||
|
import com.dsi.ant.AntMesg;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
public class AntChannelResponseMessageTest extends AndroidTestCase {
|
||||||
|
public void testParse() {
|
||||||
|
byte[] rawMessage = {
|
||||||
|
0,
|
||||||
|
AntMesg.MESG_EVENT_ID,
|
||||||
|
AntDefine.EVENT_RX_SEARCH_TIMEOUT
|
||||||
|
};
|
||||||
|
AntChannelResponseMessage message = new AntChannelResponseMessage(rawMessage);
|
||||||
|
|
||||||
|
assertEquals(0, message.getChannelNumber());
|
||||||
|
assertEquals(AntMesg.MESG_EVENT_ID, message.getMessageId());
|
||||||
|
assertEquals(AntDefine.EVENT_RX_SEARCH_TIMEOUT, message.getMessageCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
public class AntMessageTest extends AndroidTestCase {
|
||||||
|
private static class TestAntMessage extends AntMessage {
|
||||||
|
public static short decodeShort(byte low, byte high) {
|
||||||
|
return AntMessage.decodeShort(low, high);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDecode() {
|
||||||
|
assertEquals(0x1234, TestAntMessage.decodeShort((byte) 0x34, (byte) 0x12));
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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.ant;
|
||||||
|
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
|
public class AntStartupMessageTest extends AndroidTestCase {
|
||||||
|
public void testParse() {
|
||||||
|
byte[] rawMessage = {
|
||||||
|
0x12,
|
||||||
|
};
|
||||||
|
|
||||||
|
AntStartupMessage message = new AntStartupMessage(rawMessage);
|
||||||
|
assertEquals(0x12, message.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user