diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 9ad6d7dc0..69a413916 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -54,6 +54,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; +import nodomain.freeyourgadget.gadgetbridge.devices.lenovo.watchxplus.WatchXPlusConstants; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimePreferenceActivity; import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsPreferencesActivity; @@ -210,26 +211,52 @@ public class SettingsActivity extends AbstractSettingsActivity { }); - pref = findPreference("wxp_button_BP_calibration"); + pref = findPreference("wxp_button_BP_calibration_list"); pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - preference.setSummary("Calibrating, please wait... (if no result after 15s. re-run)"); + //preference.setSummary("Calibrating, please wait... (if no result after 15s. re-run)"); GBApplication.deviceService().onSendConfiguration("BP_CAL"); return true; } }); + pref = findPreference("watchxplus_longsit_period"); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + GBApplication.deviceService().onSendConfiguration("LONG_SIT"); + return true; + } + }); + + pref = findPreference("watchxplus_longsit_switch"); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + GBApplication.deviceService().onSendConfiguration("LONG_SIT"); + return true; + } + }); + pref = findPreference("wxp_power_mode"); pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - LOG.info(" uhaaaa "); GBApplication.deviceService().onSendConfiguration("WXP_POWER_MODE"); return true; } }); + pref = findPreference(WatchXPlusConstants.PREF_WXP_LANGUAGE); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + GBApplication.deviceService().onSendConfiguration("WXP_LANGUAGE"); + return true; + } + }); + final Preference unit = findPreference(PREF_MEASUREMENT_SYSTEM); unit.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java index 424eb2ab3..f73f68a6c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusConstants.java @@ -38,14 +38,19 @@ public final class WatchXPlusConstants extends LenovoWatchConstants { public static final String PREF_REPEAT = "watchxplus_repeat"; public static final String PREF_CONTINIOUS = "watchxplus_continious"; public static final String PREF_MISSED_CALL = "watchxplus_missed"; + public static final String PREF_MISSED_CALL_REPEAT = "watchxplus_repeat_missedcall"; public static final String PREF_IS_BP_CALIBRATED = "watchxplus_is_bp_calibrated"; public static final String PREF_BUTTON_REJECT = "watchxplus_button_reject"; public static final String PREF_SHAKE_REJECT = "watchxplus_shake_reject"; public static final String PREF_BP_CAL_LOW = "pref_wxp_bp_calibration_low"; public static final String PREF_BP_CAL_HIGH = "pref_wxp_bp_calibration_high"; + public static final String PREF_BP_CAL_SWITCH = "wxp_button_BP_calibration_list"; public static final String PREF_DO_NOT_DISTURB = "do_not_disturb_no_auto"; public static final String PREF_DO_NOT_DISTURB_START = "do_not_disturb_no_auto_start"; public static final String PREF_DO_NOT_DISTURB_END = "do_not_disturb_no_auto_end"; + public static final String PREF_LONGSIT_SWITCH = "watchxplus_longsit_switch"; + public static final String PREF_LONGSIT_PERIOD = "watchxplus_longsit_period"; + public static final String PREF_WXP_LANGUAGE = "wxp_language_pref"; // time format constants public static final byte ARG_SET_TIMEMODE_24H = 0x00; @@ -72,6 +77,8 @@ public final class WatchXPlusConstants extends LenovoWatchConstants { public static final byte[] CMD_SET_QUITE_HOURS_TIME = new byte[]{0x03, 0x62}; public static final byte[] CMD_SET_QUITE_HOURS_SWITCH = new byte[]{0x03, 0x61}; public static final byte[] CMD_SET_PERSONAL_INFO = new byte[]{0x01, 0x0E}; + public static final byte[] CMD_INACTIVITY_REMINDER_SWITCH = new byte[]{0x03, 0x51}; + public static final byte[] CMD_INACTIVITY_REMINDER_SET = new byte[]{0x03, 0x52}; public static final byte[] CMD_FITNESS_GOAL_SETTINGS = new byte[]{0x10, 0x02}; public static final byte[] CMD_DAY_STEPS_INFO = new byte[]{0x10, 0x03}; @@ -87,7 +94,9 @@ public final class WatchXPlusConstants extends LenovoWatchConstants { public static final byte[] RESP_BUTTON_WHILE_RING = new byte[]{0x04, 0x03, 0x03}; public static final byte[] RESP_BP_CALIBRATION = new byte[]{0x08, 0x05, 0x0C}; public static final byte[] RESP_SET_PERSONAL_INFO = new byte[]{0x08, 0x01, 0x0E}; - public static final byte[] RESP_GOAL_AIM_STATUS = new byte[]{0x08, 0x10, 0x02}; + public static final byte[] RESP_GOAL_AIM_STATUS = new byte[]{0x08, 0x10, 0x02}; + public static final byte[] RESP_INACTIVITY_REMINDER_SWITCH = new byte[]{0x08, 0x03, 0x51}; + public static final byte[] RESP_INACTIVITY_REMINDER_SET = new byte[]{0x08, 0x03, 0x52}; public static final byte[] RESP_AUTHORIZATION_TASK = new byte[]{0x01, 0x01, 0x05}; public static final byte[] RESP_DAY_STEPS_INDICATOR = new byte[]{0x08, 0x10, 0x03}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java index 6f64f5807..977ba302d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusDeviceCoordinator.java @@ -178,7 +178,7 @@ public class WatchXPlusDeviceCoordinator extends AbstractDeviceCoordinator { /* Prefs from device settings on main page */ -// return saved time format +// return time format pref public static byte getTimeMode(SharedPreferences sharedPrefs) { String timeMode = sharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_TIMEFORMAT, getContext().getString(R.string.p_timeformat_24h)); if (timeMode.equals(getContext().getString(R.string.p_timeformat_24h))) { @@ -188,6 +188,12 @@ Prefs from device settings on main page } } +// return watch language pref + public static byte getLanguage(SharedPreferences sharedPrefs) { + int settingRead = prefs.getInt(WatchXPlusConstants.PREF_WXP_LANGUAGE, 1); + return (byte) settingRead; + } + // check if it is needed to toggle Lift Wrist to Sreen on public static boolean shouldEnableHeadsUpScreen(SharedPreferences sharedPrefs) { String liftMode = sharedPrefs.getString(WatchXPlusConstants.PREF_ACTIVATE_DISPLAY, getContext().getString(R.string.p_on)); @@ -260,6 +266,34 @@ Prefs from device settings on main page } } + /** + * @param startOut out Only hour/minute are used. + * @param endOut out Only hour/minute are used. + * @return True if quite hours are enabled. + */ + public static boolean getLongSitHours(SharedPreferences sharedPrefs, Calendar startOut, Calendar endOut) { + boolean enabled = prefs.getBoolean(WatchXPlusConstants.PREF_LONGSIT_SWITCH, false); + + if (!enabled) { + LOG.info(" DND is disabled "); + return false; + } else { + String start = sharedPrefs.getString(WatchXPlusConstants.PREF_DO_NOT_DISTURB_START, "00:00"); + String end = sharedPrefs.getString(WatchXPlusConstants.PREF_DO_NOT_DISTURB_END, "00:00"); + + DateFormat df = new SimpleDateFormat("HH:mm"); + + try { + startOut.setTime(df.parse(start)); + endOut.setTime(df.parse(end)); + + return true; + } catch (Exception e) { + return false; + } + } + } + /* Values from device specific settings page */ @@ -283,6 +317,12 @@ Values from device specific settings page return (boolean) prefs.getBoolean(WatchXPlusConstants.PREF_MISSED_CALL, false); } +//read missed call notification + public static int getMissedCallRepeat(String address) { + return (int) prefs.getInt(WatchXPlusConstants.PREF_MISSED_CALL_REPEAT, 0); + } + + //read button reject call settings public static boolean getButtonReject(String address) { return (boolean) prefs.getBoolean(WatchXPlusConstants.PREF_BUTTON_REJECT, false); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WorkProgress b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WorkProgress index e34cb75e0..f44d63653 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WorkProgress +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WorkProgress @@ -1,11 +1,9 @@ -NEDD TO BE DONE +NEED TO BE DONE Watch settings - - Set watch language (currently forced to English) - Set watch units (metric/imperial) - Implement temperature alarm on watch - switch, lowTemp, highTemp - - Implement continious blood pressure measurement (on, off, scheduled) - - Implement long sit reminder + - Implement continuous blood pressure measurement (on, off, scheduled) Add feature to initiate button press event on watch - Send command to watch @@ -14,7 +12,7 @@ NEDD TO BE DONE Schedulers: - Screen on scheduler (inApp, not supported by watch) - Disconnect reminder scheduler (inApp, not supported by watch) - - Continious blood pressure measurement (supported by watch) + - Continuous blood pressure measurement (supported by watch, there are command for that, but not tested) Refine send weather to watch - Send weather icon @@ -23,11 +21,11 @@ NEDD TO BE DONE - Fix get sleep data Measurements - - Blood pressure mesurement + - Blood pressure measurement - Show blood pressure measurement (view) - - Implement heart rate measurement - - Implement temperature measurement - - Implement UV index measurement + - Implement heart rate measurement //tried to implement with no luck + - Implement temperature measurement //tried to implement with no luck + - Implement UV index measurement //tried to implement with no luck @@ -39,6 +37,7 @@ WORK PROGRESS - settings for repeat notification [0-10 times] (05.11.2019) - settings for continious notification while phone ring [on, off] (06.11.2019) - settings for send once notification for missed call [on, off] (06.11.2019) + * send missed call notification every minute for X times (17.11.2019) - On text message, or other application - On triger phone alarm (05.11.2019) @@ -55,7 +54,7 @@ WORK PROGRESS * it's used in Climb activity - Status of blood pressure calibration (06.11.2019) * it's used in blood pressure measurement - - Blood pressure calibration (09.11.2019) (TODO fix button initiating calibration) + - Blood pressure calibration (09.11.2019) Device settings - Lift wrist to screen on [on, off,TODO scheduled] (02.11.2019) @@ -68,10 +67,12 @@ WORK PROGRESS - Trad-watch mode -> the watch only works as an analog one - Do not disturb [on, off, scheduled] (10.11.2019) (need reconnect to apply) - Send User details to watch [height, weight, age, gender] (10.11.2019) (need more testing) + - Implemented long sit reminder (inactivity reminder)[on, off, period] (17.11.2019) + - Set watch language [English, Chinese] (17.11.2019) Activity data - get steps per day - - get heart reate measurements + - get heart rate measurements - get sleep data - set user goal for steps diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java index 1fd5186a2..bb6eb6f7b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/lenovo/watchxplus/WatchXPlusDeviceSupport.java @@ -17,10 +17,12 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.lenovo.watchxplus; +import android.app.AlertDialog; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.BroadcastReceiver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; @@ -29,6 +31,7 @@ import android.os.Handler; import android.widget.Toast; import androidx.annotation.IntRange; +import androidx.annotation.MainThread; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -51,6 +54,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.DebugActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; @@ -155,7 +159,6 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } catch (IOException e) { e.printStackTrace(); } - return builder; } @@ -185,15 +188,17 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { sendNotification(WatchXPlusConstants.NOTIFICATION_CHANNEL_DEFAULT, message); } -// cancel notification -// cancel watch notification - stop vibration and turn off screen + /** Cancel notification + * cancel watch notification - stop vibration and turn off screen + * on watch - clear phone icon near bluetooth + */ private void cancelNotification() { try { getQueue().clear(); TransactionBuilder builder = performInitialized("cancelNotification"); byte[] bArr; - int mPosition = 1024; - int mMessageId = 0xFF; + int mPosition = 1024; // all positions + int mMessageId = 0xFF; // all messages bArr = new byte[6]; bArr[0] = (byte) ((int) (mPosition >> 24)); bArr[1] = (byte) ((int) (mPosition >> 16)); @@ -211,6 +216,10 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } } + /** Format text and send it to watch + * @param notificationChannel - text or call + * @param notificationText - text to show + */ private void sendNotification(int notificationChannel, String notificationText) { try { TransactionBuilder builder = performInitialized("showNotification"); @@ -251,7 +260,11 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } } - + /** enable notification channels on watch + * @param builder + * enable all notification channels + * TODO add settings to choose notification channels + */ private WatchXPlusDeviceSupport enableNotificationChannels(TransactionBuilder builder) { builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), buildCommand(WatchXPlusConstants.CMD_NOTIFICATION_SETTINGS, @@ -368,6 +381,9 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } } + /** send command to request watch firmware version + * @param builder + */ public WatchXPlusDeviceSupport getFirmwareVersion(TransactionBuilder builder) { builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), buildCommand(WatchXPlusConstants.CMD_FIRMWARE_INFO, @@ -376,6 +392,9 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } + /** send command to request watch battery state + * @param builder + */ private WatchXPlusDeviceSupport getBatteryState(TransactionBuilder builder) { builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), buildCommand(WatchXPlusConstants.CMD_BATTERY_INFO, @@ -384,21 +403,24 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } + /** initialize device on connect + * @param builder + */ public WatchXPlusDeviceSupport initialize(TransactionBuilder builder) { getFirmwareVersion(builder) .getBatteryState(builder) .enableNotificationChannels(builder) - .setFitnessGoal(builder) - .getBloodPressureCalibrationStatus(builder) - .syncPreferences(builder); + .setFitnessGoal(builder) // set steps per day + .getBloodPressureCalibrationStatus(builder) // request blood pressure calibration + .syncPreferences(builder); // read preferences from app and set them to watch builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); builder.setGattCallback(this); - return this; } @Override public void onDeleteNotification(int id) { + isMissedCall = false; cancelNotification(); } @@ -450,23 +472,39 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } } + /** send notification on watch when phone rings + * @param callSpec - phone state + * send notification on incoming call, cancel notification when call is answered, ignored or rejected + * send missed call notification (if enabled from settings) when phone state changed from ringing to end call + * TODO add missed call reminder (send notification to watch at desired period) + */ // variables to handle ring notifications - boolean isRinging = false; - int remainingRepeats = 0; + boolean isRinging = false; // store ringing state + boolean outCall = false; // store outgoing call state + boolean isMissedCall = false; // missed call state + int remainingRepeats = 0; // initialize call notification reminds + int remainingMissedRepeats = 0; // initialize missed call notification reminds @Override public void onSetCallState(final CallSpec callSpec) { - final int repeatDelay = 5000; // repeat delay of 5 sec - // get settings from device settings page - final boolean continiousRing = WatchXPlusDeviceCoordinator.getContiniousVibrationOnCall(getDevice().getAddress()); + final int repeatDelay = 5000; // repeat delay of 5 sec (watch show call notifications for about 5 sec.) + final int repeatMissedDelay = 60000; // repeat missed call delay of 60 sec + // get settings for continuous vibration while phone rings + final boolean continuousRing = WatchXPlusDeviceCoordinator.getContiniousVibrationOnCall(getDevice().getAddress()); + // set settings for missed call boolean missedCall = WatchXPlusDeviceCoordinator.getMissedCallReminder(getDevice().getAddress()); int repeatCount = WatchXPlusDeviceCoordinator.getRepeatOnCall(getDevice().getAddress()); + int repeatCountMissed = WatchXPlusDeviceCoordinator.getMissedCallRepeat(getDevice().getAddress()); // check if repeatCount is in boundaries min=0, max=10 if (repeatCount < 0) repeatCount = 0; if (repeatCount > 10) repeatCount = 10; // limit repeats to 10 + // check if repeatCountMissed is in boundaries min=0, max=10 + if (repeatCountMissed < 0) repeatCountMissed = 0; + if (repeatCountMissed > 10) repeatCountMissed = 10; // limit repeats to 10 switch (callSpec.command) { case CallSpec.CALL_INCOMING: isRinging = true; + isMissedCall = false; remainingRepeats = repeatCount; if (("Phone".equals(callSpec.name)) || (callSpec.name.contains("ropusn")) || (callSpec.name.contains("issed"))) { // do nothing for notifications without caller name, e.g. system call event @@ -478,7 +516,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { handler.postDelayed(new Runnable() { public void run() { // Actions to do after repeatDelay seconds - if (((isRinging) && (remainingRepeats > 0)) || ((isRinging) && (continiousRing))) { + if (((isRinging) && (remainingRepeats > 0)) || ((isRinging) && (continuousRing))) { remainingRepeats = remainingRepeats - 1; sendNotification(WatchXPlusConstants.NOTIFICATION_CHANNEL_PHONE_CALL, callSpec.name); // re-run handler @@ -494,41 +532,76 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { break; case CallSpec.CALL_START: isRinging = false; + outCall = false; + isMissedCall = false; cancelNotification(); break; case CallSpec.CALL_REJECT: isRinging = false; + outCall = false; + isMissedCall = false; cancelNotification(); break; case CallSpec.CALL_ACCEPT: isRinging = false; + outCall = false; + isMissedCall = false; cancelNotification(); break; case CallSpec.CALL_OUTGOING: + outCall = true; isRinging = false; + isMissedCall = false; cancelNotification(); break; case CallSpec.CALL_END: - if (isRinging) { + if ((isRinging) && (!outCall)) { // it's a missed call, don't clear notification to preserve small icon near bluetooth isRinging = false; + outCall = false; + isMissedCall = true; + remainingMissedRepeats = repeatCountMissed; // send missed call notification if enabled in settings if (missedCall) { sendNotification(WatchXPlusConstants.NOTIFICATION_CHANNEL_PHONE_CALL, "Missed call"); + // repeat missed call notification + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + public void run() { + // Actions to do after repeatDelay seconds + if ((isMissedCall) && (remainingMissedRepeats > 0)) { + remainingMissedRepeats = remainingMissedRepeats - 1; + sendNotification(WatchXPlusConstants.NOTIFICATION_CHANNEL_PHONE_CALL, "Missed call"); + // re-run handler + handler.postDelayed(this, repeatMissedDelay); + } else { + remainingMissedRepeats = 0; + isMissedCall = false; + // stop handler + handler.removeCallbacks(this); + } + } + }, repeatMissedDelay); } } else { isRinging = false; + outCall = false; + isMissedCall = false; cancelNotification(); } break; default: isRinging = false; + isMissedCall = false; cancelNotification(); break; } } -// handle button press while ringing + /** handle button press while ringing + * @param value - reply from watch + * while phone rings choose what to do when watch button is pressed + */ private void handleButtonWhenRing(byte[] value) { GBDeviceEventCallControl callCmd = new GBDeviceEventCallControl(); // get saved settings if true - reject call, otherwise ignore call @@ -554,11 +627,17 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { buildCommand(WatchXPlusConstants.CMD_FITNESS_GOAL_SETTINGS, WatchXPlusConstants.WRITE_VALUE, Conversion.toByteArr16(fitnessGoal))); - return this; } -// set personal info + /** set personal info - read it from About me + * @param builder + * @param height - user height in meters + * @param weight - user weight in kg + * @param age - user age + * @param gender - user age + * send personal information on watch + */ private WatchXPlusDeviceSupport setPersonalInformation(TransactionBuilder builder, int height, int weight, int age, int gender) { LOG.warn(" Setting Personal Information... height:"+height+" weight:"+weight+" age:"+age+" gender:"+gender); byte[] command = WatchXPlusConstants.CMD_SET_PERSONAL_INFO; @@ -576,8 +655,10 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } -// handle get/set personal info -// for test purposes only + /** handle get/set personal info + * @param value - reply from watch + * actual do nothing (for test purposes only) + */ private void handlePersonalInfo(byte[] value) { int height = Conversion.fromByteArr16(value[8]); int weight = Conversion.fromByteArr16(value[9]); @@ -585,6 +666,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { int gender = Conversion.fromByteArr16(value[11]); LOG.info(" Personal info - height:" + height + ", weight:" + weight + ", age:" + age + ", gender:" + gender); } + @Override public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { @@ -724,20 +806,25 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { getDisconnectReminderStatus(builder); break; case DeviceSettingsPreferenceConst.PREF_TIMEFORMAT: - setTimeFormat(builder, sharedPreferences); + setLanguageAndTimeFormat(builder, sharedPreferences); break; case WatchXPlusConstants.PREF_DO_NOT_DISTURB: case WatchXPlusConstants.PREF_DO_NOT_DISTURB_START: case WatchXPlusConstants.PREF_DO_NOT_DISTURB_END: - LOG.info(" bravo "); setQuiteHours(builder, sharedPreferences); break; case "BP_CAL": sendBloodPressureCalibration(); break; + case "LONG_SIT": + setLongSitHours(builder, sharedPreferences); + break; case "WXP_POWER_MODE": setPowerMode(config); break; + case "WXP_LANGUAGE": + setLanguageAndTimeFormat(builder, sharedPreferences); + break; } builder.queue(getQueue()); } catch (IOException e) { @@ -755,26 +842,111 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { requestBloodPressureMeasurement(); } -// set do not disturb time - private WatchXPlusDeviceSupport setQuiteHours(TransactionBuilder tbuilder, boolean enable, int hourStart, int minuteStart, int hourEnd, int minuteEnd) { - LOG.warn(" Setting DND time... Hs:"+hourStart+" Ms:"+minuteStart+" He:"+hourEnd+" Me:"+minuteEnd); - byte[] command = WatchXPlusConstants.CMD_SET_QUITE_HOURS_TIME; - byte[] bArr = new byte[4]; - bArr[0] = (byte) hourStart; // byte[08] - bArr[1] = (byte) minuteStart; // byte[09] - bArr[2] = (byte) hourEnd; // byte[10] - bArr[3] = (byte) minuteEnd; // byte[11] + /** set long sit reminder time + * @param builder + * @param enable - state (true - enabled or false - disabled) + * @param hourStart - begin hour + * @param minuteStart - begin minute + * @param hourEnd - end hour + * @param minuteEnd - end minute + * set long sit reminder (inactivity reminder) on watch + */ + private WatchXPlusDeviceSupport setLongSitHours(TransactionBuilder builder, boolean enable, int hourStart, int minuteStart, int hourEnd, int minuteEnd, int period) { + LOG.warn(" Setting Long sit reminder... Enabled:"+enable+" Period:"+period); + LOG.warn(" Setting Long sit time... Hs:"+hourEnd+" Ms:"+minuteEnd+" He:"+hourStart+" Me:"+minuteStart); + LOG.warn(" Setting Long sit DND time... Hs:"+hourStart+" Ms:"+minuteStart+" He:"+hourEnd+" Me:"+minuteEnd); + // set Long Sit reminder time + byte[] command = WatchXPlusConstants.CMD_INACTIVITY_REMINDER_SET; - tbuilder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), - buildCommand(WatchXPlusConstants.CMD_SET_QUITE_HOURS_TIME, - WatchXPlusConstants.WRITE_VALUE, - bArr)); - setQuiteHoursSwitch(tbuilder, enable); + byte[] bArr = new byte[10]; + bArr[0] = (byte) hourEnd; // byte[08] + bArr[1] = (byte) minuteEnd; // byte[09] + bArr[2] = (byte) hourStart; // byte[10] + bArr[3] = (byte) minuteStart; // byte[11] + bArr[4] = (byte) hourStart; // byte[12] + bArr[5] = (byte) minuteStart; // byte[13] + bArr[6] = (byte) hourEnd; // byte[14] + bArr[7] = (byte) minuteEnd; // byte[15] + bArr[8] = (byte) (period >> 8); // byte[16] + bArr[9] = (byte) period; // byte[17] + + builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), + buildCommand(command, WatchXPlusConstants.WRITE_VALUE, bArr)); + // set long sit reminder state (enabled, disabled) + setLongSitSwitch(builder, enable); return this; } - // set do not disturb switch + /** get Long sit settings from app, and send it to watch + * @param builder + * @param sharedPreferences + * @return + */ + private WatchXPlusDeviceSupport setLongSitHours(TransactionBuilder builder, SharedPreferences sharedPreferences) { + Calendar start = new GregorianCalendar(); + Calendar end = new GregorianCalendar(); + boolean enable = WatchXPlusDeviceCoordinator.getLongSitHours(sharedPreferences, start, end); + if (enable) { + int period = prefs.getInt(WatchXPlusConstants.PREF_LONGSIT_PERIOD, 60); + return this.setLongSitHours(builder, enable, + start.get(Calendar.HOUR_OF_DAY), start.get(Calendar.MINUTE), + end.get(Calendar.HOUR_OF_DAY), end.get(Calendar.MINUTE), + period); + } else { + // disable Long sit reminder + LOG.info(" Long sit reminder are disabled"); + return this.setLongSitSwitch(builder, enable); + } + } + + /** set long sit reminder switch + * @param tbuilder + * @param enable - true or false + * enabled or disables long sit reminder (inactivity reminder) on watch + */ + private WatchXPlusDeviceSupport setLongSitSwitch(TransactionBuilder tbuilder, boolean enable) { + LOG.warn("Setting Long sit reminder switch to" + enable); + tbuilder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), + buildCommand(WatchXPlusConstants.CMD_INACTIVITY_REMINDER_SWITCH, + WatchXPlusConstants.WRITE_VALUE, + new byte[]{(byte) (enable ? 0x01 : 0x00)})); + return this; + } + + + + /** set do not disturb time + * @param builder + * @param enable - state (true - enabled or false - disabled) + * @param hourStart - begin hour + * @param minuteStart - begin minute + * @param hourEnd - end hour + * @param minuteEnd - end minute + * set do not disturb on watch + */ + private WatchXPlusDeviceSupport setQuiteHours(TransactionBuilder builder, boolean enable, int hourStart, int minuteStart, int hourEnd, int minuteEnd) { + LOG.warn(" Setting DND time... Hs:"+hourStart+" Ms:"+minuteStart+" He:"+hourEnd+" Me:"+minuteEnd); + // set DND time + byte[] command = WatchXPlusConstants.CMD_SET_QUITE_HOURS_TIME; + + byte[] bArr = new byte[4]; + bArr[0] = (byte) hourStart; // byte[08] + bArr[1] = (byte) minuteStart; // byte[09] + bArr[2] = (byte) hourEnd; // byte[10] + bArr[3] = (byte) minuteEnd; // byte[11] + builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), + buildCommand(command, WatchXPlusConstants.WRITE_VALUE, bArr)); + // set DND state (enabled, disabled) + setQuiteHoursSwitch(builder, enable); + return this; + } + + /** set do not disturb switch + * @param tbuilder + * @param enable - true or false + * enabled or disables DND on watch + */ private WatchXPlusDeviceSupport setQuiteHoursSwitch(TransactionBuilder tbuilder, boolean enable) { LOG.warn("Setting DND switch to" + enable); tbuilder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), @@ -784,6 +956,11 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } + /** get DND settings from app, and send it to watch + * @param builder + * @param sharedPreferences + * @return + */ private WatchXPlusDeviceSupport setQuiteHours(TransactionBuilder builder, SharedPreferences sharedPreferences) { Calendar start = new GregorianCalendar(); Calendar end = new GregorianCalendar(); @@ -793,20 +970,22 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { start.get(Calendar.HOUR_OF_DAY), start.get(Calendar.MINUTE), end.get(Calendar.HOUR_OF_DAY), end.get(Calendar.MINUTE)); } else { + // disable DND LOG.info(" Quiet hours are disabled"); return this.setQuiteHoursSwitch(builder, enable); } } -// set watch power mode + /** set watch power + * @param config + * switch watch power mode + * modes (0- normal, 1- energysaving, 2- only watch) + */ private WatchXPlusDeviceSupport setPowerMode(String config) { int settingRead = prefs.getInt("wxp_power_mode", 0); byte[] bArr = new byte[1]; - if (settingRead == 0) bArr[0] = 0x00; - if (settingRead == 1) bArr[0] = 0x01; - if (settingRead == 2) bArr[0] = 0x02; - LOG.info(" setting: " + config + " mode: " + bArr[0]); - + bArr[0] = (byte) settingRead; + LOG.info(" setting: " + config); try { TransactionBuilder builder = performInitialized("setPowerMode"); builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), @@ -820,7 +999,9 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } -// check status of blood pressure calibration + /** request status of blood pressure calibration + * @param builder + */ private WatchXPlusDeviceSupport getBloodPressureCalibrationStatus(TransactionBuilder builder) { builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), buildCommand(WatchXPlusConstants.CMD_IS_BP_CALIBRATED, @@ -829,20 +1010,25 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } - CoordinatorLayout coordinatorLayout; - -// check status of blood pressure calibration + /** send blood pressure calibration to watch + * TODO add better error handling if blood pressure calibration is failed + */ private WatchXPlusDeviceSupport sendBloodPressureCalibration() { try { + int beginCalibration = prefs.getInt(WatchXPlusConstants.PREF_BP_CAL_SWITCH, 0); + if (beginCalibration == 1) { + LOG.warn(" Calibrating BP - cancel " + beginCalibration); + return this; + } int mLowP = prefs.getInt(WatchXPlusConstants.PREF_BP_CAL_LOW, 80); int mHighP = prefs.getInt(WatchXPlusConstants.PREF_BP_CAL_HIGH, 130); - LOG.warn("Calibrating BP ... LowP=" + mLowP + " HighP="+mHighP); + LOG.warn(" Calibrating BP ... LowP=" + mLowP + " HighP="+mHighP); GB.toast("Calibrating BP...", Toast.LENGTH_LONG, GB.INFO); TransactionBuilder builder = performInitialized("bpCalibrate"); byte[] command = WatchXPlusConstants.CMD_BP_CALIBRATION; - byte mStart = 0x01; + byte mStart = 0x01; // initiate calibration byte[] bArr = new byte[5]; bArr[0] = (byte) mStart; // byte[08] @@ -862,6 +1048,10 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } + /** handle watch response if blood pressure is calibrated + * @param value - watch response + * save result to global variable (uses for BP measurement) + */ private void handleBloodPressureCalibrationStatus(byte[] value) { if (Conversion.fromByteArr16(value[8]) != 0) { WatchXPlusDeviceCoordinator.isBPCalibrated = false; @@ -870,7 +1060,9 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { } } - + /** handle watch response for result of blood pressure calibration + * @param value - watch response + */ private void handleBloodPressureCalibrationResult(byte[] value) { if (Conversion.fromByteArr16(value[8]) != 0x00) { WatchXPlusDeviceCoordinator.isBPCalibrated = false; @@ -882,8 +1074,10 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { GB.toast("OK. Measured Low:"+low+" high:"+high, Toast.LENGTH_LONG, GB.INFO); } } -// end check status of blood pressure calibration + /** request blood pressure measurement + * first check if blood pressure is calibrated + */ private void requestBloodPressureMeasurement() { if (!WatchXPlusDeviceCoordinator.isBPCalibrated) { LOG.warn("BP is NOT calibrated"); @@ -1021,7 +1215,7 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { handleContentDataChunk(value); return true; } else { - LOG.info(" Unhandled characteristic changed: " + characteristicUUID); + LOG.info(" Unhandled characteristic changed: " + characteristicUUID + " value " + value); logMessageContent(characteristic.getValue()); } @@ -1303,6 +1497,10 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return result; } + /** handle watch response for steps goal (show steps setting) + * @param value - watch reply + * for test purposes only + */ private void handleSportAimStatus(byte[] value) { int stepsAim = Conversion.fromByteArr16(value[8], value[9]); LOG.debug(" Received goal stepsAim: " + stepsAim); @@ -1440,19 +1638,27 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return (byte) (checksum & 0xFF); } + /** handle watch response for firmware version + * @param value - watch response + */ private void handleFirmwareInfo(byte[] value) { versionInfo.fwVersion = String.format(Locale.US, "%d.%d.%d", value[8], value[9], value[10]); handleGBDeviceEvent(versionInfo); } + /** handle watch response for battery level + * @param value + */ private void handleBatteryState(byte[] value) { batteryInfo.state = value[8] == 1 ? BatteryState.BATTERY_NORMAL : BatteryState.BATTERY_LOW; batteryInfo.level = value[9]; handleGBDeviceEvent(batteryInfo); } -// handle lift wrist to screen on and shake to refuse call -// for test purposes only + /** handle watch response for lift wrist, and shake to refuse/ignore call + * @param value - watch response + * for test purposes only + */ private void handleShakeState(byte[] value) { boolean z = true; String light = "lightScreen"; @@ -1471,18 +1677,17 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { LOG.info(" handleShakeState: " + light + " " + refuse); } -// handle disconnect reminder state -// for test purposes only + /** handle disconnect reminder (lost device) status + * @param value - watch response + * for test purposes only + */ private void handleDisconnectReminderState(byte[] value) { boolean z = true; if (1 != value[8]) { z = false; } LOG.info(" disconnectReminder: " + Boolean.valueOf(z) + " val: " + value[8]); - return; - - } // read preferences @@ -1491,8 +1696,9 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { this.setHeadsUpScreen(transaction, sharedPreferences); // lift wirst to screen on this.setQuiteHours(transaction, sharedPreferences); // DND this.setDisconnectReminder(transaction, sharedPreferences); // disconnect reminder - this.setTimeFormat(transaction, sharedPreferences); // set time mode 12/24h + this.setLanguageAndTimeFormat(transaction, sharedPreferences); // set time mode 12/24h this.setAltitude(transaction); // set altitude calibration + this.setLongSitHours(transaction, sharedPreferences); // set Long sit reminder ActivityUser activityUser = new ActivityUser(); this.setPersonalInformation(transaction, activityUser.getHeightCm(), activityUser.getWeightKg(), activityUser.getAge(),activityUser.getGender()); @@ -1589,18 +1795,6 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } -// set time format - private WatchXPlusDeviceSupport setTimeFormat(TransactionBuilder transactionBuilder, byte timeMode) { - byte[] bArr = new byte[2]; - bArr[0] = 0x01; //byte[08] language - force to English language - bArr[1] = timeMode; //byte[09] time - transactionBuilder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), - buildCommand(WatchXPlusConstants.CMD_TIME_LANGUAGE, - WatchXPlusConstants.WRITE_VALUE, - bArr)); - return this; - } - // calibrate altitude private WatchXPlusDeviceSupport setAltitude(TransactionBuilder transactionBuilder) { int value = WatchXPlusDeviceCoordinator.getAltitude(getDevice().getAddress()); @@ -1622,9 +1816,22 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport { return this; } - private WatchXPlusDeviceSupport setTimeFormat(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { - return this.setTimeFormat(transactionBuilder, - WatchXPlusDeviceCoordinator.getTimeMode(sharedPreferences)); + // set time format + private WatchXPlusDeviceSupport setLanguageAndTimeFormat(TransactionBuilder transactionBuilder, byte timeMode, byte language) { + byte[] bArr = new byte[2]; + bArr[0] = language; //byte[08] language + bArr[1] = timeMode; //byte[09] time + transactionBuilder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE), + buildCommand(WatchXPlusConstants.CMD_TIME_LANGUAGE, + WatchXPlusConstants.WRITE_VALUE, + bArr)); + return this; + } + + private WatchXPlusDeviceSupport setLanguageAndTimeFormat(TransactionBuilder transactionBuilder, SharedPreferences sharedPreferences) { + return this.setLanguageAndTimeFormat(transactionBuilder, + WatchXPlusDeviceCoordinator.getTimeMode(sharedPreferences), + WatchXPlusDeviceCoordinator.getLanguage(sharedPreferences)); } @Override diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 920f0e19d..4d2c5ee94 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -283,23 +283,41 @@ Сверяване Watch 9 свързване Watch 9 сверяване - WatchX Plus сверяване + + Watch X Plus сверяване Единици Формат на часа - Калибриране на височина - Повтори известия за звънене + Калибриране на височината + Повтаряй известие за позвъняване Възможни стойности min=0, max=10 Известявай докато телефона звъни - Изкл. - игнорира, Вкл. - отказ + Известие за пропуснато обаждане + Watch X Plus настройки + Настройки на известията + Изкл. - заглуши, Вкл. - откажи + Бутона заглушава/отказва повикване + Повтаря действието на бутона + Разклати за заглушаване/отказване на повикването Калибриране на кръвно налягане - Кръвно налягане DIASTOLIC (долна) - Кръвно налягане SYSTOLIC (горна) - Бутона игнорира/отказва повикване - Дублира действието на бутона - Разклащането игнорира/отказва повикване - Известие за пропуснато повикване - WatchXPlus настройки - WatchXPlus калибриране + Кръвно налягане DIASTOLIC (ниска) + Кръвно налягане SYSTOLIC (висока) + Калибриране + Натисни тук за калибриране + Калибриране на сензорите + Настройки на устройството + Режим на часовника + Нормален + Икономичен + Само часовник + Повтаряй известие за пропуснато повикване всяка минута за X пъти + Повтаряй известието за пропуснато повикване + Напомняне за бездействие + Напомняй ако няма активност за повече от X минути + Времевия интервал е от настройката за DND + Включи напомняне за активност + Период на неактивност (минути) + Език + Наблюдение/анализ на съня Съхраняване на log файлове Инициализиране @@ -537,10 +555,6 @@ Начало Не безпокой Вие спахте от %1$s до %2$s - Режим на часовника - Само часовник - Икономичен - Нормален Стъпки: %1$02d Сън: %1$s 5 минути @@ -562,13 +576,10 @@ Избери всички Сподели Запази настройките - Натисни тук за старт на калибрирането - Започни калибриране Горна граница Долна граница От ляво на дясно Аларми - WatchXPlus настройки метрични инчови крачки diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index c766f389d..d88b7c361 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -69,6 +69,22 @@ never + + @string/simplified_chinese + @string/english + + + 0 + 1 + + + @string/prefs_wxp_button_bp_calibration + @string/Cancel + + + 0 + 1 + @string/wxp_mode_normal @string/wxp_mode_saving diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2dd8fb0ff..8740ed001 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -187,6 +187,7 @@ All day heart rate measurement HPlus/Makibes settings + Watch X Plus calibration Units Time format Altitude calibration @@ -194,7 +195,8 @@ Possible values min=0, max=10 Vibration during phone ring Vibration on missed call - WatchXPlus settings + Watch X Plus settings + Notification settings Off - ignore, On - reject Button ignore/reject call Duplicates watch button action @@ -202,14 +204,22 @@ Blood Pressure calibration Blood Pressure DIASTOLIC (low) Blood Pressure SYSTOLIC (high) - Begin calibration + Calibration Press here to begin calibration - WatchXPlus calibration - WatchXPlus settings + Sensors Calibration + Device settings Watch mode Normal Power saving Only watch + Repeat missed call notification every minute for X times + Repeat missed call notification + Inactivity reminder + Remind if there is no activity for more than X minutes + Inactivity time interval is from DND setting + Enable inactivity reminder + Inactivity period (minutes) + Language Makibes HR3 settings @@ -725,7 +735,6 @@ Calibrate Watch 9 pairing Watch 9 calibration - WatchX Plus calibration Contextual Arabic Enable this to support contextual Arabic Right To Left Support diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index c84357aee..f49005db3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -581,7 +581,7 @@ android:title="@string/preferences_watchxplus_settings"> + android:title="@string/pref_header_wxp_notification"> + + + + + + + - +