1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-08-24 16:21:16 +02:00

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 <dakhnod@noreply.codeberg.org>
Co-committed-by: dakhnod <dakhnod@noreply.codeberg.org>
This commit is contained in:
dakhnod 2022-01-06 13:31:00 +01:00 committed by Andreas Shimokawa
parent f49facd17c
commit 8d1a1c07b7
8 changed files with 232 additions and 34 deletions

View File

@ -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";

View File

@ -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);

View File

@ -16,6 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
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;
}
}

View File

@ -16,6 +16,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
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<Short, Class<? extends ConfigItem>> 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<ConfigItem> 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<Byte>{
static public class HeartRateMeasurementModeItem extends GenericConfigItem<Byte> {
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<Integer> {
public DailyStepGoalConfigItem(){
public DailyStepGoalConfigItem() {
this(-1);
}
@ -255,7 +263,7 @@ public class ConfigurationPutRequest extends FilePutRequest {
}
static public class VibrationStrengthConfigItem extends GenericConfigItem<Byte> {
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;
}
}
}

View File

@ -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){}
}

View File

@ -2043,5 +2043,17 @@
<item>playback_control</item>
<item>volume_control</item>
</string-array>
<string-array name="fossil_hr_activity_recognition_modes">
<item>@string/pref_activity_recognition_mode_none</item>
<item>@string/pref_activity_recognition_mode_ask</item>
<item>@string/pref_activity_recognition_mode_auto</item>
</string-array>
<string-array name="fossil_hr_activity_recognition_mode_values">
<item>mode_none</item>
<item>mode_ask</item>
<item>mode_auto</item>
</string-array>
</resources>

View File

@ -1468,6 +1468,16 @@
<string name="distance_format_miles">###.#mi</string>
<!-- Translators: the ### indicate number of digits, keep intact -->
<string name="distance_format_feet">###ft</string>
<string name="prefs_activity_recognition">Activity recognition settings</string>
<string name="pref_activity_recognize_running">recognize running</string>
<string name="pref_activity_recognize_biking">recognize biking</string>
<string name="pref_activity_recognize_walking">recognize walking</string>
<string name="pref_activity_recognize_rowing">recognize rowing</string>
<string name="pref_activity_recognition_mode_none">none</string>
<string name="pref_activity_recognition_mode_ask">ask</string>
<string name="pref_activity_recognition_mode_auto">auto</string>
<string name="menuitem_menu">Menu</string>
<string name="fossil_hr_button_config_info">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.</string>
</resources>

View File

@ -54,6 +54,41 @@
android:summary="@string/fossil_hr_button_config_info" />
</PreferenceScreen>
<PreferenceScreen
android:icon="@drawable/ic_activity_unknown_small"
android:title="@string/prefs_activity_recognition"
android:key="activity_recognition_settings">
<ListPreference
android:key="activity_recognize_running"
android:title="@string/pref_activity_recognize_running"
android:icon="@drawable/ic_activity_running"
android:entryValues="@array/fossil_hr_activity_recognition_mode_values"
android:entries="@array/fossil_hr_activity_recognition_modes"/>
<ListPreference
android:key="activity_recognize_biking"
android:title="@string/pref_activity_recognize_biking"
android:icon="@drawable/ic_activity_biking"
android:entryValues="@array/fossil_hr_activity_recognition_mode_values"
android:entries="@array/fossil_hr_activity_recognition_modes"/>
<ListPreference
android:key="activity_recognize_walking"
android:title="@string/pref_activity_recognize_walking"
android:icon="@drawable/ic_activity_walking"
android:entryValues="@array/fossil_hr_activity_recognition_mode_values"
android:entries="@array/fossil_hr_activity_recognition_modes"/>
<ListPreference
android:key="activity_recognize_rowing"
android:title="@string/pref_activity_recognize_rowing"
android:icon="@drawable/ic_activity_rowing"
android:entryValues="@array/fossil_hr_activity_recognition_mode_values"
android:entries="@array/fossil_hr_activity_recognition_modes"/>
</PreferenceScreen>
<Preference
android:title="@string/qhybrid_pref_title_actions"
android:icon="@drawable/ic_pending_actions"