diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9597b4a212..6ce050552c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,7 @@
+
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 29608fc459..671dc95622 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
@@ -32,6 +32,12 @@ public final class MiBandConst {
public static final String PREF_MIBAND_ALARMS = "mi_alarms";
public static final String PREF_MIBAND_DONT_ACK_TRANSFER = "mi_dont_ack_transfer";
public static final String PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR = "mi_reserve_alarm_calendar";
+ public static final String PREF_MIBAND_BUTTON_ACTION_MENU_ENABLE = "mi2_enable_button_action_menu";
+ public static final String PREF_MIBAND_MENU_ELEMENTS = "mi2_menu_elements";
+ public static final String PREF_MIBAND_MENU_FORWARD = "mi2_menu_forward";
+ public static final String PREF_MIBAND_MENU_BACKWARD = "mi2_menu_backward";
+ public static final String PREF_MIBAND_MENU_VALIDATE = "mi2_menu_validate";
+ public static final String PREF_MIBAND_MENU_WAKELOCK = "mi2_menu_wakelock";
public static final String PREF_MIBAND_BUTTON_ACTION_ENABLE = "mi2_enable_button_action";
public static final String PREF_MIBAND_BUTTON_ACTION_VIBRATE = "mi2_button_action_vibrate";
public static final String PREF_MIBAND_BUTTON_PRESS_COUNT = "mi_button_press_count";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java
index c9ca6d1f8b..f98781e42d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.PowerManager;
import android.text.format.DateFormat;
import android.widget.Toast;
@@ -104,6 +105,7 @@
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.actions.StopNotificationAction;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2CustomMenuNotificationStrategy;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2NotificationStrategy;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2TextNotificationStrategy;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchActivityOperation;
@@ -147,6 +149,15 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
private static long currentButtonPressTime = 0;
private static long currentButtonTimerActivationTime = 0;
+ private static Boolean browsingCustomMenu = false;
+ private static int currentOptionId = 0;
+ private static Timer customMenuTimer = null;
+ private static Timer customMenuTimerTimeout = null;
+
+ // To make Timers work correctly when Android device is in deep sleep
+ // @see https://stackoverflow.com/questions/47472741/timer-not-expiring-precisely-during-sleep-state-in-android
+ private static PowerManager.WakeLock deviceWakeLock = null;
+
private static final Logger LOG = LoggerFactory.getLogger(HuamiSupport.class);
private final DeviceInfoProfile deviceInfoProfile;
private final IntentListener mListener = new IntentListener() {
@@ -340,7 +351,9 @@ public NotificationStrategy getNotificationStrategy() {
return new Mi2NotificationStrategy(this);
}
}
- if (GBApplication.getPrefs().getBoolean(MiBandConst.PREF_MI2_ENABLE_TEXT_NOTIFICATIONS, true)) {
+ if (browsingCustomMenu) {
+ return new Mi2CustomMenuNotificationStrategy(this);
+ } else if (GBApplication.getPrefs().getBoolean(MiBandConst.PREF_MI2_ENABLE_TEXT_NOTIFICATIONS, true)) {
return new Mi2TextNotificationStrategy(this);
}
return new Mi2NotificationStrategy(this);
@@ -1050,6 +1063,123 @@ public void runButtonAction() {
currentButtonPressTime = System.currentTimeMillis();
}
+ public void runCustomMenu() {
+ Prefs prefs = GBApplication.getPrefs();
+
+
+ if(this.getCustomMenuElements().length > 0) {
+ /*if (currentButtonTimerActivationTime != currentButtonPressTime) {
+ return;
+ }*/
+ LOG.info("Running custom menu");
+ browsingCustomMenu = true;
+ currentButtonActionId = 0;
+ this.displayCustomMenuOption(0);
+ }
+ }
+
+ private void displayCustomMenuOption(int optionId) {
+ Prefs prefs = GBApplication.getPrefs();
+
+ currentOptionId = optionId;
+
+ String eltLabel = this.getCustomMenuCurrentElement()[0];
+ LOG.info("Sending Menu Option " + eltLabel);
+ try {
+ TransactionBuilder builder = performInitialized("sending menu option #" + eltLabel);
+ VibrationProfile profile = getPreferredVibrateProfile("Custom Menu", prefs, (short) 0);
+ profile.setAlertLevel(HuamiService.ALERT_LEVEL_MESSAGE);
+ SimpleNotification simpleNotification = new SimpleNotification(eltLabel, null, null/*NotificationType.UNKNOWN*/);
+ sendCustomNotification(profile, simpleNotification, 0, 0, 0, 0, null, builder);
+ builder.queue(getQueue());
+ } catch (IOException ex) {
+ LOG.error("Unable to send notification to MI device", ex);
+ } finally {
+ currentButtonPressCount = 0;
+ currentButtonPressTime = System.currentTimeMillis();
+ }
+ }
+
+ private void runCustomMenuCurrentAction() {
+ Prefs prefs = GBApplication.getPrefs();
+
+ /*if (currentButtonTimerActivationTime != currentButtonPressTime) {
+ return;
+ }*/
+
+ String elementMessage = this.getCustomMenuCurrentElement()[1];
+ Intent in = new Intent();
+ in.setAction(elementMessage);
+ in.putExtra("button_id", currentButtonActionId);
+ LOG.info("Sending " + elementMessage + " with button_id " + currentButtonActionId);
+ this.getContext().getApplicationContext().sendBroadcast(in);
+
+ LOG.info("Sending Confirmation");
+ try {
+ TransactionBuilder builder = performInitialized("sending custom menu action confirmation");
+ VibrationProfile profile = getPreferredVibrateProfile("Custom Menu", prefs, (short) 0);
+ profile.setAlertLevel(HuamiService.ALERT_LEVEL_MESSAGE);
+ SimpleNotification simpleNotification = new SimpleNotification("OK", null, null/*NotificationType.UNKNOWN*/);
+ sendCustomNotification(profile, simpleNotification, 0, 0, 0, 0, null, builder);
+ builder.queue(getQueue());
+ } catch (IOException ex) {
+ LOG.error("Unable to send notification to MI device", ex);
+ } finally {
+ browsingCustomMenu = false;
+ currentButtonActionId = 0;
+
+ currentButtonPressCount = 0;
+ currentButtonPressTime = System.currentTimeMillis();
+ }
+ }
+
+ // TODO custom separator ?
+ private String[] getCustomMenuElements() {
+ Prefs prefs = GBApplication.getPrefs();
+ return prefs.getString(MiBandConst.PREF_MIBAND_MENU_ELEMENTS, "").split(";");
+ }
+
+ // TODO custom separator ?
+ private String[] getCustomMenuCurrentElement() {
+ return getCustomMenuElements()[currentOptionId].split(":");
+ }
+
+ private int getCustomMenuNextOptionId() {
+ int menuElementCount = this.getCustomMenuElements().length;
+ return (currentOptionId + 1) % menuElementCount;
+ }
+
+ private int getCustomMenuPreviousOptionId() {
+ int menuElementCount = this.getCustomMenuElements().length;
+ return (menuElementCount + currentOptionId - 1) % menuElementCount;
+ }
+
+ private Timer resetCustomMenuTimerTimeout() {
+ Prefs prefs = GBApplication.getPrefs();
+ final Boolean isWakelockEnabled = prefs.getBoolean(MiBandConst.PREF_MIBAND_MENU_WAKELOCK, false);
+
+ if(customMenuTimerTimeout != null) {
+ customMenuTimerTimeout.cancel();
+ }
+
+ // TODO Mi band 2 seems to display notifications only during 5 secs
+ // This isn't very clean, but i couldn't find anything else.
+ customMenuTimerTimeout = new Timer("Mi Band Button Action Custom Menu Timeout Timer");
+ customMenuTimerTimeout.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ LOG.info("Deactivating custom menu due to timeout");
+ browsingCustomMenu = false;
+ if (isWakelockEnabled && deviceWakeLock != null && deviceWakeLock.isHeld()) {
+ LOG.info("Releasing Wakelock");
+ deviceWakeLock.release();
+ }
+ customMenuTimerTimeout.cancel();
+ }
+ }, 5000);
+ return customMenuTimerTimeout;
+ }
+
public void handleDeviceEvent(byte[] value) {
if (value == null || value.length == 0) {
return;
@@ -1169,6 +1299,9 @@ public void handleButtonEvent() {
int buttonActionDelay = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_ACTION_DELAY, 0);
int requiredButtonPressCount = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_PRESS_COUNT, 0);
+ final Boolean isCustomMenuEnabled = prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_MENU_ENABLE, false);
+ final Boolean isWakelockEnabled = prefs.getBoolean(MiBandConst.PREF_MIBAND_MENU_WAKELOCK, false);
+
if (requiredButtonPressCount > 0) {
long timeSinceLastPress = System.currentTimeMillis() - currentButtonPressTime;
@@ -1181,10 +1314,12 @@ public void handleButtonEvent() {
}
currentButtonPressTime = System.currentTimeMillis();
- if (currentButtonPressCount == requiredButtonPressCount) {
+ if (currentButtonPressCount == requiredButtonPressCount && !browsingCustomMenu) {
currentButtonTimerActivationTime = currentButtonPressTime;
+
if (buttonActionDelay > 0) {
LOG.info("Activating timer");
+
final Timer buttonActionTimer = new Timer("Mi Band Button Action Timer");
buttonActionTimer.scheduleAtFixedRate(new TimerTask() {
@Override
@@ -1198,8 +1333,66 @@ public void run() {
LOG.info("Activating button action");
runButtonAction();
}
+
+ if (isCustomMenuEnabled) {
+ if(isWakelockEnabled) {
+ LOG.info("Acquiring wakelock");
+ PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+ deviceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport:wakelock");
+ deviceWakeLock.acquire();
+ }
+ final Timer customMenuTimer = new Timer("Mi Band Button Action Custom Menu");
+ customMenuTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ LOG.info("Activating custom menu");
+ resetCustomMenuTimerTimeout();
+ runCustomMenu();
+ customMenuTimer.cancel();
+ }
+ }, buttonActionDelay);
+ }
+
currentButtonActionId++;
currentButtonPressCount = 0;
+ } else if (browsingCustomMenu) {
+ if (customMenuTimer != null) {
+ customMenuTimer.cancel();
+ }
+
+ final int moveForwardTapCount = prefs.getInt(MiBandConst.PREF_MIBAND_MENU_FORWARD, 1);
+ final int moveBackwardTapCount = prefs.getInt(MiBandConst.PREF_MIBAND_MENU_BACKWARD, 2);
+ final int validateTapCount = prefs.getInt(MiBandConst.PREF_MIBAND_MENU_VALIDATE, 3);
+
+ customMenuTimer = new Timer("Mi Band Button Action Custom Menu Timer");
+ customMenuTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ if (currentButtonPressCount == moveForwardTapCount) {
+ resetCustomMenuTimerTimeout();
+ displayCustomMenuOption(getCustomMenuNextOptionId());
+ currentButtonActionId++;
+ currentButtonPressCount = 0;
+ } else if (currentButtonPressCount == moveBackwardTapCount) {
+ resetCustomMenuTimerTimeout();
+ displayCustomMenuOption(getCustomMenuPreviousOptionId());
+ currentButtonActionId++;
+ currentButtonPressCount = 0;
+ } else if (currentButtonPressCount == validateTapCount) {
+ currentButtonTimerActivationTime = currentButtonPressTime;
+ customMenuTimerTimeout.cancel();
+ LOG.info("Selected action on custom menu");
+ runCustomMenuCurrentAction();
+ currentButtonActionId++;
+ currentButtonPressCount = 0;
+ if (isWakelockEnabled && deviceWakeLock != null && deviceWakeLock.isHeld()) {
+ LOG.info("Releasing wakelock");
+ deviceWakeLock.release();
+ }
+ }
+ customMenuTimer.cancel();
+ }
+ }, buttonPressMaxDelay);
}
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2CustomMenuNotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2CustomMenuNotificationStrategy.java
new file mode 100644
index 0000000000..cb52aab983
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2CustomMenuNotificationStrategy.java
@@ -0,0 +1,60 @@
+/* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele
+ Gobbetti
+
+ 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 . */
+package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import androidx.annotation.NonNull;
+import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.OverflowStrategy;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiIcon;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
+import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
+
+public class Mi2CustomMenuNotificationStrategy extends Mi2NotificationStrategy {
+ public Mi2CustomMenuNotificationStrategy(HuamiSupport support) {
+ super(support);
+ }
+
+ @Override
+ protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) {
+ // announce text messages with configured alerts first
+ // TODO this seems non-necessary
+ //super.sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder);
+ // and finally send the text message, if any
+ if (simpleNotification != null && !StringUtils.isEmpty(simpleNotification.getMessage())) {
+ sendAlert(simpleNotification, builder);
+ }
+ }
+
+ protected void sendAlert(@NonNull SimpleNotification simpleNotification, TransactionBuilder builder) {
+ AlertNotificationProfile> profile = new AlertNotificationProfile<>(getSupport());
+ // override the alert category, since only SMS and incoming call support text notification
+ AlertCategory category = AlertCategory.SMS;
+ NewAlert alert = new NewAlert(category, 1, simpleNotification.getMessage());
+ profile.newAlert(builder, alert, OverflowStrategy.MAKE_MULTIPLE);
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index caf9172cea..6f9ce777fa 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -435,6 +435,18 @@
Disconnect notification
Button actions
Specify actions on Mi Band 2 button press
+ Custom Menu
+ Create a custom menu on Mi Band 2
+ Menu Elements
+ The elements of your menu (label1:message1;label2:message2;...)
+ Move forward
+ How many taps to move forward in the menu
+ Move backward
+ How many taps to move backward in the menu
+ Validate current option
+ How many taps to confirm selected menu option
+ Enable WakeLock
+ Enable if you have issues when phone sleeps. WARNING : this may reduce your battery life.
Button press count
Number of button presses to trigger message broadcast
Broadcast message to send
diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml
index 5a8b262f13..0503d6222e 100644
--- a/app/src/main/res/xml/miband_preferences.xml
+++ b/app/src/main/res/xml/miband_preferences.xml
@@ -84,6 +84,62 @@
android:key="mi_button_press_count_match_delay"
android:summary="@string/mi2_prefs_button_press_count_match_delay_summary"
android:title="@string/mi2_prefs_button_press_count_match_delay" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+