From f7abe2d4a35b142ba78f509f528d87bb5db1eaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Sat, 15 Jul 2017 21:48:26 +0100 Subject: [PATCH] Mi Band 2: Inactivity Warnings --- .../devices/miband/MiBand2Coordinator.java | 48 ++++++-- .../devices/miband/MiBand2Service.java | 16 +++ .../devices/miband/MiBandConst.java | 7 ++ .../miband/MiBandPreferencesActivity.java | 106 ++++++++++++++++++ .../devices/miband2/MiBand2Support.java | 65 +++++++++++ app/src/main/res/values-pt/strings.xml | 4 + app/src/main/res/values/strings.xml | 4 + app/src/main/res/xml/miband_preferences.xml | 57 ++++++++++ 8 files changed, 295 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java index 535da565..7cc26721 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Coordinator.java @@ -47,9 +47,6 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_END; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_START; - public class MiBand2Coordinator extends MiBandCoordinator { private static final Logger LOG = LoggerFactory.getLogger(MiBand2Coordinator.class); @@ -139,27 +136,54 @@ public class MiBand2Coordinator extends MiBandCoordinator { return prefs.getBoolean(MiBandConst.PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO, false); } - public static Date getDoNotDisturbStart() { + public static boolean getInactivityWarnings() { Prefs prefs = GBApplication.getPrefs(); - String time = prefs.getString(PREF_MI2_DO_NOT_DISTURB_START, "01:00"); + return prefs.getBoolean(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS, false); + } - DateFormat df = new SimpleDateFormat("HH:mm"); - try { - return df.parse(time); - } catch(Exception e) { - } + public static int getInactivityWarningsThreshold() { + Prefs prefs = GBApplication.getPrefs(); + return prefs.getInt(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD, 60); + } - return new Date(); + public static boolean getInactivityWarningsDnd() { + Prefs prefs = GBApplication.getPrefs(); + return prefs.getBoolean(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND, false); + } + + public static Date getInactivityWarningsStart() { + return getTimePreference(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_START, "06:00"); + } + + public static Date getInactivityWarningsEnd() { + return getTimePreference(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_END, "22:00"); + } + + public static Date getInactivityWarningsDndStart() { + return getTimePreference(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND_START, "12:00"); + } + + public static Date getInactivityWarningsDndEnd() { + return getTimePreference(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND_END, "14:00"); + } + + public static Date getDoNotDisturbStart() { + return getTimePreference(MiBandConst.PREF_MI2_DO_NOT_DISTURB_START, "01:00"); } public static Date getDoNotDisturbEnd() { + return getTimePreference(MiBandConst.PREF_MI2_DO_NOT_DISTURB_END, "06:00"); + } + + public static Date getTimePreference(String key, String defaultValue) { Prefs prefs = GBApplication.getPrefs(); - String time = prefs.getString(PREF_MI2_DO_NOT_DISTURB_END, "06:00"); + String time = prefs.getString(key, defaultValue); DateFormat df = new SimpleDateFormat("HH:mm"); try { return df.parse(time); } catch(Exception e) { + LOG.error("Unexpected exception in MiBand2Coordinator.getTime: " + e.getMessage()); } return new Date(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index 3442cecb..90fb9c7b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -174,6 +174,22 @@ public class MiBand2Service { public static final byte[] DISPLAY_XXX = new byte[] {ENDPOINT_DISPLAY, 0x03, 0x0, 0x0 }; public static final byte[] DISPLAY_YYY = new byte[] {ENDPOINT_DISPLAY, 0x10, 0x0, 0x1, 0x1 }; + // The third byte controls the threshold, in minutes + // The last 8 bytes represent 2 separate time intervals for the inactivity warnings + // If there is no do not disturb interval, the last 4 bytes (the second interval) are 0 + // and only the first interval of the command is used + public static int INACTIVITY_WARNINGS_THRESHOLD = 2; + public static int INACTIVITY_WARNINGS_INTERVAL_1_START_HOURS = 4; + public static int INACTIVITY_WARNINGS_INTERVAL_1_START_MINUTES = 5; + public static int INACTIVITY_WARNINGS_INTERVAL_1_END_HOURS = 6; + public static int INACTIVITY_WARNINGS_INTERVAL_1_END_MINUTES = 7; + public static int INACTIVITY_WARNINGS_INTERVAL_2_START_HOURS = 8; + public static int INACTIVITY_WARNINGS_INTERVAL_2_START_MINUTES = 9; + public static int INACTIVITY_WARNINGS_INTERVAL_2_END_HOURS = 10; + public static int INACTIVITY_WARNINGS_INTERVAL_2_END_MINUTES = 11; + public static final byte[] COMMAND_ENABLE_INACTIVITY_WARNINGS = new byte[] { 0x08, 0x01, 0x3c, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 }; + public static final byte[] COMMAND_DISABLE_INACTIVITY_WARNINGS = new byte[] { 0x08, 0x00, 0x3c, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 }; + public static byte ENDPOINT_DND = 0x09; public static final byte[] COMMAND_DO_NOT_DISTURB_AUTOMATIC = new byte[] { ENDPOINT_DND, (byte) 0x83 }; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java index a9bffb2c..cba053df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java @@ -52,6 +52,13 @@ public final class MiBandConst { public static final String PREF_MI2_DO_NOT_DISTURB_SCHEDULED = "scheduled"; public static final String PREF_MI2_DO_NOT_DISTURB_START = "mi2_do_not_disturb_start"; public static final String PREF_MI2_DO_NOT_DISTURB_END = "mi2_do_not_disturb_end"; + public static final String PREF_MI2_INACTIVITY_WARNINGS = "mi2_inactivity_warnings"; + public static final String PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD = "mi2_inactivity_warnings_threshold"; + public static final String PREF_MI2_INACTIVITY_WARNINGS_START = "mi2_inactivity_warnings_start"; + public static final String PREF_MI2_INACTIVITY_WARNINGS_END = "mi2_inactivity_warnings_end"; + public static final String PREF_MI2_INACTIVITY_WARNINGS_DND = "mi2_inactivity_warnings_dnd"; + public static final String PREF_MI2_INACTIVITY_WARNINGS_DND_START = "mi2_inactivity_warnings_dnd_start"; + public static final String PREF_MI2_INACTIVITY_WARNINGS_DND_END = "mi2_inactivity_warnings_dnd_end"; public static final String PREF_MIBAND_SETUP_BT_PAIRING = "mi_setup_bt_pairing"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java index 124608a2..9aabf77e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java @@ -48,6 +48,13 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_START; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ENABLE_TEXT_NOTIFICATIONS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_GOAL_NOTIFICATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND_END; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND_START; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_END; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_START; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ADDRESS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS; @@ -147,6 +154,104 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { } }); + final Preference inactivityWarnings = findPreference(PREF_MI2_INACTIVITY_WARNINGS); + inactivityWarnings.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_INACTIVITY_WARNINGS); + } + }); + return true; + } + }); + + final Preference inactivityWarningsThreshold = findPreference(PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD); + inactivityWarningsThreshold.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD); + } + }); + return true; + } + }); + + final Preference inactivityWarningsStart = findPreference(PREF_MI2_INACTIVITY_WARNINGS_START); + inactivityWarningsStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_INACTIVITY_WARNINGS_START); + } + }); + return true; + } + }); + + final Preference inactivityWarningsEnd = findPreference(PREF_MI2_INACTIVITY_WARNINGS_END); + inactivityWarningsEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_INACTIVITY_WARNINGS_END); + } + }); + return true; + } + }); + + final Preference inactivityWarningsDnd = findPreference(PREF_MI2_INACTIVITY_WARNINGS_DND); + inactivityWarningsDnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_INACTIVITY_WARNINGS_DND); + } + }); + return true; + } + }); + + final Preference inactivityWarningsDndStart = findPreference(PREF_MI2_INACTIVITY_WARNINGS_DND_START); + inactivityWarningsDndStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_INACTIVITY_WARNINGS_DND_START); + } + }); + return true; + } + }); + + final Preference inactivityWarningsDndEnd = findPreference(PREF_MI2_INACTIVITY_WARNINGS_DND_END); + inactivityWarningsDndEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_INACTIVITY_WARNINGS_DND_END); + } + }); + return true; + } + }); + String doNotDisturbState = prefs.getString(MiBandConst.PREF_MI2_DO_NOT_DISTURB, PREF_MI2_DO_NOT_DISTURB_OFF); boolean doNotDisturbScheduled = doNotDisturbState.equals(PREF_MI2_DO_NOT_DISTURB_SCHEDULED); @@ -270,6 +375,7 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { prefKeys.add(PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR); prefKeys.add(PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS); prefKeys.add(PREF_MI2_ENABLE_TEXT_NOTIFICATIONS); + prefKeys.add(PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD); prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_ALARM_CLOCK)); prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index 13878293..3307e766 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -1097,6 +1097,16 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { case MiBandConst.PREF_MI2_DO_NOT_DISTURB_START: case MiBandConst.PREF_MI2_DO_NOT_DISTURB_END: setDoNotDisturb(builder); + break; + case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS: + case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD: + case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_START: + case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_END: + case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND: + case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND_START: + case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND_END: + setInactivityWarnings(builder); + break; } builder.queue(getQueue()); } catch (IOException e) { @@ -1238,6 +1248,60 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return this; } + private MiBand2Support setInactivityWarnings(TransactionBuilder builder) { + boolean enable = MiBand2Coordinator.getInactivityWarnings(); + LOG.info("Setting inactivity warnings to " + enable); + + if (enable) { + byte[] data = MiBand2Service.COMMAND_ENABLE_INACTIVITY_WARNINGS.clone(); + + int threshold = MiBand2Coordinator.getInactivityWarningsThreshold(); + data[MiBand2Service.INACTIVITY_WARNINGS_THRESHOLD] = (byte) threshold; + + Calendar calendar = GregorianCalendar.getInstance(); + + boolean enableDnd = MiBand2Coordinator.getInactivityWarningsDnd(); + + Date intervalStart = MiBand2Coordinator.getInactivityWarningsStart(); + Date intervalEnd = MiBand2Coordinator.getInactivityWarningsEnd(); + Date dndStart = MiBand2Coordinator.getInactivityWarningsDndStart(); + Date dndEnd = MiBand2Coordinator.getInactivityWarningsDndEnd(); + + // The first interval always starts when the warnings interval starts + calendar.setTime(intervalStart); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE); + + if(enableDnd) { + // The first interval ends when the dnd interval starts + calendar.setTime(dndStart); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE); + + // The second interval starts when the dnd interval ends + calendar.setTime(dndEnd); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE); + + // ... and it ends when the warnings interval ends + calendar.setTime(intervalEnd); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE); + } else { + // No Dnd, use the first interval + calendar.setTime(intervalEnd); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY); + data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE); + } + + builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), data); + } else { + builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISABLE_INACTIVITY_WARNINGS); + } + + return this; + } + public void phase2Initialize(TransactionBuilder builder) { LOG.info("phase2Initialize..."); enableFurtherNotifications(builder, true); @@ -1251,6 +1315,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { setRotateWristToSwitchInfo(builder); setActivateDisplayOnLiftWrist(builder); setGoalNotification(builder); + setInactivityWarnings(builder); setHeartrateSleepSupport(builder); } } diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 3516f7d2..fa52294b 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -310,6 +310,10 @@ A pulseira não recebe notificações enquanto activo Hora de início Hora de fim + Avisos de inactividade + A pulseira irá vibrar quando estiver inactivo durante algum tempo + Limite de inactividade (em minutos) + Desactivar os avisos de inactividade durante um intervalo de tempo Prestes a transferir dados desde %1$s aguarde para tornar a ligar Sobre você diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4bc1ccf0..5989ccd6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -354,6 +354,10 @@ Rotate wrist to switch info Do Not Disturb The band won\'t receive notifications while active + Inactivity warnings + The band will vibrate when you have been inactive for a while + Inactivity threshold (in minutes) + Disable the inactivity warnings for a time interval Start time End time About to transfer data since %1$s diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index e7338045..8419ab9f 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -84,6 +84,63 @@ android:title="@string/miband2_prefs_dateformat" android:summary="%s" /> + + + + + + + + + + + + + + + + + + + +