adding FitPro bands support

This commit is contained in:
vanous 2021-09-21 16:37:19 +02:00
parent b8855f16ee
commit e00ee75ad2
29 changed files with 2667 additions and 6 deletions

View File

@ -43,7 +43,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(33, MAIN_PACKAGE + ".entities");
Schema schema = new Schema(34, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@ -80,6 +80,7 @@ public class GBDaoGenerator {
addSonySWR12Sample(schema, user, device);
addBangleJSActivitySample(schema, user, device);
addCasioGBX100Sample(schema, user, device);
addFitProActivitySample(schema, user, device);
addHybridHRActivitySample(schema, user, device);
addCalendarSyncState(schema, device);
@ -614,4 +615,21 @@ public class GBDaoGenerator {
batteryLevel.addIntProperty("level").notNull();
return batteryLevel;
}
private static Entity addFitProActivitySample(Schema schema, Entity user, Entity device) {
Entity activitySample = addEntity(schema, "FitProActivitySample");
activitySample.implementsSerializable();
addCommonActivitySampleProperties("AbstractFitProActivitySample", activitySample, user, device);
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
addHeartRateProperties(activitySample);
activitySample.addIntProperty("caloriesBurnt");
activitySample.addIntProperty("distanceMeters");
activitySample.addIntProperty("spo2Percent");
activitySample.addIntProperty("pressureLowMmHg");
activitySample.addIntProperty("pressureHighMmHg");
activitySample.addIntProperty("activeTimeMinutes");
return activitySample;
}
}

View File

@ -46,6 +46,8 @@ vendor's servers.
- Casio
- [GB-5600B/GB-6900B/GB-X6900B](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Casio-GB-5600B%2FGB-6900B%2FGB-X6900B)
- [GBX-100](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Casio-GBX-100)
- [FitPro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/FitPro)
- Fossil
- [Hybrid HR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Fossil-Hybrid-HR) [**\[!\]**](#special-pairing-procedures)
- Q Hybrid
@ -114,6 +116,7 @@ Please see [FEATURES.md](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/ma
* Taavi Eomäe (iTag)
* Erik Bloß (TLW64)
* Yukai Li (Lefun)
* Petr Vaněk (FitPro)
## Contribute

View File

@ -46,6 +46,7 @@ public class AboutUserPreferencesActivity extends AbstractSettingsActivity {
addPreferenceHandlerFor(PREF_USER_HEIGHT_CM);
addPreferenceHandlerFor(PREF_USER_WEIGHT_KG);
addPreferenceHandlerFor(PREF_USER_GENDER);
addPreferenceHandlerFor(PREF_USER_STEPS_GOAL);
}
@Override

View File

@ -21,6 +21,8 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_DATEFORMAT = "dateformat";
public static final String PREF_TIMEFORMAT = "timeformat";
public static final String PREF_WEARLOCATION = "wearlocation";
public static final String PREF_VIBRATION_ENABLE = "vibration_enable";
public static final String PREF_NOTIFICATION_ENABLE = "notification_enable";
public static final String PREF_SCREEN_ORIENTATION = "screen_orientation";
public static final String PREF_RESERVER_ALARMS_CALENDAR = "reserve_alarms_calendar";
public static final String PREF_ALLOW_HIGH_MTU = "allow_high_mtu";
@ -43,8 +45,25 @@ public class DeviceSettingsPreferenceConst {
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_ACTIVATE_DISPLAY_ON_LIFT = "activate_display_on_lift_wrist";
public static final String PREF_DISPLAY_ON_LIFT_START = "display_on_lift_start";
public static final String PREF_DISPLAY_ON_LIFT_END = "display_on_lift_end";
public static final String PREF_SLEEP_TIME = "prefs_enable_sleep_time";
public static final String PREF_SLEEP_TIME_START = "prefs_sleep_time_start";
public static final String PREF_SLEEP_TIME_END = "prefs_sleep_time_end";
public static final String PREF_LIFTWRIST_NOSHED = "activate_display_on_lift_wrist_noshed";
public static final String PREF_DISCONNECTNOTIF_NOSHED = "disconnect_notification_noshed";
public static final String PREF_LONGSIT_START = "pref_longsit_start";
public static final String PREF_LONGSIT_END = "pref_longsit_end";
public static final String PREF_AUTOHEARTRATE_SWITCH = "pref_autoheartrate_switch";
public static final String PREF_AUTOHEARTRATE_SLEEP = "pref_autoheartrate_sleep";
public static final String PREF_AUTOHEARTRATE_INTERVAL = "pref_autoheartrate_interval";
public static final String PREF_AUTOHEARTRATE_START = "pref_autoheartrate_start";
public static final String PREF_AUTOHEARTRATE_END = "pref_autoheartrate_end";
public static final String PREF_POWER_MODE = "power_mode";
public static final String PREF_BUTTON_BP_CALIBRATE = "prefs_sensors_button_bp_calibration";
public static final String PREF_ALTITUDE_CALIBRATE = "pref_sensors_altitude";
@ -52,6 +71,9 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_LONGSIT_SWITCH = "pref_longsit_switch";
public static final String PREF_LONGSIT_SWITCH_NOSHED = "screen_longsit_noshed";
public static final String PREF_DO_NOT_DISTURB_NOAUTO = "do_not_disturb_no_auto";
public static final String PREF_DO_NOT_DISTURB_NOAUTO_START = "do_not_disturb_no_auto_start";
public static final String PREF_DO_NOT_DISTURB_NOAUTO_END = "do_not_disturb_no_auto_end";
public static final String PREF_FIND_PHONE_ENABLED = "prefs_find_phone";
public static final String PREF_AUTOLIGHT = "autolight";
public static final String PREF_AUTOREMOVE_MESSAGE = "autoremove_message";
@ -74,4 +96,5 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_TRANSLITERATION_ENABLED = "pref_transliteration_enabled";
public static final String PREF_SOUNDS = "sounds";
public static final String PREF_AUTH_KEY = "authkey";
}

View File

@ -65,6 +65,8 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DATEFORMAT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FAKE_RING_DURATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FIND_PHONE_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_DANGEROUS_EXTERNAL_INTENTS;
@ -79,9 +81,20 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LIFTWRIST_NOSHED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_PERIOD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_SWITCH;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_SWITCH;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_SLEEP;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_INTERVAL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTIFICATION_ENABLE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_OPERATING_SOUNDS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_POWER_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SCREEN_ORIENTATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_LOW_VIBRATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_SMART_INTERVAL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_STAMINA;
@ -90,6 +103,7 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TRANSLITERATION_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_STRENGH_PERCENTAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_ENABLE;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION;
@ -372,6 +386,8 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
addPreferenceHandlerFor(PREF_EXPOSE_HR_THIRDPARTY);
addPreferenceHandlerFor(PREF_BT_CONNECTED_ADVERTISEMENT);
addPreferenceHandlerFor(PREF_WEARLOCATION);
addPreferenceHandlerFor(PREF_VIBRATION_ENABLE);
addPreferenceHandlerFor(PREF_NOTIFICATION_ENABLE);
addPreferenceHandlerFor(PREF_SCREEN_ORIENTATION);
addPreferenceHandlerFor(PREF_TIMEFORMAT);
addPreferenceHandlerFor(PREF_BUTTON_1_FUNCTION_SHORT);
@ -391,7 +407,16 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
addPreferenceHandlerFor(PREF_ALTITUDE_CALIBRATE);
addPreferenceHandlerFor(PREF_LONGSIT_PERIOD);
addPreferenceHandlerFor(PREF_LONGSIT_SWITCH);
addPreferenceHandlerFor(PREF_LONGSIT_START);
addPreferenceHandlerFor(PREF_LONGSIT_END);
addPreferenceHandlerFor(PREF_AUTOHEARTRATE_SWITCH);
addPreferenceHandlerFor(PREF_AUTOHEARTRATE_SLEEP);
addPreferenceHandlerFor(PREF_AUTOHEARTRATE_INTERVAL);
addPreferenceHandlerFor(PREF_AUTOHEARTRATE_START);
addPreferenceHandlerFor(PREF_AUTOHEARTRATE_END);
addPreferenceHandlerFor(PREF_DO_NOT_DISTURB_NOAUTO);
addPreferenceHandlerFor(PREF_DO_NOT_DISTURB_NOAUTO_START);
addPreferenceHandlerFor(PREF_DO_NOT_DISTURB_NOAUTO_END);
addPreferenceHandlerFor(PREF_FIND_PHONE_ENABLED);
addPreferenceHandlerFor(PREF_AUTOLIGHT);
addPreferenceHandlerFor(PREF_AUTOREMOVE_MESSAGE);
@ -415,6 +440,81 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
addPreferenceHandlerFor(PREF_SONYSWR12_LOW_VIBRATION);
addPreferenceHandlerFor(PREF_SONYSWR12_SMART_INTERVAL);
String sleepTimeState = prefs.getString(PREF_SLEEP_TIME, PREF_DO_NOT_DISTURB_OFF);
boolean sleepTimeScheduled = sleepTimeState.equals(PREF_DO_NOT_DISTURB_SCHEDULED);
final Preference sleepTimeInfo = findPreference(PREF_SLEEP_TIME);
if (sleepTimeInfo != null) {
//sleepTimeInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(sleepTimeInfo));
sleepTimeInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME);
}
});
return true;
}
});
}
final Preference sleepTimeStart = findPreference(PREF_SLEEP_TIME_START);
if (sleepTimeStart != null) {
sleepTimeStart.setEnabled(sleepTimeScheduled);
sleepTimeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_START);
}
});
return true;
}
});
}
final Preference sleepTimeEnd = findPreference(PREF_SLEEP_TIME_END);
if (sleepTimeEnd != null) {
sleepTimeEnd.setEnabled(sleepTimeScheduled);
sleepTimeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_END);
}
});
return true;
}
});
}
final Preference sleepTime = findPreference(PREF_SLEEP_TIME);
if (sleepTime != null) {
sleepTime.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
final boolean scheduled = PREF_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString());
Objects.requireNonNull(sleepTimeStart).setEnabled(scheduled);
Objects.requireNonNull(sleepTimeEnd).setEnabled(scheduled);
if (sleepTimeInfo != null) {
//sleepTimeInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString()));
}
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME);
}
});
return true;
}
});
}
String displayOnLiftState = prefs.getString(PREF_ACTIVATE_DISPLAY_ON_LIFT, PREF_DO_NOT_DISTURB_OFF);
boolean displayOnLiftScheduled = displayOnLiftState.equals(PREF_DO_NOT_DISTURB_SCHEDULED);

View File

@ -0,0 +1,250 @@
/* Copyright (C) 2016-2020 Petr Vaněk
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.fitpro;
import java.util.UUID;
public class FitProConstants {
// cd 00 2a 05 01 0c 00 0c 0000012d000000bb000018
// | | | | | | | | |-- payload ->
// | | | | | | | |----- payload length low
// | | | | | | |-------- payload length high
// | | | | | |----------- command
// | | | | |-------------- delimiter/version
// | | | |----------------- command group
// | | |-------------------- full length low
// | |----------------------- full length high
// |-------------------------- header
public static final byte DATA_HEADER = (byte) 0xCD;
public static final byte DATA_HEADER_ACK = (byte) 0xDC;
public static final byte[] DATA_TEMPLATE = {
(byte) DATA_HEADER, // header
(byte) 0x00, // delimiter or first byte of argument count?
(byte) 0, // argument count, calculated
(byte) 0, // command 1
(byte) 0x1, // delimiter?
(byte) 0, // command 2
(byte) 0x0, // delimiter or first byte of data length?
(byte) 0, // data length calculated
// data payload
};
public static final UUID UUID_CHARACTERISTIC_UART = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9d");
public static final UUID UUID_CHARACTERISTIC_TX = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9d");
public static final UUID UUID_CHARACTERISTIC_RX = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9d");
//public static final UUID LT716_OTA = UUID.fromString("00010203-0405-0607-0809-0a0b0c0d1912"); //when in OTA mode
public static final byte CMD_GROUP_GENERAL = (byte) 0x12;
public static final byte CMD_GROUP_BAND_INFO = (byte) 0x20;
public static final byte CMD_GROUP_RECEIVE_BUTTON_DATA = 0x1c;
public static final byte CMD_GROUP_RECEIVE_SPORTS_DATA = 0x15;
public static final byte CMD_GROUP_HEARTRATE_SETTINGS = 0x16;
public static final byte CMD_GROUP_REQUEST_DATA = 0x1a;
public static final byte CMD_GROUP_BIND = 0x14;
public static final byte CMD_GROUP_RESET = 0x1d;
//group general 0x12
public static final byte CMD_FIND_BAND = (byte) 0x0b;
public static final byte CMD_SET_DATE_TIME = (byte) 0x1;
public static final byte CMD_SET_LANGUAGE = (byte) 0x15;
public static final byte CMD_NOTIFICATION_MESSAGE = (byte) 0x12;
public static final byte CMD_NOTIFICATION_CALL = (byte) 0x11;
public static final byte CMD_WEATHER = (byte) 0x20;
public static final byte CMD_CAMERA = (byte) 0xc;
public static final byte CMD_HEART_RATE_MEASUREMENT = 0x18; //on/off
public static final byte CMD_DND = (byte) 0x14;
public static final byte CMD_INIT1 = 0xa;
public static final byte CMD_INIT2 = 0xc;
public static final byte CMD_INIT3 = (byte) 0xff;
public static final byte CMD_SET_SLEEP_TIMES = (byte) 0xF;
public static final byte CMD_ALARM = (byte) 0x2;
public static final byte CMD_SET_ARM = (byte) 0x6;
public static final byte CMD_GET_HR = (byte) 0xd; //0/1
public static final byte CMD_GET_PRESS = (byte) 0xe; //0/1
public static final byte CMD_NOTIFICATIONS_ENABLE = 0x7;
public static final byte[] VALUE_SET_NOTIFICATIONS_ENABLE_ON = new byte[]{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1};
public static final byte[] VALUE_SET_NOTIFICATIONS_ENABLE_OFF = new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
public static final byte CMD_SET_LONG_SIT_REMINDER = (byte) 0x5;
public static final byte[] VALUE_SET_LONG_SIT_REMINDER_ON = new byte[]{0x0, 0x1, 0x0, (byte) 0x96};
public static final byte[] VALUE_SET_LONG_SIT_REMINDER_OFF = new byte[]{0x0, 0x0, 0x0, (byte) 0x96, 0x4, 0x8, 0x16, 0x7f};
public static final byte CMD_SET_DISPLAY_ON_LIFT = (byte) 0x9;
public static final byte CMD_SET_STEP_GOAL = (byte) 0x3;
public static final byte CMD_SET_USER_DATA = (byte) 0x4;
public static final byte CMD_SET_DEVICE_VIBRATIONS = (byte) 0x8;
//group receive sports data 0x15
public static final byte CMD_REQUEST_DAY_STEPS_SUMMARY = 0x6;
//request steps/hr, as per
// https://github.com/vanous/MFitX/blob/AntiGoogle/app/src/main/java/anonymouls/dev/mgcex/app/backend/LM517CommandInterpreter.kt#L242
public static final byte CMD_REQUEST_FETCH_DAY_STEPS_DATA = 0xd;
public static final byte CMD_REQUEST_STEPS_DATA1 = 0x6;
public static final byte CMD_REQUEST_STEPS_DATA0x7 = 0x7;
public static final byte CMD_REQUEST_STEPS_DATA0x8 = 0x8;
public static final byte CMD_REQUEST_STEPS_DATA0x10 = 0x10;
//also sleep data as per mfitx
//https://github.com/vanous/MFitX/blob/AntiGoogle/app/src/main/java/anonymouls/dev/mgcex/app/backend/LM517CommandInterpreter.kt#L235
public static final byte RX_HEART_RATE_DATA = 0x0e;
public static final byte RX_SPORTS_MEASUREMENT = 0x18; //0/1
public static final byte SPORTS_RECEIVE_KEY = 0x1; //0/1
public static final byte RX_SLEEP_DATA = 0x3;
public static final byte RX_STEP_DATA = 0x2;
// https://github.com/vanous/MFitX/blob/AntiGoogle/app/src/main/java/anonymouls/dev/mgcex/app/backend/LM517CommandInterpreter.kt#L162
public static final byte RX_SPORTS_DAY_DATA = 0xc; //0/1
//group get band info 0x20
public static final byte CMD_RX_BAND_INFO = (byte) 0x2;
//group request data 0x1a
public static final byte CMD_GET_STEPS_TARGET = 0x2;
public static final byte CMD_GET_HW_INFO = 0x10;
public static final byte CMD_GET_AUTO_HR = 0x8;
public static final byte CMD_GET_CONTACTS = 0xd;
//group 0x14
public static final byte CMD_UNBIND = (byte) 0x0;
//group 0x1d
public static final byte CMD_RESET = (byte) 0x1;
// group receive data
public static final byte RX_FIND_PHONE = (byte) 0x01;
public static final byte RX_CAMERA1 = (byte) 0x02;
public static final byte RX_CAMERA2 = (byte) 0x03;
public static final byte RX_CAMERA3 = (byte) 0x04;
public static final byte RX_MEDIA_PLAY_PAUSE = (byte) 0x0b;
public static final byte RX_MEDIA_FORW = (byte) 0x0c;
public static final byte RX_MEDIA_BACK = (byte) 0x0a;
//values
public static final byte VALUE_ON = (byte) 0x1;
public static final byte VALUE_OFF = (byte) 0x0;
public static final byte UNIT_METRIC = (byte) 0x1;
public static final byte UNIT_IMPERIAL = (byte) 0x2;
public static final byte GENDER_MALE = (byte) 0x1;
public static final byte GENDER_FEMALE = (byte) 0x0;
public static final byte VALUE_SET_ARM_LEFT = (byte) 0x0; //guessing
public static final byte VALUE_SET_ARM_RIGHT = (byte) 0x1;
public static final byte[] VALUE_SET_DEVICE_VIBRATIONS_ENABLE = new byte[]{0x1, 0x1, 0x1, 0x1};
public static final byte[] VALUE_SET_DEVICE_VIBRATIONS_DISABLE = new byte[]{0, 0, 0, 0};
public static final byte NOTIFICATION_ICON_FACEBOOK = (byte) 0x4;
public static final byte NOTIFICATION_ICON_TWITTER = (byte) 0x5;
public static final byte NOTIFICATION_ICON_WHATSAPP = (byte) 0x8;
public static final byte NOTIFICATION_ICON_LINE = (byte) 0x7;
public static final byte NOTIFICATION_ICON_SMS = (byte) 0x1;
public static final byte NOTIFICATION_ICON_WECHAT = (byte) 0x3;
public static final byte NOTIFICATION_ICON_QQ = (byte) 0x2;
public static final byte NOTIFICATION_ICON_INSTAGRAM = (byte) 0x10;
public static final int LANG_CHINESE = 0x0;
public static final int LANG_PORTUGUESE = 0xc;
public static final int LANG_GERMAN = 0x5;
public static final int LANG_SPANISH = 0x6;
public static final int LANG_FRENCH = 0x7;
public static final int LANG_NETHERLANDS = 0xa;
public static final int LANG_POLISH = 0xb;
public static final int LANG_RUSSIAN = 0xd;
public static final int LANG_TURKISH = 0x10;
public static final int LANG_ENGLISH = 0x1;
public static final int LANG_CZECH = 0x4;
public static final int LANG_ITALIAN = 0x12;
//00 01 00 96 03 08 16 7f
//^^ zeros ^^ on/off ^^ sep ^^unknown ^^minutes ^^from ^^ to ^^ unknown
//minutes are array of minutes by 15, in 45,60, 75... 3,4,5...
//the byte 4 96 could be experimented with to set different values..., byte 3 is probably Hi, byte 4 Low
//public static final byte[] CMD_SET_LONG_SIT_REMINDER = new byte[]{(byte) 0x12, (byte) 0x5};
//maybe could be usign the ON/OFF?
//public static final byte[] VALUE_SET_LONG_SIT_REMINDER_ON = new byte[]{0x0, 0x1, 0x0, (byte) 0x96};
//public static final byte[] VALUE_SET_LONG_SIT_REMINDER_OFF = new byte[]{0x0, 0x0, 0x0, (byte) 0x96, 0x4, 0x8, 0x16, 0x7f};
//Value: cd 00 0a 12 01 09 00 05 01 01 e0 05 28 ON
//Value: cd 00 0a 12 01 09 00 05 00 01 e0 05 28 OFF
// 00 1 2 3 4 5 6 7 8 9 10 11 12
//byte 8 on/off
// 9,10,11,12 time from/to
//public static final byte[] CMD_SET_DISPLAY_ON_LIFT = new byte[]{(byte) 0x12, (byte) 0x9};
//maybe could be usign the ON/OFF?
//public static final byte VALUE_SET_DISPLAY_ON_LIFT_ON = 0x1;
//public static final byte[] VALUE_SET_DISPLAY_ON_LIFT_OFF = new byte[]{0x0, 0x0, 0x0, 0x0, 0x0};
//time
//0xCD 0x00 0x09 0x12 0x01 0x01 0x00 0x04 0xA5 0x83 0x73 0xDB
//find watch
//new byte[]{(byte) 0xcd, (byte) 0x00, (byte) 0x06,
// (byte) 0x12, (byte) 0x01, (byte) 0x0b, (byte) 0x00, (byte) 0x01, (byte) 0x01};
/*init procedure:
get pair cd 00 06 12 01 0a 00 01 02 18, 10
cd 00 09 12 01 01 00 04 55 f8 36 90
cd 00 05 1a 01 0a 00 00
cd 00 05 1a 01 0c 00 00
cd 00 06 12 01 15 00 01 01
cd 00 06 12 01 ff 00 01 01
cd 00 05 1a 01 01 00 00
dc 00 05 1a 01 00 0c 01
cd 00 05 1a 01 0f 00 00
cd 00 05 1a 01 10 00 00
dc 00 05 1a 01 00 1c 01
something cd 00 05 20 01 02 00 00 32, 2
real time step cd 00 06 15 01 06 00 01 01 21, 6
*/
//received heartrate
//0xcd 0x00 0x11 0x15 0x01 0x0e 0x00 0x0c 0x2a 0xf8 0x00 0x01 0x00 0x00 0x31 0x2c 0x62 0x75 0x54 0x47
//0xcd 0x00 0x11 0x15 0x01 0x0e 0x00 0x0c 0x2b 0x04 0x00 0x01 0x00 0x01 0x43 0x94 0x62 0x5c 0x48 0x53
//cd001115010e000c2b04000100014394625c4853
//find band
// cd 00 06 12 01 0b 00 01 01
//set celsius/fahrenheit?
//cd0006120121000100
//cd0006120121000101
//find phone
//0xcd 0x00 0x05 0x1c 0x01 0x01 0x00 0x00
//camera:
//0xcd 0x00 0x05 0x1c 0x01 0x03 0x00 0x00
//0xcd 0x00 0x05 0x1c 0x01 0x02 0x00 0x00
//0xcd 0x00 0x05 0x1c 0x01 0x04 0x00 0x00
// << 0xCD 0x00 0x05 0x1C 0x01 0x0A 0x00 0x00
//pause 0xCD 0x00 0x05 0x1C 0x01 0x0B 0x00 0x00
// >> 0xCD 0x00 0x05 0x1C 0x01 0x0C 0x00 0x00
//0xCD 0x00 0x05 0x1C 0x01 0x03 0x00 0x00
}

View File

@ -0,0 +1,185 @@
/* Copyright (C) 2016-2020 Petr Vaněk
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.fitpro;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.FitProActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class FitProDeviceCoordinator extends AbstractDeviceCoordinator {
private static final Logger LOG = LoggerFactory.getLogger(FitProDeviceCoordinator.class);
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
Long deviceId = device.getId();
QueryBuilder<?> qb = session.getFitProActivitySampleDao().queryBuilder();
qb.where(FitProActivitySampleDao.Properties.DeviceId.eq(deviceId)).buildDelete().executeDeleteWithoutDetachingEntities();
}
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
try {
BluetoothDevice device = candidate.getDevice();
String name = device.getName();
if (name != null &&
(name.equals("M6") ||
name.equals("M4"))
) {
return DeviceType.FITPRO;
}
} catch (Exception ex) {
LOG.error("unable to check device support", ex);
}
return DeviceType.UNKNOWN;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.FITPRO;
}
@Override
public int getBondingStyle() {
// different devices seem to work differently.
// user will unfortunately need to decide
return BONDING_STYLE_ASK;
}
@Nullable
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Override
public boolean supportsActivityDataFetching() {
return true;
}
@Override
public boolean supportsActivityTracking() {
return true;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return new FitProSampleProvider(device, session);
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return null;
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 8;
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return true;
}
@Override
public String getManufacturer() {
return "FitPro";
}
@Override
public boolean supportsAppsManagement() {
return false;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return null;
}
@Override
public boolean supportsCalendarEvents() {
return false;
}
@Override
public boolean supportsRealtimeData() {
return true;
}
@Override
public boolean supportsWeather() {
return true;
}
@Override
public boolean supportsFindDevice() {
return true;
}
@Override
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
return new int[]{
R.xml.devicesettings_liftwrist_display_no_on,
R.xml.devicesettings_longsit_extended,
R.xml.devicesettings_donotdisturb_no_auto,
R.xml.devicesettings_sleep_time,
R.xml.devicesettings_wearlocation,
R.xml.devicesettings_autoheartrate,
R.xml.devicesettings_vibrations_enable,
R.xml.devicesettings_notifications_enable,
R.xml.devicesettings_fitpro,
};
}
}

View File

@ -0,0 +1,100 @@
/* Copyright (C) 2016-2020 Petr Vaněk
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.fitpro;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.Property;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.FitProActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.FitProActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
public class FitProSampleProvider extends AbstractSampleProvider<FitProActivitySample> {
public FitProSampleProvider(GBDevice device, DaoSession session) {
super(device, session);
}
@Override
public AbstractDao<FitProActivitySample, ?> getSampleDao() {
return getSession().getFitProActivitySampleDao();
}
// as per FitProDeviceSupport.rawActivityKindToUniqueKind
@Override
public int normalizeType(int rawType) {
switch (rawType) {
case 1:
return ActivityKind.TYPE_ACTIVITY;
case 11:
return ActivityKind.TYPE_DEEP_SLEEP;
case 12:
return ActivityKind.TYPE_LIGHT_SLEEP;
default:
return ActivityKind.TYPE_UNKNOWN;
}
}
@Override
public int toRawActivityKind(int activityKind) {
switch (activityKind) {
case ActivityKind.TYPE_ACTIVITY:
return 1;
case ActivityKind.TYPE_DEEP_SLEEP:
return 11;
case ActivityKind.TYPE_LIGHT_SLEEP:
return 12;
default:
return 1;
}
}
@Override
public float normalizeIntensity(int rawIntensity) {
return rawIntensity / 2000f; //samples are per 5 minutes, so this should be sufficient
}
@Override
public FitProActivitySample createActivitySample() {
return new FitProActivitySample();
}
@Nullable
@Override
protected Property getRawKindSampleProperty() {
return FitProActivitySampleDao.Properties.RawKind;
}
@NonNull
@Override
protected Property getTimestampSampleProperty() {
return FitProActivitySampleDao.Properties.Timestamp;
}
@NonNull
@Override
protected Property getDeviceIdentifierSampleProperty() {
return FitProActivitySampleDao.Properties.DeviceId;
}
}

View File

@ -91,6 +91,7 @@ public enum DeviceType {
MIJIA_LYWSD02(200, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_mijia_lywsd02),
LEFUN(210, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled, R.string.devicetype_lefun),
SMAQ2OSS(220, R.drawable.ic_device_default, R.drawable.ic_device_default, R.string.devicetype_smaq2oss),
FITPRO(230, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_fitpro),
ITAG(250, R.drawable.ic_device_itag, R.drawable.ic_device_itag_disabled, R.string.devicetype_itag),
NUTMINI(251, R.drawable.ic_device_itag, R.drawable.ic_device_itag_disabled, R.string.devicetype_nut_mini),
VIBRATISSIMO(300, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo),

View File

@ -86,7 +86,7 @@ public class Weather {
}
private static final Weather weather = new Weather();
public static Weather getInstance() {return weather;}
public static Weather getInstance() { return weather; }
public static byte mapToPebbleCondition(int openWeatherMapCondition) {
/* deducted values:
@ -880,4 +880,95 @@ public class Weather {
return 5;
}
}
public static byte mapToFitProCondition(int openWeatherMapCondition) {
switch (openWeatherMapCondition) {
case 100:
return 1;
case 104:
return 2;
case 101:
case 102:
case 103:
return 3;
case 305:
case 309:
return 4;
case 306:
case 314:
case 399:
return 5;
case 307:
case 308:
case 310:
case 311:
case 312:
case 315:
case 316:
case 317:
case 318:
return 6;
case 300:
case 301:
case 302:
case 303:
return 7;
case 400:
case 407:
return 8;
case 401:
case 408:
case 499:
return 9;
case 402:
case 403:
case 409:
case 410:
return 10;
case 404:
case 405:
case 406:
return 11;
case 500:
case 501:
case 502:
case 509:
case 510:
case 511:
case 512:
case 513:
case 514:
case 515:
return 12;
case 304:
case 313:
return 13;
case 503:
case 504:
case 507:
case 508:
return 14;
case 200:
case 201:
case 202:
case 203:
case 204:
return 15;
case 205:
case 206:
case 207:
case 208:
return 16;
case 209:
case 210:
case 211:
return 17;
case 212:
return 18;
case 231:
return 19;
default:
return 3;
}
}
}

View File

@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.fitpro.FitProDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.banglejs.BangleJSDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.CasioGB6900DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.CasioGBX100DeviceSupport;
@ -354,6 +355,9 @@ public class DeviceSupportFactory {
case DOMYOS_T540:
deviceSupport = new ServiceDeviceSupport(new DomyosT540Support(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case FITPRO:
deviceSupport = new ServiceDeviceSupport(new FitProDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
}
if (deviceSupport != null) {
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);

View File

@ -80,4 +80,18 @@ public class ArrayUtils {
public static boolean startsWith(byte[] array, byte[] values) {
return equals(array, values, 0);
}
/**
* Converts an array to string representation
*
* @param array the array to convert
* @return
*/
public static String arrayToString(byte[] array) {
StringBuilder stringBuilder = new StringBuilder();
for (byte i : array) {
stringBuilder.append(String.format("0x%02X ", i));
}
return stringBuilder.toString();
}
}

View File

@ -43,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.fitpro.FitProDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.casio.gb6900.CasioGB6900DeviceCoordinator;
@ -304,7 +305,7 @@ public class DeviceHelper {
result.add(new SMAQ2OSSCoordinator());
result.add(new UM25Coordinator());
result.add(new DomyosT540Cooridnator());
result.add(new FitProDeviceCoordinator());
return result;
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#7E7E7E">
<path
android:fillColor="@android:color/white"
android:pathData="M20,8V6c0,-1.65 -1.35,-3 -3,-3H7C5.35,3 4,4.35 4,6v2c-1.65,0 -3,1.35 -3,3v5c0,1.65 1.35,3 3,3v1c0,0.55 0.45,1 1,1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1c0.55,0 1,-0.45 1,-1v-1c1.65,0 3,-1.35 3,-3v-5C23,9.35 21.65,8 20,8zM6,6c0,-0.55 0.45,-1 1,-1h10c0.55,0 1,0.45 1,1v2.78c-0.61,0.55 -1,1.34 -1,2.22v2H7v-2c0,-0.88 -0.39,-1.67 -1,-2.22V6zM21,16c0,0.55 -0.45,1 -1,1H4c-0.55,0 -1,-0.45 -1,-1v-5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v4h14v-4c0,-0.55 0.45,-1 1,-1s1,0.45 1,1V16z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#7E7E7E"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,16v-5c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6z" />
</vector>

View File

@ -52,6 +52,34 @@
<item>sk</item>
</string-array>
<string-array name="pref_fitpro_language_options">
<item>@string/automatic</item>
<item>@string/simplified_chinese</item>
<item>@string/english</item>
<item>@string/turkish</item>
<item>@string/polish</item>
<item>@string/portuguese</item>
<item>@string/russian</item>
<item>@string/spanish</item>
<item>@string/german</item>
<item>@string/french</item>
<item>@string/czesh</item>
</string-array>
<string-array name="pref_fitpro_language_values">
<item>auto</item>
<item>zh_CN</item>
<item>en_US</item>
<item>tr_TR</item>
<item>pl_PL</item>
<item>pt_BR</item>
<item>ru_RU</item>
<item>es_ES</item>
<item>de_DE</item>
<item>fr_FR</item>
<item>cs_CZ</item>
</string-array>
<string-array name="notification_mode">
<item>@string/always</item>
<item>@string/when_screen_off</item>
@ -1724,4 +1752,28 @@
<item>@string/p_menuitem_goal</item>
</string-array>
<string-array name="inactivity_minutes">
<item>45</item>
<item>60</item>
<item>75</item>
<item>90</item>
<item>105</item>
<item>120</item>
</string-array>
<string-array name="inactivity_minutes_values">
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
</string-array>
<string-array name="autoheartrate_interval_hours">
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
</resources>

View File

@ -876,6 +876,7 @@
<string name="devicetype_sonyswr12" translatable="false">Sony SWR12</string>
<string name="devicetype_waspos">Wasp-os</string>
<string name="devicetype_smaq2oss">SMA-Q2 OSS</string>
<string name="devicetype_fitpro">FitPro</string>
<string name="devicetype_domyos_t540"/>
<string name="choose_auto_export_location">Choose export location</string>
<string name="notification_channel_name">General</string>
@ -1257,4 +1258,16 @@
<string name="watchface_setting_title_power_saving">Power saving</string>
<string name="watchface_setting_power_saving_display">Disable display updates while off wrist</string>
<string name="watchface_setting_power_saving_hands">Disable hands movement while off wrist</string>
<string name="prefs_sleep_time">Sleep times</string>
<string name="prefs_sleep_time_label">Define sleep hours</string>
<string name="prefs_sleep_time_summary">Specifies times when sleep is registered</string>
<string name="prefs_vibration_enable">Enable vibrations</string>
<string name="prefs_notifications_enable">Enable notifications</string>
<string name="enable_vibrations_summary">Vibrations for calls, messages, notifications and more</string>
<string name="enable_notifications_summary">Notifications for calls, messages and more</string>
<string name="prefs_autoheartrate">Automatic Heart Rate</string>
<string name="prefs_autoheartrate_summary">Periodical Heart Rate measurements during the day and also while asleep</string>
<string name="prefs_autoheartrate_measurement">Automatic Heart Rate measurements</string>
<string name="prefs_autoheartrate_sleep">Take measurements during sleep</string>
<string name="prefs_autoheartrate_interval">Frequency of measurements</string>
</resources>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_heartrate"
android:key="screen_autoheartrate"
android:persistent="false"
android:summary="@string/prefs_autoheartrate_summary"
android:title="@string/prefs_autoheartrate">
<!-- workaround for missing toolbar -->
<PreferenceCategory android:title="@string/prefs_autoheartrate_summary" />
<SwitchPreference
android:defaultValue="false"
android:key="pref_autoheartrate_switch"
android:title="@string/prefs_autoheartrate_measurement" />
<SwitchPreference
android:defaultValue="false"
android:dependency="pref_autoheartrate_switch"
android:key="pref_autoheartrate_sleep"
android:title="@string/prefs_autoheartrate_sleep" />
<ListPreference
android:defaultValue="2"
android:dependency="pref_autoheartrate_switch"
android:entries="@array/autoheartrate_interval_hours"
android:entryValues="@array/autoheartrate_interval_hours"
android:key="pref_autoheartrate_interval"
android:summary="%s"
android:title="@string/prefs_autoheartrate_interval" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="06:00"
android:key="pref_autoheartrate_start"
android:title="@string/mi2_prefs_do_not_disturb_start" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="23:00"
android:key="pref_autoheartrate_end"
android:title="@string/mi2_prefs_do_not_disturb_end" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -19,7 +19,7 @@
android:title="@string/mi2_prefs_do_not_disturb" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="01:00"
android:defaultValue="22:00"
android:key="do_not_disturb_no_auto_start"
android:title="@string/mi2_prefs_do_not_disturb_start" />

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue="auto"
android:entries="@array/pref_fitpro_language_options"
android:entryValues="@array/pref_fitpro_language_values"
android:icon="@drawable/ic_language"
android:key="language"
android:summary="%s"
android:title="@string/pref_title_language" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_arrow_upward"
android:key="screen_liftwrist_display"
android:persistent="false"
android:title="@string/mi2_prefs_activate_display_on_lift">
<!-- workaround for missing toolbar -->
<PreferenceCategory android:title="@string/mi2_prefs_activate_display_on_lift" />
<ListPreference
android:defaultValue="@string/p_off"
android:enabled="true"
android:entries="@array/do_not_disturb_no_auto"
android:entryValues="@array/do_not_disturb_no_auto_values"
android:key="activate_display_on_lift_wrist"
android:summary="%s"
android:title="@string/mi2_prefs_activate_display_on_lift" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="06:00"
android:key="display_on_lift_start"
android:title="@string/mi2_prefs_do_not_disturb_start" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="23:00"
android:key="display_on_lift_end"
android:title="@string/mi2_prefs_do_not_disturb_end" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_activity_sleep"
android:icon="@drawable/ic_chair"
android:key="screen_longsit"
android:persistent="false"
android:summary="@string/mi2_prefs_inactivity_warnings_summary"

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_chair"
android:key="screen_longsit"
android:persistent="false"
android:summary="@string/mi2_prefs_inactivity_warnings_summary"
android:title="@string/mi2_prefs_inactivity_warnings">
<!-- workaround for missing toolbar -->
<PreferenceCategory android:title="@string/mi2_prefs_inactivity_warnings_summary" />
<SwitchPreference
android:defaultValue="false"
android:key="pref_longsit_switch"
android:title="@string/mi2_prefs_inactivity_warnings" />
<ListPreference
android:defaultValue="4"
android:dependency="pref_longsit_switch"
android:entries="@array/inactivity_minutes"
android:entryValues="@array/inactivity_minutes_values"
android:key="pref_longsit_period"
android:summary="@string/mi2_prefs_inactivity_warnings_summary"
android:title="@string/mi2_prefs_inactivity_warnings_threshold" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="08:00"
android:key="pref_longsit_start"
android:title="@string/mi2_prefs_do_not_disturb_start" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="16:00"
android:key="pref_longsit_end"
android:title="@string/mi2_prefs_do_not_disturb_end" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:icon="@drawable/ic_activity_sleep"
android:icon="@drawable/ic_chair"
android:defaultValue="false"
android:key="screen_longsit_noshed"
android:title="@string/mi2_prefs_inactivity_warnings"

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_notifications"
android:key="notification_enable"
android:summary="@string/enable_notifications_summary"
android:title="@string/prefs_notifications_enable" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_access_time"
android:key="prefs_sleep_time"
android:persistent="false"
android:summary="@string/prefs_sleep_time_summary"
android:title="@string/prefs_sleep_time">
<!-- workaround for missing toolbar -->
<PreferenceCategory android:title="@string/prefs_sleep_time_label" />
<ListPreference
android:defaultValue="@string/p_off"
android:enabled="true"
android:entries="@array/do_not_disturb_no_auto"
android:entryValues="@array/do_not_disturb_no_auto_values"
android:key="prefs_enable_sleep_time"
android:summary="%s"
android:title="@string/prefs_sleep_time" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="22:00"
android:key="prefs_sleep_time_start"
android:title="@string/mi2_prefs_do_not_disturb_start" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="06:00"
android:key="prefs_sleep_time_end"
android:title="@string/mi2_prefs_do_not_disturb_end" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_vibration"
android:key="vibration_enable"
android:summary="@string/enable_vibrations_summary"
android:title="@string/prefs_vibration_enable" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,50 @@
package nodomain.freeyourgadget.gadgetbridge.test;
import org.junit.Test;
public class FitProTests extends TestBase {
// since unit tests seem to be broken in Android Studio these are not complete
// but but at least this provides some help for the future
// can be used this way:
@Test
public void testSleepPacket() {
String sleepData = "cd002915010300242b1f0008000f0003001e0002002d0001003c0001004b0001005a00010069000200780002";
//handleSleepData(stringToByteArray(sleepData));
}
@Test
public void testStepPacket() {
//note different format of data and thus different stringWith0xToByteArray method
String stepData = "0xcd 0x00 0x31 0x15 0x01 0x02 0x00 0x2c 0x2b 0x34 0x00 0x05 0x80 0x45 0x07 0x1a 0x09 0xc8 0xbb 0x06 0x79 0x55 0x06 0x26 0x09 0xe8 0xb0 0xe4 0x71 0x15 0x05 0x9b 0x0a 0x08 0xa4 0xdc 0x7b 0x85 0x06 0x67 0x0a 0x28 0xb4 0x16 0x76 0x35 0x06 0x16 0x0a 0x48 0xac 0x57";
//handleStepData(stringWith0xToByteArray(stepData));
}
public byte[] stringToByteArray(String s) {
byte[] byteArray = new byte[s.length() / 2];
String[] strBytes = new String[s.length() / 2];
int k = 0;
for (int i = 0; i < s.length(); i = i + 2) {
int j = i + 2;
strBytes[k] = s.substring(i, j);
byteArray[k] = (byte) Integer.parseInt(strBytes[k], 16);
k++;
}
return byteArray;
}
public static byte[] stringWith0xToByteArray(String s) {
String[] split = s.split(" ");
int k = 0;
byte[] byteArray = new byte[split.length];
for (String ch : split) {
byteArray[k] = (byte) Integer.parseInt(ch.split("x")[1], 16);
k++;
}
return byteArray;
}
}