forked from upstream-mirrors/OpenTracks
Removed support for ANT+.
This commit is contained in:
@@ -22,7 +22,6 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':myTracksLib')
|
implementation project(':myTracksLib')
|
||||||
compile files('libs/antlib.jar')
|
|
||||||
androidTestCompile files('libs/AndroidMockGenerator.jar')
|
androidTestCompile files('libs/AndroidMockGenerator.jar')
|
||||||
androidTestCompile files('libs/robotium-solo-5.0.1.jar')
|
androidTestCompile files('libs/robotium-solo-5.0.1.jar')
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -106,8 +106,3 @@
|
|||||||
# class name is enough to get the file name.
|
# class name is enough to get the file name.
|
||||||
-renamesourcefileattribute MT
|
-renamesourcefileattribute MT
|
||||||
-keepattributes SourceFile,LineNumberTable
|
-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
# Keep services needed by ANT+ protocol
|
|
||||||
-keep public class com.dsi.ant.IAnt_6
|
|
||||||
-keep public class com.dsi.ant.IAnt
|
|
||||||
-keep public class com.dsi.ant.IServiceSettings
|
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ limitations under the License.
|
|||||||
<!-- Permission for activity recognition -->
|
<!-- Permission for activity recognition -->
|
||||||
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
||||||
<!-- Permissions to access sensors -->
|
<!-- Permissions to access sensors -->
|
||||||
<uses-permission android:name="com.dsi.ant.permission.ANT" />
|
|
||||||
<uses-permission android:name="com.dsi.ant.permission.ANT_ADMIN" />
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
<!-- Permission for Android sync provider -->
|
<!-- Permission for Android sync provider -->
|
||||||
|
|||||||
-2
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
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 com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
@@ -96,7 +95,6 @@ public class SensorManagerFactory {
|
|||||||
context, R.string.sensor_type_key, PreferencesUtils.SENSOR_TYPE_DEFAULT);
|
context, R.string.sensor_type_key, PreferencesUtils.SENSOR_TYPE_DEFAULT);
|
||||||
|
|
||||||
switch (sensorType) {
|
switch (sensorType) {
|
||||||
case "ANT": return new AntSensorManager(context);
|
|
||||||
case "ZEPHYR": return new ZephyrSensorManager(context);
|
case "ZEPHYR": return new ZephyrSensorManager(context);
|
||||||
case "POLAR": return new PolarSensorManager(context);
|
case "POLAR": return new PolarSensorManager(context);
|
||||||
}
|
}
|
||||||
|
|||||||
-588
@@ -1,588 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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.AntInterface;
|
|
||||||
import com.dsi.ant.AntInterfaceIntent;
|
|
||||||
import com.dsi.ant.AntMesg;
|
|
||||||
import com.dsi.ant.exception.AntInterfaceException;
|
|
||||||
import com.dsi.ant.exception.AntServiceNotConnectedException;
|
|
||||||
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.SensorState;
|
|
||||||
import com.google.android.apps.mytracks.services.sensors.SensorManager;
|
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ant Sensor Manager.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class AntSensorManager extends SensorManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Channel States
|
|
||||||
*/
|
|
||||||
public enum ChannelStates {
|
|
||||||
CLOSED, // Channel is closed
|
|
||||||
PENDING_OPEN, // User has requested opening the channel, waiting for a reset
|
|
||||||
SEARCHING, // Channel is opened, but has not received data
|
|
||||||
TRACKING_STATUS, // Channel is opened and has received status data recently
|
|
||||||
TRACKING_DATA, // Channel is opened and has received measurement data
|
|
||||||
// recently
|
|
||||||
OFFLINE // Channel is closed as the result of a search timeout
|
|
||||||
}
|
|
||||||
public static final short WILDCARD = 0;
|
|
||||||
|
|
||||||
private static final String TAG = AntSensorManager.class.getSimpleName();
|
|
||||||
private static final int CHANNELS = 4;
|
|
||||||
private static final String RADIO_ANT = "ant";
|
|
||||||
private static final byte ANT_NETWORK = (byte) 0x01;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final ChannelConfiguration channelConfig[];
|
|
||||||
private final IntentFilter statusIntentFilter;
|
|
||||||
private final AntInterface antInterface;
|
|
||||||
|
|
||||||
private boolean serviceConnected = false;
|
|
||||||
private boolean hasClaimedInterface = false;
|
|
||||||
|
|
||||||
private SensorDataSet sensorDataSet = null;
|
|
||||||
private long lastSensorDataSetTime = 0;
|
|
||||||
private AntSensorValue antSensorValue = new AntSensorValue();
|
|
||||||
|
|
||||||
private boolean requestedReset = false;
|
|
||||||
|
|
||||||
private AntInterface.ServiceListener serviceListener = new AntInterface.ServiceListener() {
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected() {
|
|
||||||
setSensorState(Sensor.SensorState.CONNECTING);
|
|
||||||
serviceConnected = true;
|
|
||||||
try {
|
|
||||||
hasClaimedInterface = antInterface.hasClaimedInterface();
|
|
||||||
if (!hasClaimedInterface) {
|
|
||||||
hasClaimedInterface = antInterface.claimInterface();
|
|
||||||
}
|
|
||||||
if (!hasClaimedInterface) {
|
|
||||||
tryClaimAnt();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!antInterface.isEnabled()) {
|
|
||||||
antInterface.enable();
|
|
||||||
}
|
|
||||||
requestReset();
|
|
||||||
enableDataMessage(true);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected() {
|
|
||||||
serviceConnected = false;
|
|
||||||
if (hasClaimedInterface) {
|
|
||||||
enableDataMessage(false);
|
|
||||||
}
|
|
||||||
setSensorState(SensorState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param context the context
|
|
||||||
*/
|
|
||||||
public AntSensorManager(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
|
|
||||||
channelConfig = new ChannelConfiguration[CHANNELS];
|
|
||||||
channelConfig[0] = new HeartRateChannelConfiguration();
|
|
||||||
channelConfig[1] = new SpeedDistanceChannelConfiguration();
|
|
||||||
channelConfig[2] = new BikeCadenceChannelConfiguration();
|
|
||||||
channelConfig[3] = new CombinedBikeChannelConfiguration();
|
|
||||||
|
|
||||||
statusIntentFilter = new IntentFilter();
|
|
||||||
statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION);
|
|
||||||
statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLING_ACTION);
|
|
||||||
statusIntentFilter.addAction(AntInterfaceIntent.ANT_DISABLED_ACTION);
|
|
||||||
statusIntentFilter.addAction(AntInterfaceIntent.ANT_DISABLING_ACTION);
|
|
||||||
statusIntentFilter.addAction(AntInterfaceIntent.ANT_RESET_ACTION);
|
|
||||||
statusIntentFilter.addAction(AntInterfaceIntent.ANT_INTERFACE_CLAIMED_ACTION);
|
|
||||||
statusIntentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
|
||||||
|
|
||||||
antInterface = new AntInterface();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected synchronized void setUpChannel() {
|
|
||||||
tearDownChannel();
|
|
||||||
if (AntInterface.hasAntSupport(context)) {
|
|
||||||
context.registerReceiver(statusReceiver, statusIntentFilter);
|
|
||||||
if (!antInterface.initService(context, serviceListener)) {
|
|
||||||
AntInterface.goToMarket(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tearDownChannel() {
|
|
||||||
try {
|
|
||||||
context.unregisterReceiver(statusReceiver);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// Can safely ignore
|
|
||||||
}
|
|
||||||
enableDataMessage(false);
|
|
||||||
if (serviceConnected) {
|
|
||||||
try {
|
|
||||||
if (hasClaimedInterface) {
|
|
||||||
antInterface.releaseInterface();
|
|
||||||
}
|
|
||||||
hasClaimedInterface = false;
|
|
||||||
antInterface.stopRequestForceClaimInterface();
|
|
||||||
} catch (AntServiceNotConnectedException e) {
|
|
||||||
// Can safely ignore
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
Log.w(TAG, "Exception in tearDonwChannel.", e);
|
|
||||||
}
|
|
||||||
antInterface.releaseService();
|
|
||||||
serviceConnected = false;
|
|
||||||
}
|
|
||||||
setSensorState(SensorState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SensorDataSet getSensorDataSet() {
|
|
||||||
return sensorDataSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to claim the ant interface.
|
|
||||||
*/
|
|
||||||
private void tryClaimAnt() {
|
|
||||||
try {
|
|
||||||
antInterface.requestForceClaimInterface(context.getString(R.string.my_tracks_app_name));
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures ant radio.
|
|
||||||
*/
|
|
||||||
private void configureAntRadio() {
|
|
||||||
try {
|
|
||||||
if (serviceConnected && hasClaimedInterface && antInterface.isEnabled()) {
|
|
||||||
try {
|
|
||||||
antInterface.ANTDisableEventBuffering();
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
Log.e(TAG, "Cannot disable event buffering.", e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Cannot disable event buffering now.");
|
|
||||||
}
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
Log.e(TAG, "Unable to check enabled state.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles ant error.
|
|
||||||
*/
|
|
||||||
private void handleAntError() {
|
|
||||||
clearAllChannels();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a channel.
|
|
||||||
*
|
|
||||||
* @param channel the channel
|
|
||||||
*/
|
|
||||||
private void openChannel(byte channel) {
|
|
||||||
short deviceNumber = (short) PreferencesUtils.getInt(
|
|
||||||
context, channelConfig[channel].getDeviceIdKey(), WILDCARD);
|
|
||||||
channelConfig[channel].setDeviceNumber(deviceNumber);
|
|
||||||
channelConfig[channel].setChannelState(ChannelStates.PENDING_OPEN);
|
|
||||||
setupAntChannel(ANT_NETWORK, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes a channel.
|
|
||||||
*
|
|
||||||
* @param channel the channel
|
|
||||||
*/
|
|
||||||
private void closeChannel(byte channel) {
|
|
||||||
channelConfig[channel].setInitializing(false);
|
|
||||||
channelConfig[channel].setDeinitializing(true);
|
|
||||||
channelConfig[channel].setChannelState(ChannelStates.CLOSED);
|
|
||||||
try {
|
|
||||||
antInterface.ANTCloseChannel(channel);
|
|
||||||
// Note, unassign channel after getting channel closed event
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
Log.w(TAG, "Unable to close channel: " + channel, e);
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all channels.
|
|
||||||
*/
|
|
||||||
private void clearAllChannels() {
|
|
||||||
for (int i = 0; i < CHANNELS; i++) {
|
|
||||||
channelConfig[i].setChannelState(ChannelStates.CLOSED);
|
|
||||||
}
|
|
||||||
setSensorState(SensorState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests reset.
|
|
||||||
*/
|
|
||||||
private void requestReset() {
|
|
||||||
try {
|
|
||||||
requestedReset = true;
|
|
||||||
antInterface.ANTResetSystem();
|
|
||||||
configureAntRadio();
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
Log.e(TAG, "Unable to reset ant.", e);
|
|
||||||
requestedReset = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
if (antInterface == null || !antInterface.isServiceConnected()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return antInterface.isEnabled();
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
Log.w(TAG, "Unable to check enabled.", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final BroadcastReceiver statusReceiver = new BroadcastReceiver() {
|
|
||||||
public void onReceive(Context c, Intent intent) {
|
|
||||||
String ANTAction = intent.getAction();
|
|
||||||
if (ANTAction.equals(AntInterfaceIntent.ANT_DISABLED_ACTION)) {
|
|
||||||
clearAllChannels();
|
|
||||||
} else if (ANTAction.equals(AntInterfaceIntent.ANT_RESET_ACTION)) {
|
|
||||||
if (!requestedReset) {
|
|
||||||
// Someone else triggered an ant reset
|
|
||||||
clearAllChannels();
|
|
||||||
} else {
|
|
||||||
requestedReset = false;
|
|
||||||
configureAntRadio();
|
|
||||||
}
|
|
||||||
} else if (ANTAction.equals(AntInterfaceIntent.ANT_INTERFACE_CLAIMED_ACTION)) {
|
|
||||||
boolean wasClaimed = hasClaimedInterface;
|
|
||||||
|
|
||||||
// Could also read ANT_INTERFACE_CLAIMED_PID from intent and see if it
|
|
||||||
// matches the current process PID.
|
|
||||||
try {
|
|
||||||
hasClaimedInterface = antInterface.hasClaimedInterface();
|
|
||||||
if (hasClaimedInterface) {
|
|
||||||
enableDataMessage(true);
|
|
||||||
} else {
|
|
||||||
if (wasClaimed) {
|
|
||||||
// Claimed by another application
|
|
||||||
enableDataMessage(false);
|
|
||||||
setSensorState(SensorState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
} else if (ANTAction.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
|
|
||||||
if (isAirPlaneMode()) {
|
|
||||||
clearAllChannels();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BroadcastReceiver dataReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context c, Intent intent) {
|
|
||||||
if (intent.getAction().equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) {
|
|
||||||
byte[] antRxMessage = intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE);
|
|
||||||
byte channel;
|
|
||||||
switch (antRxMessage[AntMesg.MESG_ID_OFFSET]) {
|
|
||||||
case AntMesg.MESG_BROADCAST_DATA_ID:
|
|
||||||
case AntMesg.MESG_ACKNOWLEDGED_DATA_ID:
|
|
||||||
channel = antRxMessage[AntMesg.MESG_DATA_OFFSET];
|
|
||||||
if (channelConfig[channel].getChannelState() != ChannelStates.CLOSED) {
|
|
||||||
channelConfig[channel].setChannelState(ChannelStates.TRACKING_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channelConfig[channel].getDeviceNumber() == WILDCARD) {
|
|
||||||
try {
|
|
||||||
antInterface.ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channelConfig[channel].decodeMessage(antRxMessage, antSensorValue);
|
|
||||||
setSensorDataSet();
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_RESPONSE_EVENT_ID:
|
|
||||||
handleResponseEventMessage(antRxMessage);
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_CHANNEL_ID_ID:
|
|
||||||
channel = antRxMessage[AntMesg.MESG_DATA_OFFSET];
|
|
||||||
short deviceNumber = (short) ((antRxMessage[AntMesg.MESG_DATA_OFFSET + 1] & 0xFF
|
|
||||||
| ((antRxMessage[AntMesg.MESG_DATA_OFFSET + 2] & 0xFF) << 8)) & 0xFFFF);
|
|
||||||
channelConfig[channel].setDeviceNumber(deviceNumber);
|
|
||||||
PreferencesUtils.setInt(context, channelConfig[channel].getDeviceIdKey(), deviceNumber);
|
|
||||||
Toast.makeText(context,
|
|
||||||
context.getString(R.string.settings_sensor_connected, deviceNumber),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
setSensorState(SensorState.CONNECTED);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles response event message.
|
|
||||||
*
|
|
||||||
* @param message the message
|
|
||||||
*/
|
|
||||||
private void handleResponseEventMessage(byte[] message) {
|
|
||||||
// For a list of possible message codes see ANT Message Protocol and Usage
|
|
||||||
// section 9.5.6.1 available from thisisant.com
|
|
||||||
byte channel = message[AntMesg.MESG_DATA_OFFSET];
|
|
||||||
if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_EVENT_ID)
|
|
||||||
&& (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.EVENT_RX_SEARCH_TIMEOUT)) {
|
|
||||||
// A channel timed out searching, unassign it
|
|
||||||
channelConfig[channel].setInitializing(false);
|
|
||||||
channelConfig[channel].setDeinitializing(false);
|
|
||||||
channelConfig[channel].setChannelState(ChannelStates.OFFLINE);
|
|
||||||
try {
|
|
||||||
antInterface.ANTUnassignChannel(channel);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
setSensorState(SensorState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channelConfig[channel].isInitializing()) {
|
|
||||||
if (message[AntMesg.MESG_DATA_OFFSET + 2] != 0) {
|
|
||||||
// Error response
|
|
||||||
Log.e(TAG, String.format(Locale.US,
|
|
||||||
"Error code(%#02x) on message ID(%#02x) on channel %d",
|
|
||||||
message[AntMesg.MESG_DATA_OFFSET + 2], message[AntMesg.MESG_DATA_OFFSET + 1],
|
|
||||||
channel));
|
|
||||||
} else {
|
|
||||||
// Switch on message id
|
|
||||||
switch (message[AntMesg.MESG_DATA_OFFSET + 1]) {
|
|
||||||
case AntMesg.MESG_ASSIGN_CHANNEL_ID:
|
|
||||||
try {
|
|
||||||
antInterface.ANTSetChannelId(channel, channelConfig[channel].getDeviceNumber(),
|
|
||||||
channelConfig[channel].getDeviceType(), ChannelConfiguration.TRANSMISSION_TYPE);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_CHANNEL_ID_ID:
|
|
||||||
try {
|
|
||||||
antInterface.ANTSetChannelPeriod(channel, channelConfig[channel].getMessagPeriod());
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_CHANNEL_MESG_PERIOD_ID:
|
|
||||||
try {
|
|
||||||
antInterface.ANTSetChannelRFFreq(channel, ChannelConfiguration.FREQUENCY);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_CHANNEL_RADIO_FREQ_ID:
|
|
||||||
try {
|
|
||||||
// Disable high priority search
|
|
||||||
antInterface.ANTSetChannelSearchTimeout(channel, (byte) 0);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_CHANNEL_SEARCH_TIMEOUT_ID:
|
|
||||||
try {
|
|
||||||
// Set search timeout to 30 seconds (low priority search)
|
|
||||||
antInterface.ANTSetLowPriorityChannelSearchTimeout(channel, (byte) 12);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_SET_LP_SEARCH_TIMEOUT_ID:
|
|
||||||
if (channelConfig[channel].getDeviceNumber() == WILDCARD) {
|
|
||||||
try {
|
|
||||||
// Configure proximity search, if using wild card search
|
|
||||||
antInterface.ANTSetProximitySearch(
|
|
||||||
channel, ChannelConfiguration.PROXIMITY_SEARCH);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
antInterface.ANTOpenChannel(channel);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_PROX_SEARCH_CONFIG_ID:
|
|
||||||
try {
|
|
||||||
antInterface.ANTOpenChannel(channel);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AntMesg.MESG_OPEN_CHANNEL_ID:
|
|
||||||
channelConfig[channel].setInitializing(false);
|
|
||||||
channelConfig[channel].setChannelState(ChannelStates.SEARCHING);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (channelConfig[channel].isDeinitializing()) {
|
|
||||||
if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_EVENT_ID)
|
|
||||||
&& (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.EVENT_CHANNEL_CLOSED)) {
|
|
||||||
try {
|
|
||||||
antInterface.ANTUnassignChannel(channel);
|
|
||||||
} catch (AntInterfaceException e) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
} else if ((message[AntMesg.MESG_DATA_OFFSET + 1] == AntMesg.MESG_UNASSIGN_CHANNEL_ID)
|
|
||||||
&& (message[AntMesg.MESG_DATA_OFFSET + 2] == AntDefine.RESPONSE_NO_ERROR)) {
|
|
||||||
channelConfig[channel].setDeinitializing(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets sensor data set.
|
|
||||||
*/
|
|
||||||
private void setSensorDataSet() {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
// Data comes in at ~4Hz rate from the sensors, so after >300 msec fresh
|
|
||||||
// data is here from all the connected sensors
|
|
||||||
if (now < lastSensorDataSetTime + 300) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastSensorDataSetTime = now;
|
|
||||||
|
|
||||||
SensorDataSet.Builder builder = Sensor.SensorDataSet.newBuilder();
|
|
||||||
int heartRate = antSensorValue.getHeartRate();
|
|
||||||
if (heartRate != -1) {
|
|
||||||
builder.setHeartRate(Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(heartRate).setState(Sensor.SensorState.SENDING));
|
|
||||||
}
|
|
||||||
int cadence = antSensorValue.getCadence();
|
|
||||||
if (cadence != -1) {
|
|
||||||
builder.setCadence(Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(cadence).setState(Sensor.SensorState.SENDING));
|
|
||||||
}
|
|
||||||
sensorDataSet = builder.setCreationTime(now).build();
|
|
||||||
setSensorState(SensorState.SENDING);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up ant channel.
|
|
||||||
*
|
|
||||||
* @param networkNumber the network number
|
|
||||||
* @param channel the channel
|
|
||||||
*/
|
|
||||||
private void setupAntChannel(byte networkNumber, byte channel) {
|
|
||||||
try {
|
|
||||||
channelConfig[channel].setInitializing(true);
|
|
||||||
channelConfig[channel].setDeinitializing(false);
|
|
||||||
|
|
||||||
// Assign as slave channel on selected network
|
|
||||||
antInterface.ANTAssignChannel(channel, AntDefine.PARAMETER_RX_NOT_TX, networkNumber);
|
|
||||||
|
|
||||||
// The rest of the channel configuration will occur after the response is
|
|
||||||
// received in handleResponseEventMessage
|
|
||||||
} catch (AntInterfaceException aie) {
|
|
||||||
handleAntError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables data message.
|
|
||||||
*
|
|
||||||
* @param enabled true to enable
|
|
||||||
*/
|
|
||||||
private void enableDataMessage(boolean enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
context.registerReceiver(
|
|
||||||
dataReceiver, new IntentFilter(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION));
|
|
||||||
for (int i = 0; i < CHANNELS; i++) {
|
|
||||||
openChannel((byte) i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
context.unregisterReceiver(dataReceiver);
|
|
||||||
for (int i = 0; i < CHANNELS; i++) {
|
|
||||||
closeChannel((byte) i);
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// Can safely ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if in airplane mode.
|
|
||||||
*/
|
|
||||||
private boolean isAirPlaneMode() {
|
|
||||||
if (!Settings.System.getString(
|
|
||||||
context.getContentResolver(), Settings.System.AIRPLANE_MODE_RADIOS)
|
|
||||||
.contains(RADIO_ANT)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0)
|
|
||||||
== 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Field field = Settings.System.class.getField("AIRPLANE_MODE_TOGGLEABLE_RADIOS");
|
|
||||||
return !Settings.System.getString(context.getContentResolver(), (String) field.get(null))
|
|
||||||
.contains(RADIO_ANT);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// This is expected if the list does not yet exist, so just return true
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-60
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains the latest ant sensor value.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class AntSensorValue {
|
|
||||||
|
|
||||||
private int heartRate = -1;
|
|
||||||
private int cadence = -1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the heart rate.
|
|
||||||
*/
|
|
||||||
public int getHeartRate() {
|
|
||||||
return heartRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the heart rate.
|
|
||||||
*
|
|
||||||
* @param heartRate the heart rate to set
|
|
||||||
*/
|
|
||||||
public void setHeartRate(int heartRate) {
|
|
||||||
this.heartRate = heartRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the cadence.
|
|
||||||
*/
|
|
||||||
public int getCadence() {
|
|
||||||
return cadence;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the cadence.
|
|
||||||
*
|
|
||||||
* @param cadence the cadence to set
|
|
||||||
*/
|
|
||||||
public void setCadence(int cadence) {
|
|
||||||
this.cadence = cadence;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bike cadence sensor channel configuration.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class BikeCadenceChannelConfiguration extends ChannelConfiguration {
|
|
||||||
|
|
||||||
private static final int DEVICE_ID_KEY = R.string.ant_bike_cadence_sensor_id_key;
|
|
||||||
private static final byte DEVICE_TYPE = 0x7A;
|
|
||||||
private static final short MESSAGE_PERIOD = 8102;
|
|
||||||
|
|
||||||
private CadenceCounter cadenceCounter = new CadenceCounter();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeviceIdKey() {
|
|
||||||
return DEVICE_ID_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getDeviceType() {
|
|
||||||
return DEVICE_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short getMessagPeriod() {
|
|
||||||
return MESSAGE_PERIOD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
|
|
||||||
int revolutionCount = ((int) message[9] & 0xFF) + ((int) message[10] & 0xFF) * 256;
|
|
||||||
int eventTime = ((int) message[7] & 0xFF) + ((int) message[8] & 0xFF) * 256;
|
|
||||||
antSensorValue.setCadence(cadenceCounter.getEventsPerMinute(revolutionCount, eventTime));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-178
@@ -1,178 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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 java.util.LinkedList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A counter that processes an Ant+ sensor data (count + event time) and returns
|
|
||||||
* the instantaneous cadence value.
|
|
||||||
*
|
|
||||||
* @author Laszlo Molnar
|
|
||||||
*/
|
|
||||||
public class CadenceCounter {
|
|
||||||
|
|
||||||
private static final int MILLIS_PER_MINUTE = 60000;
|
|
||||||
private static final int MAX_HISTORY_TIME_IN_MILLIS = 5000; // 5 seconds
|
|
||||||
private static final int MAX_HISTORY_SIZE = 100;
|
|
||||||
private static final int EVENT_TIME_PER_MINUTE = 60 * 1024;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cadence data.
|
|
||||||
*
|
|
||||||
* @author Laszlo Molnar
|
|
||||||
*/
|
|
||||||
private static class CadenceData {
|
|
||||||
final long systemTime;
|
|
||||||
final int count;
|
|
||||||
final int eventTime;
|
|
||||||
|
|
||||||
private CadenceData(long systemTime, int count, int eventTime) {
|
|
||||||
this.systemTime = systemTime;
|
|
||||||
this.count = count;
|
|
||||||
this.eventTime = eventTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The last count
|
|
||||||
private int lastCount;
|
|
||||||
|
|
||||||
// The last calculated cadence
|
|
||||||
private int eventsPerMinute;
|
|
||||||
|
|
||||||
// The history of the previous sensor data, oldest first
|
|
||||||
private LinkedList<CadenceData> history;
|
|
||||||
|
|
||||||
public CadenceCounter() {
|
|
||||||
lastCount = -1;
|
|
||||||
eventsPerMinute = 0;
|
|
||||||
history = new LinkedList<CadenceData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the cadence value.
|
|
||||||
*
|
|
||||||
* @param count count
|
|
||||||
* @param eventTime event time
|
|
||||||
*/
|
|
||||||
public int getEventsPerMinute(int count, int eventTime) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
int countChange = (count - lastCount) & 0xFFFF;
|
|
||||||
|
|
||||||
if (lastCount < 0) {
|
|
||||||
/*
|
|
||||||
* eventTime for the initial count value is probably out of date, so not
|
|
||||||
* updating the history.
|
|
||||||
*/
|
|
||||||
lastCount = count;
|
|
||||||
eventsPerMinute = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastCount = count;
|
|
||||||
|
|
||||||
if (countChange != 0) {
|
|
||||||
if (removeOldHistory(now)) {
|
|
||||||
CadenceData lastCadenceData = history.getLast();
|
|
||||||
int eventTimeChange = (eventTime - lastCadenceData.eventTime) & 0xFFFF;
|
|
||||||
if (eventTimeChange != 0) {
|
|
||||||
countChange = (count - lastCadenceData.count) & 0xFFFF;
|
|
||||||
eventsPerMinute = countChange * EVENT_TIME_PER_MINUTE / eventTimeChange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
history.addLast(new CadenceData(now, count, eventTime));
|
|
||||||
return eventsPerMinute;
|
|
||||||
} else {
|
|
||||||
// The sensor has resent old data
|
|
||||||
if (history.isEmpty()) {
|
|
||||||
eventsPerMinute = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CadenceData lastCadenceData = history.getLast();
|
|
||||||
if ((now - lastCadenceData.systemTime) * eventsPerMinute < MILLIS_PER_MINUTE) {
|
|
||||||
// The last eventsPerMinute is still valid
|
|
||||||
return eventsPerMinute;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the history
|
|
||||||
if (!removeOldHistory(now)) {
|
|
||||||
eventsPerMinute = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Too much time has passed since the last count change. A smaller value
|
|
||||||
* than eventsPerMinute must be returned. The value is calculated from the
|
|
||||||
* history.
|
|
||||||
*/
|
|
||||||
return getValueFromHistory(now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the cadence value from the history when the sensor only resends the
|
|
||||||
* old data.
|
|
||||||
*
|
|
||||||
* @param now the current system time
|
|
||||||
*/
|
|
||||||
private int getValueFromHistory(long now) {
|
|
||||||
CadenceData firstCadenceData = history.getFirst();
|
|
||||||
CadenceData lastCadenceData = history.getLast();
|
|
||||||
int eventTimeChange = (lastCadenceData.eventTime - firstCadenceData.eventTime) & 0xFFFF;
|
|
||||||
int countChange = (lastCount - firstCadenceData.count) & 0xFFFF;
|
|
||||||
|
|
||||||
// (now - lastCadenceData) + (lastCadenceData - firstCadenceData)
|
|
||||||
int systemTimeChange = (int) (now - lastCadenceData.systemTime
|
|
||||||
+ (eventTimeChange * MILLIS_PER_MINUTE) / EVENT_TIME_PER_MINUTE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* eventsPerMinute is not updated because it is still needed when a new
|
|
||||||
* sensor event arrives.
|
|
||||||
*/
|
|
||||||
if (systemTimeChange == 0) {
|
|
||||||
return eventsPerMinute;
|
|
||||||
}
|
|
||||||
int value = (countChange * MILLIS_PER_MINUTE) / systemTimeChange;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Do not return a larger number than eventsPerMinute because this function
|
|
||||||
* is only called when more time has passed after the last count change than
|
|
||||||
* the interval from eventsPerMinute.
|
|
||||||
*/
|
|
||||||
return value < eventsPerMinute ? value : eventsPerMinute;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes old data from the history.
|
|
||||||
*
|
|
||||||
* @param now the current system time
|
|
||||||
* @return true if the remaining history is not empty.
|
|
||||||
*/
|
|
||||||
private boolean removeOldHistory(long now) {
|
|
||||||
CadenceData historyElement = history.peek();
|
|
||||||
while (historyElement != null) {
|
|
||||||
if (now - historyElement.systemTime <= MAX_HISTORY_TIME_IN_MILLIS
|
|
||||||
&& history.size() < MAX_HISTORY_SIZE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
history.removeFirst();
|
|
||||||
historyElement = history.peek();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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.google.android.apps.mytracks.services.sensors.ant.AntSensorManager.ChannelStates;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ant channel configuration.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public abstract class ChannelConfiguration {
|
|
||||||
|
|
||||||
public static final byte FREQUENCY = 57; // 2457Mhz (Ant+ frequency)
|
|
||||||
public static final byte TRANSMISSION_TYPE = 0; // 0 for wild card search
|
|
||||||
public static final byte PROXIMITY_SEARCH = 7;
|
|
||||||
|
|
||||||
private short deviceNumber = AntSensorManager.WILDCARD;
|
|
||||||
private ChannelStates channelState = ChannelStates.CLOSED;
|
|
||||||
private boolean isInitializing = false;
|
|
||||||
private boolean isDeinitializing = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the My Tracks settings device id key.
|
|
||||||
*/
|
|
||||||
public abstract int getDeviceIdKey();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the device type.
|
|
||||||
*/
|
|
||||||
public abstract byte getDeviceType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the message period.
|
|
||||||
*/
|
|
||||||
public abstract short getMessagPeriod();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes the message and places the value in antSensorValue.
|
|
||||||
*
|
|
||||||
* @param message the message
|
|
||||||
* @param antSensorValue the ant sensor value
|
|
||||||
*/
|
|
||||||
public abstract void decodeMessage(byte[] message, AntSensorValue antSensorValue);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the device number.
|
|
||||||
*/
|
|
||||||
public short getDeviceNumber() {
|
|
||||||
return deviceNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the device number.
|
|
||||||
*
|
|
||||||
* @param deviceNumber the device number to set
|
|
||||||
*/
|
|
||||||
public void setDeviceNumber(short deviceNumber) {
|
|
||||||
this.deviceNumber = deviceNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the channel state.
|
|
||||||
*/
|
|
||||||
public ChannelStates getChannelState() {
|
|
||||||
return channelState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the channel state.
|
|
||||||
*
|
|
||||||
* @param channelState the channelState to set
|
|
||||||
*/
|
|
||||||
public void setChannelState(ChannelStates channelState) {
|
|
||||||
this.channelState = channelState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if initializing.
|
|
||||||
*/
|
|
||||||
public boolean isInitializing() {
|
|
||||||
return isInitializing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets isInitializing.
|
|
||||||
*
|
|
||||||
* @param isInitializing true if isInitializing
|
|
||||||
*/
|
|
||||||
public void setInitializing(boolean isInitializing) {
|
|
||||||
this.isInitializing = isInitializing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if deinitializing.
|
|
||||||
*/
|
|
||||||
public boolean isDeinitializing() {
|
|
||||||
return isDeinitializing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets isDeinitializing.
|
|
||||||
*
|
|
||||||
* @param isDeinitializing true if isDeinitializing
|
|
||||||
*/
|
|
||||||
public void setDeinitializing(boolean isDeinitializing) {
|
|
||||||
this.isDeinitializing = isDeinitializing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-56
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combined bike sensor channel configuration.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class CombinedBikeChannelConfiguration extends ChannelConfiguration {
|
|
||||||
|
|
||||||
private static final int DEVICE_ID_KEY = R.string.ant_combined_bike_sensor_id_key;
|
|
||||||
private static final byte DEVICE_TYPE = 0x79;
|
|
||||||
private static final short MESSAGE_PERIOD = 8086;
|
|
||||||
|
|
||||||
private CadenceCounter cadenceCounter = new CadenceCounter();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeviceIdKey() {
|
|
||||||
return DEVICE_ID_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getDeviceType() {
|
|
||||||
return DEVICE_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short getMessagPeriod() {
|
|
||||||
return MESSAGE_PERIOD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
|
|
||||||
int revolutionCount = ((int) message[5] & 0xFF) + ((int) message[6] & 0xFF) * 256;
|
|
||||||
int eventTime = ((int) message[3] & 0xFF) + ((int) message[4] & 0xFF) * 256;
|
|
||||||
|
|
||||||
antSensorValue.setCadence(cadenceCounter.getEventsPerMinute(revolutionCount, eventTime));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Heart rate monitor channel configuration.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class HeartRateChannelConfiguration extends ChannelConfiguration {
|
|
||||||
|
|
||||||
private static final int DEVICE_ID_KEY = R.string.ant_heart_rate_monitor_id_key;
|
|
||||||
private static final byte DEVICE_TYPE = 0x78;
|
|
||||||
private static final short MESSAGE_PERIOD = 8070;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeviceIdKey() {
|
|
||||||
return DEVICE_ID_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getDeviceType() {
|
|
||||||
return DEVICE_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short getMessagPeriod() {
|
|
||||||
return MESSAGE_PERIOD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
|
|
||||||
antSensorValue.setHeartRate(message[10] & 0xFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 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.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Speed distance monitor channel configuration.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class SpeedDistanceChannelConfiguration extends ChannelConfiguration {
|
|
||||||
|
|
||||||
private static final int DEVICE_ID_KEY = R.string.ant_speed_distance_monitor_id_key;
|
|
||||||
private static final byte DEVICE_TYPE = 0x7C;
|
|
||||||
private static final short MESSAGE_PERIOD = 8134;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeviceIdKey() {
|
|
||||||
return DEVICE_ID_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getDeviceType() {
|
|
||||||
return DEVICE_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short getMessagPeriod() {
|
|
||||||
return MESSAGE_PERIOD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void decodeMessage(byte[] message, AntSensorValue antSensorValue) {
|
|
||||||
// Check page 2 data
|
|
||||||
if (message[3] == 0x02) {
|
|
||||||
antSensorValue.setCadence(
|
|
||||||
(int) ((message[6] & 0xFF) + (((message[7] >>> 4) & 0x0F) / 16.0f)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+6
-64
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package com.google.android.apps.mytracks.settings;
|
package com.google.android.apps.mytracks.settings;
|
||||||
|
|
||||||
import com.dsi.ant.AntInterface;
|
|
||||||
import com.google.android.apps.mytracks.services.sensors.ant.AntSensorManager;
|
|
||||||
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
|
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
@@ -48,9 +46,7 @@ public class SensorSettingsActivity extends AbstractSettingsActivity {
|
|||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
addPreferencesFromResource(R.xml.sensor_settings);
|
addPreferencesFromResource(R.xml.sensor_settings);
|
||||||
|
|
||||||
boolean hasAntSupport = AntInterface.hasAntSupport(this);
|
configSensorType();
|
||||||
|
|
||||||
configSensorType(hasAntSupport);
|
|
||||||
|
|
||||||
findPreference(getString(R.string.settings_sensor_bluetooth_pairing_key))
|
findPreference(getString(R.string.settings_sensor_bluetooth_pairing_key))
|
||||||
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
@@ -60,30 +56,15 @@ public class SensorSettingsActivity extends AbstractSettingsActivity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!hasAntSupport) {
|
|
||||||
PreferenceScreen rootPreferenceScreen = (PreferenceScreen) findPreference(
|
|
||||||
getString(R.string.settings_sensor_root_key));
|
|
||||||
rootPreferenceScreen.removePreference(
|
|
||||||
findPreference(getString(R.string.settings_sensor_ant_key)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void configSensorType(boolean hasAntSupport) {
|
private void configSensorType() {
|
||||||
ListPreference preference = (ListPreference) findPreference(
|
ListPreference preference = (ListPreference) findPreference(getString(R.string.sensor_type_key));
|
||||||
getString(R.string.sensor_type_key));
|
|
||||||
String value = PreferencesUtils.getString(
|
String value = PreferencesUtils.getString(
|
||||||
this, R.string.sensor_type_key, PreferencesUtils.SENSOR_TYPE_DEFAULT);
|
this, R.string.sensor_type_key, PreferencesUtils.SENSOR_TYPE_DEFAULT);
|
||||||
String[] options = getResources().getStringArray(
|
String[] options = getResources().getStringArray(R.array.sensor_type_bluetooth_options);
|
||||||
hasAntSupport ? R.array.sensor_type_all_options : R.array.sensor_type_bluetooth_options);
|
String[] values = getResources().getStringArray( R.array.sensor_type_bluetooth_values);
|
||||||
String[] values = getResources().getStringArray(
|
|
||||||
hasAntSupport ? R.array.sensor_type_all_values : R.array.sensor_type_bluetooth_values);
|
|
||||||
|
|
||||||
if (!hasAntSupport && value.equals(R.string.sensor_type_value_ant)) {
|
|
||||||
value = PreferencesUtils.SENSOR_TYPE_DEFAULT;
|
|
||||||
PreferencesUtils.setString(this, R.string.sensor_type_key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
|
OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -102,49 +83,10 @@ public class SensorSettingsActivity extends AbstractSettingsActivity {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void updateUiBySensorType(String sensorType) {
|
private void updateUiBySensorType(String sensorType) {
|
||||||
boolean isBluetooth = getString(R.string.sensor_type_value_polar).equals(sensorType)
|
boolean isBluetooth = getString(R.string.sensor_type_value_polar).equals(sensorType) || getString(R.string.sensor_type_value_zephyr).equals(sensorType);
|
||||||
|| getString(R.string.sensor_type_value_zephyr).equals(sensorType);
|
|
||||||
findPreference(getString(R.string.settings_sensor_bluetooth_key)).setEnabled(isBluetooth);
|
findPreference(getString(R.string.settings_sensor_bluetooth_key)).setEnabled(isBluetooth);
|
||||||
|
|
||||||
boolean isAnt = getString(R.string.sensor_type_value_ant).equals(sensorType);
|
|
||||||
updateAntSensor(R.string.settings_sensor_ant_reset_heart_rate_monitor_key,
|
|
||||||
R.string.ant_heart_rate_monitor_id_key, isAnt);
|
|
||||||
updateAntSensor(R.string.settings_sensor_ant_reset_speed_distance_monitor_key,
|
|
||||||
R.string.ant_speed_distance_monitor_id_key, isAnt);
|
|
||||||
updateAntSensor(R.string.settings_sensor_ant_reset_bike_cadence_sensor_key,
|
|
||||||
R.string.ant_bike_cadence_sensor_id_key, isAnt);
|
|
||||||
updateAntSensor(R.string.settings_sensor_ant_reset_combined_bike_sensor_key,
|
|
||||||
R.string.ant_combined_bike_sensor_id_key, isAnt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an ant sensor.
|
|
||||||
*
|
|
||||||
* @param preferenceKey the preference key
|
|
||||||
* @param valueKey the value key
|
|
||||||
* @param enabled true if enabled
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void updateAntSensor(int preferenceKey, final int valueKey, boolean enabled) {
|
|
||||||
Preference preference = findPreference(getString(preferenceKey));
|
|
||||||
if (preference != null) {
|
|
||||||
preference.setEnabled(enabled);
|
|
||||||
int deviceId = PreferencesUtils.getInt(this, valueKey, AntSensorManager.WILDCARD);
|
|
||||||
if (deviceId == AntSensorManager.WILDCARD) {
|
|
||||||
preference.setSummary(R.string.settings_sensor_ant_not_connected);
|
|
||||||
} else {
|
|
||||||
preference.setSummary(getString(R.string.settings_sensor_ant_paired, deviceId));
|
|
||||||
}
|
|
||||||
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference pref) {
|
|
||||||
PreferencesUtils.setInt(SensorSettingsActivity.this, valueKey, AntSensorManager.WILDCARD);
|
|
||||||
pref.setSummary(R.string.settings_sensor_ant_not_connected);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
|
|||||||
@@ -167,18 +167,6 @@ limitations under the License.
|
|||||||
<item>1000</item>
|
<item>1000</item>
|
||||||
<item>2000</item>
|
<item>2000</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="sensor_type_all_options">
|
|
||||||
<item>@string/settings_sensor_type_polar</item>
|
|
||||||
<item>@string/settings_sensor_type_zephyr</item>
|
|
||||||
<item>@string/settings_sensor_type_ant</item>
|
|
||||||
<item>@string/value_none</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="sensor_type_all_values">
|
|
||||||
<item>@string/sensor_type_value_polar</item>
|
|
||||||
<item>@string/sensor_type_value_zephyr</item>
|
|
||||||
<item>@string/sensor_type_value_ant</item>
|
|
||||||
<item>@string/sensor_type_value_none</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="sensor_type_bluetooth_options">
|
<string-array name="sensor_type_bluetooth_options">
|
||||||
<item>@string/settings_sensor_type_polar</item>
|
<item>@string/settings_sensor_type_polar</item>
|
||||||
<item>@string/settings_sensor_type_zephyr</item>
|
<item>@string/settings_sensor_type_zephyr</item>
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ Google은 이용자에 대한 위치정보의 이용∙제공사실을 위치정
|
|||||||
<string name="my_tracks_recording_service_name">My Tracks Recording Service</string>
|
<string name="my_tracks_recording_service_name">My Tracks Recording Service</string>
|
||||||
<string name="my_tracks_web_url">www.google.com/mobile/mytracks</string>
|
<string name="my_tracks_web_url">www.google.com/mobile/mytracks</string>
|
||||||
<string name="permission_notification_value">com.google.android.apps.mytracks.MYTRACKS_NOTIFICATIONS</string>
|
<string name="permission_notification_value">com.google.android.apps.mytracks.MYTRACKS_NOTIFICATIONS</string>
|
||||||
<string name="sensor_type_value_ant">ANT</string>
|
|
||||||
<string name="sensor_type_value_none">NONE</string>
|
<string name="sensor_type_value_none">NONE</string>
|
||||||
<string name="sensor_type_value_polar">POLAR</string>
|
<string name="sensor_type_value_polar">POLAR</string>
|
||||||
<string name="sensor_type_value_zephyr">ZEPHYR</string>
|
<string name="sensor_type_value_zephyr">ZEPHYR</string>
|
||||||
|
|||||||
@@ -80,11 +80,6 @@ limitations under the License.
|
|||||||
<string name="settings_recording_key">settingsRecording</string>
|
<string name="settings_recording_key">settingsRecording</string>
|
||||||
<string name="settings_reset_key">settingsReset</string>
|
<string name="settings_reset_key">settingsReset</string>
|
||||||
<string name="settings_sensor_key">settingsSensor</string>
|
<string name="settings_sensor_key">settingsSensor</string>
|
||||||
<string name="settings_sensor_ant_key">settingsSensorAnt</string>
|
|
||||||
<string name="settings_sensor_ant_reset_bike_cadence_sensor_key">settingsSensorAntResetBikeCadenceSensor</string>
|
|
||||||
<string name="settings_sensor_ant_reset_combined_bike_sensor_key">settingsSensorAntResetCombinedBikeSensor</string>
|
|
||||||
<string name="settings_sensor_ant_reset_heart_rate_monitor_key">settingsSensorAntResetHeartRateMonitor</string>
|
|
||||||
<string name="settings_sensor_ant_reset_speed_distance_monitor_key">settingsSensorAntResetSpeedDistanceMonitor</string>
|
|
||||||
<string name="settings_sensor_bluetooth_key">settingsSensorBluetooth</string>
|
<string name="settings_sensor_bluetooth_key">settingsSensorBluetooth</string>
|
||||||
<string name="settings_sensor_bluetooth_pairing_key">settingsSensorBluetoothPairing</string>
|
<string name="settings_sensor_bluetooth_pairing_key">settingsSensorBluetoothPairing</string>
|
||||||
<string name="settings_sensor_root_key">settingsSensorRoot</string>
|
<string name="settings_sensor_root_key">settingsSensorRoot</string>
|
||||||
|
|||||||
@@ -1814,13 +1814,6 @@ limitations under the License.
|
|||||||
Character limit: ~23 (1 line)">
|
Character limit: ~23 (1 line)">
|
||||||
Sensor type
|
Sensor type
|
||||||
</string>
|
</string>
|
||||||
<string name="settings_sensor_type_ant"
|
|
||||||
translation_description="In the 'Sensor' settings, the option in the 'Sensor type' picker to
|
|
||||||
choose an ANT+ sensor. Note that ANT+ is a radio standard.
|
|
||||||
|
|
||||||
Character limit: ~20 (1 line)">
|
|
||||||
ANT+
|
|
||||||
</string>
|
|
||||||
<string name="settings_sensor_type_polar"
|
<string name="settings_sensor_type_polar"
|
||||||
translation_description="In the 'Sensor' settings, the option in the 'Sensor type' picker to
|
translation_description="In the 'Sensor' settings, the option in the 'Sensor type' picker to
|
||||||
choose a Polar bluetooth sensor. Do not translate Polar, the company name, but can translate
|
choose a Polar bluetooth sensor. Do not translate Polar, the company name, but can translate
|
||||||
|
|||||||
@@ -34,24 +34,4 @@ limitations under the License.
|
|||||||
android:key="@string/bluetooth_sensor_key"
|
android:key="@string/bluetooth_sensor_key"
|
||||||
android:title="@string/settings_sensor_bluetooth_sensor" />
|
android:title="@string/settings_sensor_bluetooth_sensor" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
|
||||||
android:key="@string/settings_sensor_ant_key"
|
|
||||||
android:title="@string/settings_sensor_type_ant" >
|
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_sensor_ant_reset_heart_rate_monitor_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_sensor_ant_reset_heart_rate_monitor" />
|
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_sensor_ant_reset_speed_distance_monitor_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_sensor_ant_reset_speed_distance_monitor" />
|
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_sensor_ant_reset_bike_cadence_sensor_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_sensor_ant_reset_bike_cadence_sensor" />
|
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_sensor_ant_reset_combined_bike_sensor_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_sensor_ant_reset_combined_bike_sensor" />
|
|
||||||
</PreferenceCategory>
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user