From 8d1a1c07b72917a627b95c41fb38267cdffbd207 Mon Sep 17 00:00:00 2001 From: dakhnod Date: Thu, 6 Jan 2022 13:31:00 +0100 Subject: [PATCH] fossilhr-activity-recognition (#2539) This PR adds the ability to change activity recognition settings on the watch. Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2539 Co-authored-by: dakhnod Co-committed-by: dakhnod --- .../DeviceSettingsPreferenceConst.java | 5 + .../DeviceSpecificSettingsFragment.java | 8 + .../fossil_hr/FossilHRWatchAdapter.java | 46 ++++-- .../ConfigurationPutRequest.java | 146 +++++++++++++++--- .../ConfigurationGetRequest.java | 4 + app/src/main/res/values/arrays.xml | 12 ++ app/src/main/res/values/strings.xml | 10 ++ .../res/xml/devicesettings_fossilhybridhr.xml | 35 +++++ 8 files changed, 232 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java index a7318161c..828ad7b9e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java @@ -45,6 +45,11 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_HYBRID_HR_DRAW_WIDGET_CIRCLES = "widget_draw_circles"; public static final String PREF_HYBRID_HR_SAVE_RAW_ACTIVITY_FILES = "save_raw_activity_files"; public static final String PREF_HYBRID_HR_DANGEROUS_EXTERNAL_INTENTS = "dangerous_external_intents"; + public static final String PREF_HYBRID_HR_ACTIVITY_RECOGNITION_RUNNING = "activity_recognize_running"; + public static final String PREF_HYBRID_HR_ACTIVITY_RECOGNITION_BIKING = "activity_recognize_biking"; + public static final String PREF_HYBRID_HR_ACTIVITY_RECOGNITION_WALKING = "activity_recognize_walking"; + public static final String PREF_HYBRID_HR_ACTIVITY_RECOGNITION_ROWING = "activity_recognize_rowing"; + public static final String PREF_ACTIVATE_DISPLAY_ON_LIFT = "activate_display_on_lift_wrist"; public static final String PREF_DISPLAY_ON_LIFT_START = "display_on_lift_start"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index 92f04e4cd..b61545ee6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -96,6 +96,10 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_DRAW_WIDGET_CIRCLES; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_FORCE_WHITE_COLOR; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_SAVE_RAW_ACTIVITY_FILES; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_RUNNING; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_BIKING; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_WALKING; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_ROWING; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYDRATION_PERIOD; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYDRATION_SWITCH; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_KEY_VIBRATION; @@ -529,6 +533,10 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp addPreferenceHandlerFor(PREF_HYBRID_HR_FORCE_WHITE_COLOR); addPreferenceHandlerFor(PREF_HYBRID_HR_SAVE_RAW_ACTIVITY_FILES); addPreferenceHandlerFor(PREF_HYBRID_HR_DANGEROUS_EXTERNAL_INTENTS); + addPreferenceHandlerFor(PREF_HYBRID_HR_ACTIVITY_RECOGNITION_RUNNING); + addPreferenceHandlerFor(PREF_HYBRID_HR_ACTIVITY_RECOGNITION_BIKING); + addPreferenceHandlerFor(PREF_HYBRID_HR_ACTIVITY_RECOGNITION_WALKING); + addPreferenceHandlerFor(PREF_HYBRID_HR_ACTIVITY_RECOGNITION_ROWING); addPreferenceHandlerFor(PREF_SONYSWR12_STAMINA); addPreferenceHandlerFor(PREF_SONYSWR12_LOW_VIBRATION); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java index fa5d7d372..16ab755f3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java @@ -16,6 +16,7 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.*; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.UnitsConfigItem; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.VibrationStrengthConfigItem; import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest.MUSIC_PHONE_REQUEST; @@ -1283,23 +1284,13 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { @Override public void onTestNewFunction() { - queueWrite(new FossilRequest() { + queueWrite((FileEncryptedInterface) new ConfigurationGetRequest(this){ @Override - public boolean isFinished() { - return true; - } - - @Override - public byte[] getStartSequence() { - return new byte[]{0x01, 0x07}; - } - - @Override - public UUID getRequestUUID() { - return UUID.fromString("3dda0005-957f-7d4a-34a6-74696673696d"); + protected void handleConfiguration(ConfigItem[] items) { + super.handleConfiguration(items); + LOG.debug(items[items.length - 1].toString()); } }); - queueWrite(new ConfirmOnDeviceRequest()); } public byte[] getSecretKey() throws IllegalAccessException { @@ -1400,6 +1391,27 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { } } + private void setActivityRecognition(){ + SharedPreferences prefs = getDeviceSpecificPreferences(); + String modeRunning = prefs.getString(DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_RUNNING, "none"); + String modeBiking = prefs.getString(DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_BIKING, "none"); + String modeWalking = prefs.getString(DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_WALKING, "none"); + String modeRowing = prefs.getString(DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_ROWING, "none"); + + FitnessConfigItem fitnessConfigItem = new FitnessConfigItem( + !modeRunning.equals("none"), + modeRunning.equals("ask"), + !modeBiking.equals("none"), + modeBiking.equals("ask"), + !modeWalking.equals("none"), + modeWalking.equals("ask"), + !modeRowing.equals("none"), + modeRowing.equals("ask") + ); + + queueWrite((FileEncryptedInterface) new ConfigurationPutRequest(fitnessConfigItem, this)); + } + @Override public void onSendConfiguration(String config) { switch (config) { @@ -1431,6 +1443,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { case SettingsActivity.PREF_MEASUREMENT_SYSTEM: setUnitsConfig(); break; + case DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_RUNNING: + case DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_BIKING: + case DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_WALKING: + case DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_ROWING: + setActivityRecognition(); + break; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/configuration/ConfigurationPutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/configuration/ConfigurationPutRequest.java index 202e97e59..768902cd3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/configuration/ConfigurationPutRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/configuration/ConfigurationPutRequest.java @@ -16,6 +16,12 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration; +import static android.content.ContentValues.TAG; + +import android.util.Log; + +import androidx.annotation.NonNull; + import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; @@ -25,18 +31,20 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.foss import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class ConfigurationPutRequest extends FilePutRequest { private static final HashMap> itemsById = new HashMap<>(); static { - itemsById.put((short)0x02, CurrentStepCountConfigItem.class); - itemsById.put((short)0x03, DailyStepGoalConfigItem.class); - itemsById.put((short)0x0A, VibrationStrengthConfigItem.class); - itemsById.put((short)0x0C, TimeConfigItem.class); - itemsById.put((short)0x0D, BatteryConfigItem.class); + itemsById.put((short) 0x02, CurrentStepCountConfigItem.class); + itemsById.put((short) 0x03, DailyStepGoalConfigItem.class); + itemsById.put((short) 0x0A, VibrationStrengthConfigItem.class); + itemsById.put((short) 0x0C, TimeConfigItem.class); + itemsById.put((short) 0x0D, BatteryConfigItem.class); itemsById.put((short) 0x0E, HeartRateMeasurementModeItem.class); itemsById.put((short) 0x10, UnitsConfigItem.class); + itemsById.put((short) 0x14, FitnessConfigItem.class); } public static ConfigItem[] parsePayload(byte[] data) { @@ -45,7 +53,7 @@ public class ConfigurationPutRequest extends FilePutRequest { ArrayList configItems = new ArrayList<>(); - while(buffer.hasRemaining()){ + while (buffer.hasRemaining()) { short id = buffer.getShort(); byte length = buffer.get(); byte[] payload = new byte[length]; @@ -84,12 +92,12 @@ public class ConfigurationPutRequest extends FilePutRequest { private static byte[] createFileContent(ConfigItem[] items) { int overallSize = 0; - for(ConfigItem item : items){ + for (ConfigItem item : items) { overallSize += item.getItemSize() + 3; } ByteBuffer buffer = ByteBuffer.allocate(overallSize); buffer.order(ByteOrder.LITTLE_ENDIAN); - for(ConfigItem item : items){ + for (ConfigItem item : items) { buffer.putShort(item.getId()); buffer.put((byte) item.getItemSize()); buffer.put(item.getContent()); @@ -117,7 +125,7 @@ public class ConfigurationPutRequest extends FilePutRequest { this.configId = configId; } - public T getValue(){ + public T getValue() { return value; } @@ -171,20 +179,20 @@ public class ConfigurationPutRequest extends FilePutRequest { ByteBuffer buffer = ByteBuffer.wrap(data); buffer.order(ByteOrder.LITTLE_ENDIAN); - switch (data.length){ - case 1:{ + switch (data.length) { + case 1: { this.value = (T) (Byte) buffer.get(); break; } - case 2:{ + case 2: { this.value = (T) (Short) buffer.getShort(); break; } - case 4:{ + case 4: { this.value = (T) (Integer) buffer.getInt(); break; } - case 8:{ + case 8: { this.value = (T) (Long) buffer.getLong(); break; } @@ -192,7 +200,7 @@ public class ConfigurationPutRequest extends FilePutRequest { } } - static public class BatteryConfigItem extends ConfigItem{ + static public class BatteryConfigItem extends ConfigItem { private int batteryPercentage, batteryVoltage; public int getBatteryPercentage() { @@ -228,9 +236,9 @@ public class ConfigurationPutRequest extends FilePutRequest { } } - static public class HeartRateMeasurementModeItem extends GenericConfigItem{ + static public class HeartRateMeasurementModeItem extends GenericConfigItem { public HeartRateMeasurementModeItem() { - this((byte)-1); + this((byte) -1); } public HeartRateMeasurementModeItem(byte value) { @@ -239,7 +247,7 @@ public class ConfigurationPutRequest extends FilePutRequest { } static public class DailyStepGoalConfigItem extends GenericConfigItem { - public DailyStepGoalConfigItem(){ + public DailyStepGoalConfigItem() { this(-1); } @@ -255,7 +263,7 @@ public class ConfigurationPutRequest extends FilePutRequest { } static public class VibrationStrengthConfigItem extends GenericConfigItem { - public VibrationStrengthConfigItem(){ + public VibrationStrengthConfigItem() { this((byte) -1); } @@ -321,7 +329,7 @@ public class ConfigurationPutRequest extends FilePutRequest { @Override public void parseData(byte[] data) { - if(data.length != 8) throw new RuntimeException("wrong data"); + if (data.length != 8) throw new RuntimeException("wrong data"); ByteBuffer buffer = ByteBuffer.wrap(data); buffer.order(ByteOrder.LITTLE_ENDIAN); @@ -331,5 +339,103 @@ public class ConfigurationPutRequest extends FilePutRequest { this.offsetMinutes = buffer.getShort(); } } + + + static public class FitnessConfigItem extends ConfigItem { + boolean recognizeRunning = false; + boolean askRunning = false; + + boolean recognizeBiking = false; + boolean askBiking = false; + + boolean recognizeWalk = false; + boolean askWalk = false; + + boolean recognizeRudder = false; + boolean askRudder = false; + + public FitnessConfigItem(boolean recognizeRunning, boolean askRunning, boolean recognizeBiking, boolean askBiking, boolean recognizeWalk, boolean askWalk, boolean recognizeRudder, boolean askRudder) { + this.recognizeRunning = recognizeRunning; + this.askRunning = askRunning; + this.recognizeBiking = recognizeBiking; + this.askBiking = askBiking; + this.recognizeWalk = recognizeWalk; + this.askWalk = askWalk; + this.recognizeRudder = recognizeRudder; + this.askRudder = askRudder; + } + + public FitnessConfigItem() { + } + + ; + + @Override + public int getItemSize() { + return 30; + } + + @Override + public short getId() { + return 0x14; + } + + @Override + public byte[] getContent() { + byte[] data = new byte[]{ + (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x02, (byte) 0x00, (byte) 0x0A, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x08, (byte) 0x00, (byte) 0x0A, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x09, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01 + }; + if (recognizeRunning) { + data[1] |= 0x01; + if (askRunning) { + data[1] |= 0x02; + } + } + if (recognizeBiking) { + data[7] |= 0x01; + if (askBiking) { + data[7] |= 0x02; + } + } + if (recognizeWalk) { + data[19] |= 0x01; + if (askWalk) { + data[19] |= 0x02; + } + } + if (recognizeRudder) { + data[25] |= 0x01; + if (askRudder) { + data[25] |= 0x02; + } + } + return data; + } + + @Override + public void parseData(byte[] data) { + recognizeRunning = (data[1] & 0x01) == 0x01; + askRunning = (data[1] & 0x02) == 0x02; + + recognizeBiking = (data[7] & 0x01) == 0x01; + askBiking = (data[7] & 0x02) == 0x02; + + recognizeWalk = (data[19] & 0x01) == 0x01; + askWalk = (data[19] & 0x02) == 0x02; + + recognizeRudder = (data[25] & 0x01) == 0x01; + askRudder = (data[25] & 0x02) == 0x02; + } + + @NonNull + @Override + public String toString() { + return + "recognizeRunning: " + recognizeRunning + " askRunning: " + askRunning + "\n" + + "recognizeBiking: " + recognizeBiking + " askBiking: " + askBiking + "\n" + + "recognizeWalking: " + recognizeWalk + " askWalk: " + askWalk + "\n" + + "recognizeRudder: " + recognizeRudder + " askRudder: " + askRudder; + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationGetRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationGetRequest.java index 4966e21de..6a1d3af3c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationGetRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationGetRequest.java @@ -71,6 +71,8 @@ public class ConfigurationGetRequest extends FileEncryptedLookupAndGetRequest im } device.sendDeviceUpdateIntent(getAdapter().getContext()); + + handleConfiguration(items); } @Override @@ -81,4 +83,6 @@ public class ConfigurationGetRequest extends FileEncryptedLookupAndGetRequest im throw new RuntimeException("strange lookup stuff"); } } + + protected void handleConfiguration(ConfigurationPutRequest.ConfigItem[] items){} } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index a9a1b16f6..6c6c5f114 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -2043,5 +2043,17 @@ playback_control volume_control + + + @string/pref_activity_recognition_mode_none + @string/pref_activity_recognition_mode_ask + @string/pref_activity_recognition_mode_auto + + + + mode_none + mode_ask + mode_auto + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ce2dbcf87..030166f53 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1468,6 +1468,16 @@ ###.#mi ###ft + + Activity recognition settings + recognize running + recognize biking + recognize walking + recognize rowing + none + ask + auto + Menu Some buttons cannot be configured because their functions are hard-coded in the watch firmware.\n\nWarning: long-pressing the upper button when a watchface from the official Fossil app is installed will also toggle between showing/hiding widgets. diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr.xml index 9f636a6ec..f0c9c022b 100644 --- a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml +++ b/app/src/main/res/xml/devicesettings_fossilhybridhr.xml @@ -54,6 +54,41 @@ android:summary="@string/fossil_hr_button_config_info" /> + + + + + + + + + + + +