Lefun: Add comments

This commit is contained in:
Yukai Li
2020-10-05 15:08:11 -06:00
committed by Gitea
parent 6974a86b87
commit 1242009b55
8 changed files with 372 additions and 40 deletions

View File

@@ -22,6 +22,9 @@ import java.util.UUID;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID; import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
/**
* Constants used with Lefun device support
*/
public class LefunConstants { public class LefunConstants {
// BLE UUIDs // BLE UUIDs
public static final UUID UUID_SERVICE_LEFUN = UUID.fromString(String.format(BASE_UUID, "18D0")); public static final UUID UUID_SERVICE_LEFUN = UUID.fromString(String.format(BASE_UUID, "18D0"));
@@ -31,16 +34,12 @@ public class LefunConstants {
// Coordinator constants // Coordinator constants
public static final String ADVERTISEMENT_NAME = "Lefun"; public static final String ADVERTISEMENT_NAME = "Lefun";
public static final String MANUFACTURER_NAME = "Teng Jin Da"; public static final String MANUFACTURER_NAME = "Teng Jin Da";
public static int NUM_ALARM_SLOTS = 5;
// Commands // Commands
public static final byte CMD_REQUEST_ID = (byte)0xab; public static final byte CMD_REQUEST_ID = (byte) 0xab;
public static final byte CMD_RESPONSE_ID = 0x5a; public static final byte CMD_RESPONSE_ID = 0x5a;
public static final int CMD_MAX_LENGTH = 20; public static final int CMD_MAX_LENGTH = 20;
// 3 header bytes plus checksum // 3 header bytes plus checksum
public static final int CMD_HEADER_LENGTH = 4; public static final int CMD_HEADER_LENGTH = 4;
public static final byte CMD_FIRMWARE_INFO = 0x00; public static final byte CMD_FIRMWARE_INFO = 0x00;
public static final byte CMD_BONDING_REQUEST = 0x01; public static final byte CMD_BONDING_REQUEST = 0x01;
public static final byte CMD_SETTINGS = 0x02; public static final byte CMD_SETTINGS = 0x02;
@@ -67,25 +66,23 @@ public class LefunConstants {
public static final byte CMD_LANGUAGE = 0x21; public static final byte CMD_LANGUAGE = 0x21;
public static final byte CMD_UNKNOWN_22 = 0x22; public static final byte CMD_UNKNOWN_22 = 0x22;
public static final byte CMD_UNKNOWN_25 = 0x25; public static final byte CMD_UNKNOWN_25 = 0x25;
public static final byte CMD_UNKNOWN_80 = (byte)0x80; public static final byte CMD_UNKNOWN_80 = (byte) 0x80;
public static final int PPG_TYPE_INVALID = -1; public static final int PPG_TYPE_INVALID = -1;
public static final int PPG_TYPE_HEART_RATE = 0; public static final int PPG_TYPE_HEART_RATE = 0;
public static final int PPG_TYPE_BLOOD_PRESSURE = 1; public static final int PPG_TYPE_BLOOD_PRESSURE = 1;
public static final int PPG_TYPE_BLOOD_OXYGEN = 2; public static final int PPG_TYPE_BLOOD_OXYGEN = 2;
public static final int PPG_TYPE_COUNT = 3; public static final int PPG_TYPE_COUNT = 3;
// DB activity kinds // DB activity kinds
public static final int DB_ACTIVITY_KIND_UNKNOWN = 0; public static final int DB_ACTIVITY_KIND_UNKNOWN = 0;
public static final int DB_ACTIVITY_KIND_ACTIVITY = 1; public static final int DB_ACTIVITY_KIND_ACTIVITY = 1;
public static final int DB_ACTIVITY_KIND_HEART_RATE = 2; public static final int DB_ACTIVITY_KIND_HEART_RATE = 2;
public static final int DB_ACTIVITY_KIND_LIGHT_SLEEP = 3; public static final int DB_ACTIVITY_KIND_LIGHT_SLEEP = 3;
public static final int DB_ACTIVITY_KIND_DEEP_SLEEP = 4; public static final int DB_ACTIVITY_KIND_DEEP_SLEEP = 4;
// Pseudo-intensity // Pseudo-intensity
public static final int INTENSITY_MIN = 0; public static final int INTENSITY_MIN = 0;
public static final int INTENSITY_DEEP_SLEEP = 1; public static final int INTENSITY_DEEP_SLEEP = 1;
public static final int INTENSITY_LIGHT_SLEEP = 2; public static final int INTENSITY_LIGHT_SLEEP = 2;
public static final int INTENSITY_AWAKE = 3; public static final int INTENSITY_AWAKE = 3;
public static final int INTENSITY_MAX = 4; public static final int INTENSITY_MAX = 4;
public static int NUM_ALARM_SLOTS = 5;
} }

View File

@@ -41,6 +41,9 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants.
import static nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants.MANUFACTURER_NAME; import static nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants.MANUFACTURER_NAME;
import static nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants.NUM_ALARM_SLOTS; import static nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants.NUM_ALARM_SLOTS;
/**
* Device coordinator for Lefun band
*/
public class LefunDeviceCoordinator extends AbstractDeviceCoordinator { public class LefunDeviceCoordinator extends AbstractDeviceCoordinator {
@Override @Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {

View File

@@ -18,6 +18,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun; package nodomain.freeyourgadget.gadgetbridge.devices.lefun;
/**
* Feature support utilities for Lefun devices
*/
public class LefunFeatureSupport { public class LefunFeatureSupport {
public static final int SUPPORT_HEART_RATE = 1 << 2; public static final int SUPPORT_HEART_RATE = 1 << 2;
public static final int SUPPORT_BLOOD_PRESSURE = 1 << 3; public static final int SUPPORT_BLOOD_PRESSURE = 1 << 3;
@@ -31,10 +34,27 @@ public class LefunFeatureSupport {
public static final int RESERVE_WALLPAPER = 1 << 6; public static final int RESERVE_WALLPAPER = 1 << 6;
public static final int RESERVE_REMOTE_CAMERA = 1 << 7; public static final int RESERVE_REMOTE_CAMERA = 1 << 7;
/**
* Checks whether a feature is supported
*
* @param deviceSupport the feature flags from the device
* @param featureSupport the feature you want to check
* @return whether feature is supported
*/
public static boolean checkSupported(short deviceSupport, int featureSupport) { public static boolean checkSupported(short deviceSupport, int featureSupport) {
return (deviceSupport & featureSupport) == featureSupport; return (deviceSupport & featureSupport) == featureSupport;
} }
/**
* Checks whether a feature is not reserved
* <p>
* Reserve flags indicate a feature is not available if set. This function takes care of the
* inverting for you, so if you get true, the feature is available.
*
* @param deviceReserve the reserve flags from the device
* @param featureReserve the reserve flag you want to check
* @return whether feature is supported
*/
public static boolean checkNotReserved(short deviceReserve, int featureReserve) { public static boolean checkNotReserved(short deviceReserve, int featureReserve) {
return !((deviceReserve & featureReserve) == featureReserve); return !((deviceReserve & featureReserve) == featureReserve);
} }

View File

@@ -24,14 +24,15 @@ import androidx.annotation.Nullable;
import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.Property; import de.greenrobot.dao.Property;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.LefunActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
/**
* Sample provider for Lefun devices
*/
public class LefunSampleProvider extends AbstractSampleProvider<LefunActivitySample> { public class LefunSampleProvider extends AbstractSampleProvider<LefunActivitySample> {
public LefunSampleProvider(GBDevice device, DaoSession session) { public LefunSampleProvider(GBDevice device, DaoSession session) {
super(device, session); super(device, session);
@@ -91,7 +92,7 @@ public class LefunSampleProvider extends AbstractSampleProvider<LefunActivitySam
@Override @Override
public float normalizeIntensity(int rawIntensity) { public float normalizeIntensity(int rawIntensity) {
return rawIntensity / (float)LefunConstants.INTENSITY_MAX; return rawIntensity / (float) LefunConstants.INTENSITY_MAX;
} }
@Override @Override

View File

@@ -23,32 +23,28 @@ import java.nio.ByteOrder;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants; import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
/**
* Base class for Lefun Bluetooth commands and responses
*/
public abstract class BaseCommand { public abstract class BaseCommand {
// Common constants // Common constants
/**
* Common get operation type
*/
public static final byte OP_GET = 0; public static final byte OP_GET = 0;
/**
* Common set operation type
*/
public static final byte OP_SET = 1; public static final byte OP_SET = 1;
abstract protected void deserializeParams(byte id, ByteBuffer params); /**
abstract protected byte serializeParams(ByteBuffer params); * Calculates command checksum
*
public void deserialize(byte[] response) { * @param data the data to generate checksum from
if (response.length < LefunConstants.CMD_HEADER_LENGTH || response.length < response[1]) * @param offset the offset in data to start calculating from
throw new IllegalArgumentException("Response is too short"); * @param length the number of bytes to include in calculation
if (calculateChecksum(response, 0, response[1] - 1) != response[response[1] - 1]) * @return the computed checksum
throw new IllegalArgumentException("Incorrect message checksum"); */
ByteBuffer buffer = ByteBuffer.wrap(response, LefunConstants.CMD_HEADER_LENGTH - 1,
response[1] - LefunConstants.CMD_HEADER_LENGTH);
buffer.order(ByteOrder.BIG_ENDIAN);
deserializeParams(response[2], buffer);
}
public byte[] serialize() {
ByteBuffer buffer = ByteBuffer.allocate(LefunConstants.CMD_MAX_LENGTH - LefunConstants.CMD_HEADER_LENGTH);
buffer.order(ByteOrder.BIG_ENDIAN);
byte id = serializeParams(buffer);
return makeCommand(id, buffer);
}
public static byte calculateChecksum(byte[] data, int offset, int length) { public static byte calculateChecksum(byte[] data, int offset, int length) {
int checksum = 0; int checksum = 0;
for (int i = offset; i < offset + length; ++i) { for (int i = offset; i < offset + length; ++i) {
@@ -62,7 +58,51 @@ public abstract class BaseCommand {
b >>= 1; b >>= 1;
} }
} }
return (byte)checksum; return (byte) checksum;
}
/**
* When implemented in a subclass, parses the response from a device
*
* @param id the command ID
* @param params the params buffer
*/
abstract protected void deserializeParams(byte id, ByteBuffer params);
/**
* When implemented in a subclass, provides the arguments to send in the command
*
* @param params the params buffer to write to
* @return the command ID
*/
abstract protected byte serializeParams(ByteBuffer params);
/**
* Deserialize a response from the device
*
* @param response the response data to deserialize
*/
public void deserialize(byte[] response) {
if (response.length < LefunConstants.CMD_HEADER_LENGTH || response.length < response[1])
throw new IllegalArgumentException("Response is too short");
if (calculateChecksum(response, 0, response[1] - 1) != response[response[1] - 1])
throw new IllegalArgumentException("Incorrect message checksum");
ByteBuffer buffer = ByteBuffer.wrap(response, LefunConstants.CMD_HEADER_LENGTH - 1,
response[1] - LefunConstants.CMD_HEADER_LENGTH);
buffer.order(ByteOrder.BIG_ENDIAN);
deserializeParams(response[2], buffer);
}
/**
* Serializes a command to send to the device
*
* @return the data to send to the device
*/
public byte[] serialize() {
ByteBuffer buffer = ByteBuffer.allocate(LefunConstants.CMD_MAX_LENGTH - LefunConstants.CMD_HEADER_LENGTH);
buffer.order(ByteOrder.BIG_ENDIAN);
byte id = serializeParams(buffer);
return makeCommand(id, buffer);
} }
/** /**
@@ -87,25 +127,57 @@ public abstract class BaseCommand {
return request; return request;
} }
/**
* Throws a standard parameters length exception
*/
protected void throwUnexpectedLength() { protected void throwUnexpectedLength() {
throw new IllegalArgumentException("Unexpected parameters length"); throw new IllegalArgumentException("Unexpected parameters length");
} }
/**
* Checks for valid command ID and throws if wrong ID provided
*
* @param id command ID from device
* @param expectedId expected command ID
*/
protected void validateId(byte id, byte expectedId) { protected void validateId(byte id, byte expectedId) {
if (id != expectedId) if (id != expectedId)
throw new IllegalArgumentException("Wrong command ID"); throw new IllegalArgumentException("Wrong command ID");
} }
/**
* Checks for valid command ID and command length
*
* @param id command ID from device
* @param params params buffer from device
* @param expectedId expected command ID
* @param expectedLength expected params length
*/
protected void validateIdAndLength(byte id, ByteBuffer params, byte expectedId, int expectedLength) { protected void validateIdAndLength(byte id, ByteBuffer params, byte expectedId, int expectedLength) {
validateId(id, expectedId); validateId(id, expectedId);
if (params.limit() - params.position() != expectedLength) if (params.limit() - params.position() != expectedLength)
throwUnexpectedLength(); throwUnexpectedLength();
} }
/**
* Gets whether a bit is set
*
* @param value the value to check against
* @param mask the bitmask
* @return whether the bits indicated by the bitmask are set
*/
protected boolean getBit(int value, int mask) { protected boolean getBit(int value, int mask) {
return (value & mask) != 0; return (value & mask) != 0;
} }
/**
* Sets a bit in a value
*
* @param value the value to modify
* @param mask the bitmask
* @param set whether to set or clear the bits
* @return the modified value
*/
protected int setBit(int value, int mask, boolean set) { protected int setBit(int value, int mask, boolean set) {
if (set) { if (set) {
return value | mask; return value | mask;
@@ -114,26 +186,43 @@ public abstract class BaseCommand {
} }
} }
/**
* Sets a bit in a value
*
* @param value the value to modify
* @param mask the bitmask
* @param set whether to set or clear the bits
* @return the modified value
*/
protected short setBit(short value, int mask, boolean set) { protected short setBit(short value, int mask, boolean set) {
if (set) { if (set) {
return (short)(value | mask); return (short) (value | mask);
} else { } else {
return (short)(value & ~mask); return (short) (value & ~mask);
} }
} }
/**
* Sets a bit in a value
*
* @param value the value to modify
* @param mask the bitmask
* @param set whether to set or clear the bits
* @return the modified value
*/
protected byte setBit(byte value, int mask, boolean set) { protected byte setBit(byte value, int mask, boolean set) {
if (set) { if (set) {
return (byte)(value | mask); return (byte) (value | mask);
} else { } else {
return (byte)(value & ~mask); return (byte) (value & ~mask);
} }
} }
/** /**
* Find index of first bit that is set * Find index of first bit that is set
* @param value *
* @return * @param value the value to look at
* @return the index of the lowest set bit, starting at 0 for least significant bit; -1 if no bits set
*/ */
protected int getLowestSetBitIndex(int value) { protected int getLowestSetBitIndex(int value) {
if (value == 0) return -1; if (value == 0) return -1;

View File

@@ -102,6 +102,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.Start
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
/**
* Device support class for Lefun devices
*/
public class LefunDeviceSupport extends AbstractBTLEDeviceSupport { public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(LefunDeviceSupport.class); private static final Logger LOG = LoggerFactory.getLogger(LefunDeviceSupport.class);
@@ -111,6 +114,9 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
private int lastStepsCount = -1; private int lastStepsCount = -1;
private int lastStepsTimestamp; private int lastStepsTimestamp;
/**
* Instantiates a new instance of LefunDeviceSupport
*/
public LefunDeviceSupport() { public LefunDeviceSupport() {
super(LOG); super(LOG);
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
@@ -449,6 +455,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Sends unit of measurement to the device
*
* @param builder the transaction builder to append to
*/
private void sendUnitsSetting(TransactionBuilder builder) { private void sendUnitsSetting(TransactionBuilder builder) {
Prefs prefs = GBApplication.getPrefs(); Prefs prefs = GBApplication.getPrefs();
String units = prefs.getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, String units = prefs.getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM,
@@ -463,6 +474,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
sendGeneralSettings(builder, (byte) 0xff, lefunUnits); sendGeneralSettings(builder, (byte) 0xff, lefunUnits);
} }
/**
* Gets a features command with the currently enabled features set
*
* @return the features command
*/
private FeaturesCommand getCurrentEnabledFeatures() { private FeaturesCommand getCurrentEnabledFeatures() {
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
boolean raiseToWakeEnabled = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_LIFTWRIST_NOSHED, true); boolean raiseToWakeEnabled = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_LIFTWRIST_NOSHED, true);
@@ -479,6 +495,13 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
return cmd; return cmd;
} }
/**
* Sends general settings to the device
*
* @param builder the transaction builder to append to
* @param amPm AM/PM indicator setting
* @param units units of measurement setting
*/
private void sendGeneralSettings(TransactionBuilder builder, byte amPm, byte units) { private void sendGeneralSettings(TransactionBuilder builder, byte amPm, byte units) {
boolean givenBuilder = builder != null; boolean givenBuilder = builder != null;
try { try {
@@ -497,6 +520,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Sends the user profile to the device
*
* @param builder the transaction builder to append to
*/
private void sendUserProfile(TransactionBuilder builder) { private void sendUserProfile(TransactionBuilder builder) {
boolean givenBuilder = builder != null; boolean givenBuilder = builder != null;
try { try {
@@ -515,6 +543,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Sends enabled features settings to the device
*
* @param cmd the features command to send
*/
private void sendEnabledFeaturesSetting(FeaturesCommand cmd) { private void sendEnabledFeaturesSetting(FeaturesCommand cmd) {
try { try {
TransactionBuilder builder = performInitialized(SetEnabledFeaturesRequest.class.getSimpleName()); TransactionBuilder builder = performInitialized(SetEnabledFeaturesRequest.class.getSimpleName());
@@ -529,6 +562,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Sends the sedentary reminder interval setting to the device
*
* @param period the reminder interval
*/
private void sendSedentaryReminderIntervalSetting(int period) { private void sendSedentaryReminderIntervalSetting(int period) {
try { try {
TransactionBuilder builder = performInitialized(SetSedentaryReminderIntervalRequest.class.getSimpleName()); TransactionBuilder builder = performInitialized(SetSedentaryReminderIntervalRequest.class.getSimpleName());
@@ -543,6 +581,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Sends the hydration reminder interval setting to the device
*
* @param period the reminder interval
*/
private void sendHydrationReminderIntervalSetting(int period) { private void sendHydrationReminderIntervalSetting(int period) {
try { try {
TransactionBuilder builder = performInitialized(SetHydrationReminderIntervalRequest.class.getSimpleName()); TransactionBuilder builder = performInitialized(SetHydrationReminderIntervalRequest.class.getSimpleName());
@@ -557,6 +600,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Sends the language selection to the device
*
* @param language the language selection
*/
private void sendLanguageSetting(byte language) { private void sendLanguageSetting(byte language) {
try { try {
TransactionBuilder builder = performInitialized(SetLanguageRequest.class.getSimpleName()); TransactionBuilder builder = performInitialized(SetLanguageRequest.class.getSimpleName());
@@ -571,6 +619,12 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Stores received general settings to prefs
*
* @param amPm AM/PM indicator setting
* @param units units of measurement setting
*/
public void receiveGeneralSettings(int amPm, int units) { public void receiveGeneralSettings(int amPm, int units) {
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
boolean ampmEnabled = amPm == SettingsCommand.AM_PM_12_HOUR; boolean ampmEnabled = amPm == SettingsCommand.AM_PM_12_HOUR;
@@ -579,6 +633,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
.apply(); .apply();
} }
/**
* Stores received enabled features settings to prefs
*
* @param cmd the features command
*/
public void receiveEnabledFeaturesSetting(FeaturesCommand cmd) { public void receiveEnabledFeaturesSetting(FeaturesCommand cmd) {
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
prefs.edit() prefs.edit()
@@ -593,6 +652,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
.apply(); .apply();
} }
/**
* Stores received sedentary reminder interval setting to prefs
*
* @param period the interval
*/
public void receiveSedentaryReminderIntervalSetting(int period) { public void receiveSedentaryReminderIntervalSetting(int period) {
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
prefs.edit() prefs.edit()
@@ -600,6 +664,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
.apply(); .apply();
} }
/**
* Stores received hydration reminder interval setting to prefs
*
* @param period the interval
*/
public void receiveHydrationReminderIntervalSetting(int period) { public void receiveHydrationReminderIntervalSetting(int period) {
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
prefs.edit() prefs.edit()
@@ -689,6 +758,13 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
return super.onCharacteristicChanged(gatt, characteristic); return super.onCharacteristicChanged(gatt, characteristic);
} }
/**
* Handles commands from the device that are not typically associated with a request
*
* @param commandId the command ID
* @param data the entire response
* @return whether the response has been handled
*/
private boolean handleAsynchronousResponse(byte commandId, byte[] data) { private boolean handleAsynchronousResponse(byte commandId, byte[] data) {
// Assume data already checked for correct response code and length // Assume data already checked for correct response code and length
switch (commandId) { switch (commandId) {
@@ -702,6 +778,12 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
return false; return false;
} }
/**
* Handles live steps data
*
* @param data the response
* @return whether the response has been handled
*/
private boolean handleAsynchronousActivity(byte[] data) { private boolean handleAsynchronousActivity(byte[] data) {
try { try {
GetStepsDataCommand cmd = new GetStepsDataCommand(); GetStepsDataCommand cmd = new GetStepsDataCommand();
@@ -715,6 +797,12 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
// Adapted from nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3.MakibesHR3DeviceSupport.broadcastSample // Adapted from nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3.MakibesHR3DeviceSupport.broadcastSample
/**
* Broadcasts live sample
*
* @param command the steps data
*/
private void broadcastSample(GetStepsDataCommand command) { private void broadcastSample(GetStepsDataCommand command) {
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
int timestamp = (int) (now.getTimeInMillis() / 1000); int timestamp = (int) (now.getTimeInMillis() / 1000);
@@ -735,6 +823,12 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
} }
/**
* Handles PPG result from earlier request
*
* @param data the response
* @return whether the response has been handled
*/
private boolean handleAsynchronousPpgResult(byte[] data) { private boolean handleAsynchronousPpgResult(byte[] data) {
try { try {
PpgResultCommand cmd = new PpgResultCommand(); PpgResultCommand cmd = new PpgResultCommand();
@@ -747,6 +841,12 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Handles find phone request
*
* @param data the response
* @return whether the response has been handled
*/
private boolean handleAntiLoss(byte[] data) { private boolean handleAntiLoss(byte[] data) {
try { try {
FindPhoneCommand cmd = new FindPhoneCommand(); FindPhoneCommand cmd = new FindPhoneCommand();
@@ -761,12 +861,26 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Callback when device info has been obtained
*/
public void completeInitialization() { public void completeInitialization() {
gbDevice.setState(GBDevice.State.INITIALIZED); gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext()); gbDevice.sendDeviceUpdateIntent(getContext());
onReadConfiguration(""); onReadConfiguration("");
} }
/**
* Converts Lefun datetime format to Unix timestamp
*
* @param year the year (2 digits based on 2000)
* @param month the month
* @param day the day
* @param hour the hour
* @param minute the minute
* @param second the second
* @return Unix timestamp of the datetime
*/
private int dateToTimestamp(byte year, byte month, byte day, byte hour, byte minute, byte second) { private int dateToTimestamp(byte year, byte month, byte day, byte hour, byte minute, byte second) {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
calendar.set( calendar.set(
@@ -780,6 +894,13 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
return (int) (calendar.getTimeInMillis() / 1000); return (int) (calendar.getTimeInMillis() / 1000);
} }
/**
* Fetches an activity sample given the timestamp
*
* @param session DAO session
* @param timestamp the timestamp
* @return fetched activity or null if none exists
*/
private LefunActivitySample getActivitySample(DaoSession session, int timestamp) { private LefunActivitySample getActivitySample(DaoSession session, int timestamp) {
LefunActivitySampleDao dao = session.getLefunActivitySampleDao(); LefunActivitySampleDao dao = session.getLefunActivitySampleDao();
Long userId = DBHelper.getUser(session).getId(); Long userId = DBHelper.getUser(session).getId();
@@ -792,6 +913,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
return q.unique(); return q.unique();
} }
/**
* Processes activity data and stores it
*
* @param command the activity data
*/
public void handleActivityData(GetActivityDataCommand command) { public void handleActivityData(GetActivityDataCommand command) {
try (DBHandler handler = GBApplication.acquireDB()) { try (DBHandler handler = GBApplication.acquireDB()) {
DaoSession session = handler.getDaoSession(); DaoSession session = handler.getDaoSession();
@@ -818,6 +944,13 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Processes PPG data and stores it
*
* @param timestamp the timestamp
* @param ppgType the PPG type
* @param ppgData the data from the PPG operation
*/
private void handlePpgData(int timestamp, int ppgType, byte[] ppgData) { private void handlePpgData(int timestamp, int ppgType, byte[] ppgData) {
int ppgData0 = ppgData[0] & 0xff; int ppgData0 = ppgData[0] & 0xff;
int ppgData1 = ppgData.length > 1 ? ppgData[1] & 0xff : 0; int ppgData1 = ppgData.length > 1 ? ppgData[1] & 0xff : 0;
@@ -851,6 +984,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Processes PPG data from bulk get operation
*
* @param command the PPG data
*/
public void handlePpgData(GetPpgDataCommand command) { public void handlePpgData(GetPpgDataCommand command) {
int timestamp = dateToTimestamp(command.getYear(), command.getMonth(), command.getDay(), int timestamp = dateToTimestamp(command.getYear(), command.getMonth(), command.getDay(),
command.getHour(), command.getMinute(), command.getSecond()); command.getHour(), command.getMinute(), command.getSecond());
@@ -859,6 +997,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
handlePpgData(timestamp, ppgType, ppgData); handlePpgData(timestamp, ppgType, ppgData);
} }
/**
* Processes PPG result received as a result of requesting PPG operation
*
* @param command the PPG result
*/
public void handlePpgData(PpgResultCommand command) { public void handlePpgData(PpgResultCommand command) {
int timestamp = (int) (Calendar.getInstance().getTimeInMillis() / 1000); int timestamp = (int) (Calendar.getInstance().getTimeInMillis() / 1000);
int ppgType = command.getPpgType(); int ppgType = command.getPpgType();
@@ -866,6 +1009,11 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
handlePpgData(timestamp, ppgType, ppgData); handlePpgData(timestamp, ppgType, ppgData);
} }
/**
* Processes bulk sleep data
*
* @param command the sleep data
*/
public void handleSleepData(GetSleepDataCommand command) { public void handleSleepData(GetSleepDataCommand command) {
try (DBHandler handler = GBApplication.acquireDB()) { try (DBHandler handler = GBApplication.acquireDB()) {
DaoSession session = handler.getDaoSession(); DaoSession session = handler.getDaoSession();
@@ -915,6 +1063,9 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
} }
} }
/**
* Runs the next queued request
*/
public void runNextQueuedRequest() { public void runNextQueuedRequest() {
Request request = queuedRequests.poll(); Request request = queuedRequests.poll();
if (request != null) { if (request != null) {

View File

@@ -31,7 +31,14 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSup
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.OperationStatus;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
/**
* Represents a request that receives several responses
*/
public abstract class MultiFetchRequest extends Request { public abstract class MultiFetchRequest extends Request {
/**
* Instantiates a new MultiFetchRequest
* @param support the device support
*/
protected MultiFetchRequest(LefunDeviceSupport support) { protected MultiFetchRequest(LefunDeviceSupport support) {
super(support, null); super(support, null);
removeAfterHandling = false; removeAfterHandling = false;
@@ -97,5 +104,9 @@ public abstract class MultiFetchRequest extends Request {
return true; return true;
} }
/**
* Gets the display operation name
* @return the operation name
*/
protected abstract String getOperationName(); protected abstract String getOperationName();
} }

View File

@@ -34,16 +34,31 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.Op
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
// Ripped from nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Request // Ripped from nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Request
/**
* Basic request for operations with Lefun devices
*/
public abstract class Request extends AbstractBTLEOperation<LefunDeviceSupport> { public abstract class Request extends AbstractBTLEOperation<LefunDeviceSupport> {
protected TransactionBuilder builder; protected TransactionBuilder builder;
protected boolean removeAfterHandling = true; protected boolean removeAfterHandling = true;
private Logger logger = (Logger) LoggerFactory.getLogger(getName()); private Logger logger = (Logger) LoggerFactory.getLogger(getName());
/**
* Instantiates Request
*
* @param support the device support
* @param builder the transaction builder to use
*/
protected Request(LefunDeviceSupport support, TransactionBuilder builder) { protected Request(LefunDeviceSupport support, TransactionBuilder builder) {
super(support); super(support);
this.builder = builder; this.builder = builder;
} }
/**
* Gets the transaction builder
*
* @return the transaction builder
*/
public TransactionBuilder getTransactionBuilder() { public TransactionBuilder getTransactionBuilder() {
return builder; return builder;
} }
@@ -57,36 +72,81 @@ public abstract class Request extends AbstractBTLEOperation<LefunDeviceSupport>
getSupport().performConnected(builder.getTransaction()); getSupport().performConnected(builder.getTransaction());
} }
/**
* When implemented in a subclass, provides the request bytes to send to the device
*
* @return the request bytes
*/
public abstract byte[] createRequest(); public abstract byte[] createRequest();
/**
* When overridden in a subclass, handles the response to the current command
*
* @param data the response data
*/
public void handleResponse(byte[] data) { public void handleResponse(byte[] data) {
operationStatus = OperationStatus.FINISHED; operationStatus = OperationStatus.FINISHED;
} }
/**
* Gets the class name of this instance
*
* @return the class name
*/
public String getName() { public String getName() {
Class thisClass = getClass(); Class thisClass = getClass();
while (thisClass.isAnonymousClass()) thisClass = thisClass.getSuperclass(); while (thisClass.isAnonymousClass()) thisClass = thisClass.getSuperclass();
return thisClass.getSimpleName(); return thisClass.getSimpleName();
} }
/**
* Logs a debug message
*
* @param message the message to log
*/
protected void log(String message) { protected void log(String message) {
logger.debug(message); logger.debug(message);
} }
/**
* When implemented in a subclass, returns the command ID associated with the current request
*
* @return the command ID
*/
public abstract int getCommandId(); public abstract int getCommandId();
/**
* Gets whether the request will queue itself
*
* @return whether the request is self-queuing
*/
public boolean isSelfQueue() { public boolean isSelfQueue() {
return false; return false;
} }
/**
* Gets whether the request expects a response
*
* @return whether the request expects a response
*/
public boolean expectsResponse() { public boolean expectsResponse() {
return true; return true;
} }
/**
* Gets whether the response should be removed from in progress requests list after handling
*
* @return whether the response should be removed after handling
*/
public boolean shouldRemoveAfterHandling() { public boolean shouldRemoveAfterHandling() {
return removeAfterHandling; return removeAfterHandling;
} }
/**
* Reports an error to the user
*
* @param message the message to show
*/
protected void reportFailure(String message) { protected void reportFailure(String message) {
GB.toast(getContext(), message, Toast.LENGTH_SHORT, GB.ERROR); GB.toast(getContext(), message, Toast.LENGTH_SHORT, GB.ERROR);
} }