Moyoung: Code and settings improvements

This commit is contained in:
Arjan Schrijver 2024-08-30 21:50:17 +02:00
parent bff6fcca56
commit 487afa6814
7 changed files with 91 additions and 88 deletions

View File

@ -23,13 +23,18 @@ import android.os.ParcelUuid;
import androidx.annotation.NonNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.moyoung.samples.MoyoungActivitySampleProvider;
@ -58,7 +63,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.moyoung.MoyoungDeviceSupport;
public abstract class AbstractMoyoungDeviceCoordinator extends AbstractDeviceCoordinator {
public abstract class AbstractMoyoungDeviceCoordinator extends AbstractBLEDeviceCoordinator {
@NonNull
@Override
@ -160,11 +165,12 @@ public abstract class AbstractMoyoungDeviceCoordinator extends AbstractDeviceCoo
return true;
}
private static final MoyoungSetting[] MOYOUNG_SETTINGS = new MoyoungSetting[] {
private static final MoyoungSetting[] MOYOUNG_SETTINGS = {
new MoyoungSettingUserInfo("USER_INFO", MoyoungConstants.CMD_SET_USER_INFO),
new MoyoungSettingByte("STEP_LENGTH", (byte)-1, MoyoungConstants.CMD_SET_STEP_LENGTH),
// (*) new MoyoungSettingEnum<>("DOMINANT_HAND", MoyoungConstants.CMD_QUERY_DOMINANT_HAND, MoyoungConstants.CMD_SET_DOMINANT_HAND, MoyoungEnumDominantHand.class),
new MoyoungSettingInt("GOAL_STEP", MoyoungConstants.CMD_QUERY_GOAL_STEP, MoyoungConstants.CMD_SET_GOAL_STEP),
new MoyoungSettingByte("HR_AUTO_INTERVAL", MoyoungConstants.CMD_QUERY_TIMING_MEASURE_HEART_RATE, MoyoungConstants.CMD_SET_TIMING_MEASURE_HEART_RATE),
new MoyoungSettingEnum<>("DEVICE_VERSION", MoyoungConstants.CMD_QUERY_DEVICE_VERSION, MoyoungConstants.CMD_SET_DEVICE_VERSION, MoyoungEnumDeviceVersion.class),
new MoyoungSettingLanguage("DEVICE_LANGUAGE", MoyoungConstants.CMD_QUERY_DEVICE_LANGUAGE, MoyoungConstants.CMD_SET_DEVICE_LANGUAGE),
@ -187,24 +193,43 @@ public abstract class AbstractMoyoungDeviceCoordinator extends AbstractDeviceCoo
new MoyoungSettingBool("BREATHING_LIGHT", MoyoungConstants.CMD_QUERY_BREATHING_LIGHT, MoyoungConstants.CMD_SET_BREATHING_LIGHT)
};
@Override
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
return new int[]{
//R.xml.devicesettings_steplength, // TODO is this needed? does it work? write-only so hard to tell
R.xml.devicesettings_moyoung_device_version,
R.xml.devicesettings_moyoung_language,
R.xml.devicesettings_timeformat,
R.xml.devicesettings_measurementsystem,
R.xml.devicesettings_moyoung_watchface,
//R.xml.devicesettings_moyoung_othermessage, // not implemented because this doesn't really do anything on the watch side, only enables/disables sending of "other" notifications in the app (no idea why they store the setting on the watch)
R.xml.devicesettings_liftwrist_display,
R.xml.devicesettings_moyoung_sedentary_reminder,
R.xml.devicesettings_donotdisturb_no_auto,
//R.xml.devicesettings_moyoung_breathinglight, // No idea what this does but it doesn't seem to change anything
R.xml.devicesettings_world_clocks,
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
final List<Integer> generic = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.GENERIC);
generic.add(R.xml.devicesettings_moyoung_device_version);
generic.add(R.xml.devicesettings_colmi_r0x);
generic.add(R.xml.devicesettings_timeformat);
generic.add(R.xml.devicesettings_measurementsystem);
generic.add(R.xml.devicesettings_moyoung_watchface);
generic.add(R.xml.devicesettings_liftwrist_display);
generic.add(R.xml.devicesettings_moyoung_sedentary_reminder);
generic.add(R.xml.devicesettings_donotdisturb_no_auto);
generic.add(R.xml.devicesettings_world_clocks);
generic.add(R.xml.devicesettings_sync_calendar);
return deviceSpecificSettings;
}
@Override
public String[] getSupportedLanguageSettings(GBDevice device) {
return new String[]{
"en_US",
"nl_NL",
};
}
@Override
public List<HeartRateCapability.MeasurementInterval> getHeartRateMeasurementIntervals() {
return Arrays.asList(
HeartRateCapability.MeasurementInterval.OFF,
HeartRateCapability.MeasurementInterval.MINUTES_5,
HeartRateCapability.MeasurementInterval.MINUTES_10,
HeartRateCapability.MeasurementInterval.MINUTES_15,
HeartRateCapability.MeasurementInterval.MINUTES_30
);
}
public MoyoungSetting[] getSupportedSettings() {
return MOYOUNG_SETTINGS;
}

View File

@ -145,6 +145,12 @@ public class MoyoungConstants {
public static final byte CMD_SET_TIMING_MEASURE_HEART_RATE = 31; // (*) {i}, i >= 0, 0 is disabled
public static final byte CMD_START_STOP_MEASURE_DYNAMIC_RATE = 104; // (*) {enabled ? 0 : -1}
public static final byte HR_INTERVAL_OFF = 0;
public static final byte HR_INTERVAL_5MIN = 1;
public static final byte HR_INTERVAL_10MIN = 2;
public static final byte HR_INTERVAL_20MIN = 4;
public static final byte HR_INTERVAL_30MIN = 6;
public static final byte CMD_TRIGGER_MEASURE_BLOOD_PRESSURE = 105; // (?) {0, 0, 0} to start, {-1, -1, -1} to stop -> {unused?, num1, num2}
public static final byte CMD_TRIGGER_MEASURE_BLOOD_OXYGEN = 107; // (?) {start ? 0 : -1} -> {num}
public static final byte CMD_TRIGGER_MEASURE_HEARTRATE = 109; // {start ? 0 : -1} -> {bpm}

View File

@ -42,8 +42,8 @@ public class MoyoungSettingEnum<T extends Enum <?> & MoyoungEnum> extends Moyoun
@Override
public T decode(byte[] data) {
if (data.length != 1)
throw new IllegalArgumentException("Wrong data length, should be 1, was " + data.length);
if (data.length < 1)
throw new IllegalArgumentException("Wrong data length, should be at least 1, was " + data.length);
return findByValue(data[0]);
}

View File

@ -18,18 +18,25 @@ package nodomain.freeyourgadget.gadgetbridge.devices.moyoung.settings;
import android.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.service.devices.moyoung.QuerySettingsOperation;
public class MoyoungSettingLanguage extends MoyoungSettingEnum<MoyoungEnumLanguage> {
private static final Logger LOG = LoggerFactory.getLogger(MoyoungSettingLanguage.class);
public MoyoungSettingLanguage(String name, byte cmdQuery, byte cmdSet) {
super(name, cmdQuery, cmdSet, MoyoungEnumLanguage.class);
}
private Pair<MoyoungEnumLanguage, MoyoungEnumLanguage[]> decodeData(byte[] data) {
if (data.length != 5)
throw new IllegalArgumentException("Wrong data length, should be 5, was " + data.length);
if (data.length < 5)
throw new IllegalArgumentException("Wrong data length, should be at least 5, was " + data.length);
byte[] current = new byte[] { data[0] };
byte[] supported = new byte[] { data[1], data[2], data[3], data[4] };
@ -48,6 +55,7 @@ public class MoyoungSettingLanguage extends MoyoungSettingEnum<MoyoungEnumLangua
}
MoyoungEnumLanguage[] supportedLanguagesArr = new MoyoungEnumLanguage[supportedLanguages.size()];
LOG.debug("Supported languages: {}", supportedLanguages);
return Pair.create(currentLanguage, supportedLanguages.toArray(supportedLanguagesArr));
}

View File

@ -212,6 +212,14 @@ public class BLETypeConversions {
return (short) (bytes[0] & 0xff | ((bytes[1] & 0xff) << 8));
}
public static int toUint24(byte... bytes) {
return (bytes[0] & 0xff) | ((bytes[1] & 0xff) << 8) | ((bytes[2] & 0xff) << 16);
}
public static int toUint24(byte[] bytes, int offset) {
return (bytes[offset + 0] & 0xff) | ((bytes[offset + 1] & 0xff) << 8) | ((bytes[offset + 2] & 0xff) << 16);
}
public static int toUint32(byte... bytes) {
return (bytes[0] & 0xff) | ((bytes[1] & 0xff) << 8) | ((bytes[2] & 0xff) << 16) | ((bytes[3] & 0xff) << 24);
}

View File

@ -92,6 +92,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
@ -117,20 +118,17 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
private final HeartRateProfile<MoyoungDeviceSupport> heartRateProfile;
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
private final IntentListener mListener = new IntentListener() {
@Override
public void notify(Intent intent) {
String s = intent.getAction();
if (Objects.equals(s, DeviceInfoProfile.ACTION_DEVICE_INFO)) {
handleDeviceInfo((DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO));
}
if (Objects.equals(s, BatteryInfoProfile.ACTION_BATTERY_INFO)) {
handleBatteryInfo((BatteryInfo) intent.getParcelableExtra(BatteryInfoProfile.EXTRA_BATTERY_INFO));
}
private final IntentListener mListener = intent -> {
String s = intent.getAction();
if (Objects.equals(s, DeviceInfoProfile.ACTION_DEVICE_INFO)) {
handleDeviceInfo(intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO));
}
if (Objects.equals(s, BatteryInfoProfile.ACTION_BATTERY_INFO)) {
handleBatteryInfo(intent.getParcelableExtra(BatteryInfoProfile.EXTRA_BATTERY_INFO));
}
};
private Handler idleUpdateHandler = new Handler();
private final Handler idleUpdateHandler = new Handler();
private int mtu = 20;
private MoyoungPacketIn packetIn = new MoyoungPacketIn();
@ -437,30 +435,6 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
return false;
}
private void addGBActivitySample(MoyoungActivitySample sample) {
addGBActivitySamples(new MoyoungActivitySample[] { sample });
}
private void addGBActivitySamples(MoyoungActivitySample[] samples) {
try (DBHandler dbHandler = GBApplication.acquireDB()) {
User user = DBHelper.getUser(dbHandler.getDaoSession());
Device device = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession());
MoyoungActivitySampleProvider provider = new MoyoungActivitySampleProvider(getDevice(), dbHandler.getDaoSession());
for (MoyoungActivitySample sample : samples) {
sample.setDevice(device);
sample.setUser(user);
sample.setProvider(provider);
provider.addGBActivitySample(sample);
}
} catch (Exception ex) {
LOG.error("Error saving samples: ", ex);
GB.toast(getContext(), "Error saving samples: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
GB.updateTransferNotification(null, "Data transfer failed", false, 0, getContext());
}
}
private void broadcastSample(MoyoungActivitySample sample) {
Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES)
.putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample)
@ -659,12 +633,6 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
}
}
private static int BytesToInt24(byte[] bArr) {
if (bArr.length != 3)
throw new IllegalArgumentException();
return ((bArr[2] << 24) >>> 8) | ((bArr[1] << 8) & 0xFF00) | (bArr[0] & 0xFF);
}
private Runnable updateIdleStepsRunnable = new Runnable() {
@Override
public void run() {
@ -777,13 +745,13 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
byte[] bArr2 = new byte[3];
System.arraycopy(data, 0, bArr2, 0, 3);
int steps = BytesToInt24(bArr2);
int steps = BLETypeConversions.toUint24(bArr2);
System.arraycopy(data, 3, bArr2, 0, 3);
int distance = BytesToInt24(bArr2);
int distance = BLETypeConversions.toUint24(bArr2);
System.arraycopy(data, 6, bArr2, 0, 3);
int calories = BytesToInt24(bArr2);
int calories = BLETypeConversions.toUint24(bArr2);
LOG.info("steps[" + daysAgo + "] steps=" + steps + ", distance=" + distance + ", calories=" + calories);
LOG.info("steps[{}] steps={}, distance={}, calories={}", daysAgo, steps, distance, calories);
try (DBHandler dbHandler = GBApplication.acquireDB()) {
User user = DBHelper.getUser(dbHandler.getDaoSession());
@ -833,7 +801,7 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
if (newSteps < 0 || newDistance < 0 || newCalories < 0)
{
LOG.warn("Ignoring a sample that would generate negative values: steps += " + newSteps + ", distance +=" + newDistance + ", calories += " + newCalories);
LOG.warn("Ignoring a sample that would generate negative values: steps += {}, distance +={}, calories += {}", newSteps, newDistance, newCalories);
}
else if (newSteps != 0 || newDistance != 0 || newCalories != 0 || daysAgo == 0)
{
@ -857,7 +825,7 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
broadcastSample(sample);
}
LOG.info("Adding a sample: " + sample.toString());
LOG.info("Adding a sample: {}", sample);
}
} catch (Exception ex) {
LOG.error("Error saving samples: ", ex);
@ -1084,22 +1052,6 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
}
}
private void addGBActivitySampleIfNotExists(MoyoungActivitySampleProvider provider, MoyoungActivitySample sample)
{
boolean alreadyHaveThisSample = false;
for (MoyoungActivitySample sample2 : provider.getAllActivitySamples(sample.getTimestamp() - 1, sample.getTimestamp() + 1))
{
if (sample2.getTimestamp() == sample2.getTimestamp() && sample2.getRawKind() == sample.getRawKind())
alreadyHaveThisSample = true;
}
if (!alreadyHaveThisSample)
{
provider.addGBActivitySample(sample);
LOG.info("Adding a sample: " + sample.toString());
}
}
@Override
public void onReset(int flags) {
// TODO: this shuts down the watch, rather than rebooting it - perhaps add a new operation type?
@ -1231,7 +1183,7 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
public void onSendConfiguration(String config) {
LOG.info("Send configuration: " + config);
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()));
Prefs prefs = getDevicePrefs();
switch (config) {
case ActivityUser.PREF_USER_HEIGHT_CM:
case ActivityUser.PREF_USER_WEIGHT_KG:
@ -1433,7 +1385,7 @@ public class MoyoungDeviceSupport extends AbstractBTLEDeviceSupport {
public void onReadConfigurationDone(MoyoungSetting setting, Object value, byte[] data)
{
LOG.info("CONFIG " + setting.name + " = " + value);
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()));
Prefs prefs = getDevicePrefs();
Map<String, String> changedProperties = new ArrayMap<>();
SharedPreferences.Editor prefsEditor = prefs.getPreferences().edit();
switch (setting.name) {

View File

@ -111,8 +111,12 @@ public class QuerySettingsOperation extends AbstractBTLEOperation<MoyoungDeviceS
continue;
if (setting.cmdQuery == packetType)
{
Object value = setting.decode(payload);
LOG.info("SETTING QUERY " + setting.name + " = " + value.toString());
try {
Object value = setting.decode(payload);
LOG.info("SETTING QUERY " + setting.name + " = " + value.toString());
} catch (Exception e) {
LOG.error("Parse error in packet for setting " + setting.name + ": ", e);
}
received[i] = true;
handled = true;
}