diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/AbortTransactionAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/AbortTransactionAction.java
new file mode 100644
index 000000000..f0f312796
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/AbortTransactionAction.java
@@ -0,0 +1,30 @@
+package nodomain.freeyourgadget.gadgetbridge.btle;
+
+import android.bluetooth.BluetoothGatt;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import nodomain.freeyourgadget.gadgetbridge.GBDevice;
+
+/**
+ * A special action that checks for an abort-condition, and if met, the currently
+ * executing transaction will be aborted by returning false.
+ */
+public abstract class AbortTransactionAction extends PlainAction {
+ private static final Logger LOG = LoggerFactory.getLogger(AbortTransactionAction.class);
+
+ public AbortTransactionAction() {
+ }
+
+ @Override
+ public boolean run(BluetoothGatt gatt) {
+ if (shouldAbort()) {
+ LOG.info("Aborting transaction because abort criteria met.");
+ return false;
+ }
+ return true;
+ }
+
+ protected abstract boolean shouldAbort();
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java
index 3ecd7bb13..58f57ff25 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/btle/CheckInitializedAction.java
@@ -12,7 +12,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBDevice;
* sequence (transaction). It will abort the entire initialization sequence
* by returning false, when the device is already initialized.
*/
-public class CheckInitializedAction extends PlainAction {
+public class CheckInitializedAction extends AbortTransactionAction {
private static final Logger LOG = LoggerFactory.getLogger(CheckInitializedAction.class);
private final GBDevice device;
@@ -22,11 +22,11 @@ public class CheckInitializedAction extends PlainAction {
}
@Override
- public boolean run(BluetoothGatt gatt) {
- boolean continueWithOtherInitActions = !device.isInitialized();
- if (!continueWithOtherInitActions) {
+ protected boolean shouldAbort() {
+ boolean abort = device.isInitialized();
+ if (abort) {
LOG.info("Aborting device initialization, because already initialized: " + device);
}
- return continueWithOtherInitActions;
+ return abort;
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java
index 3e7e71c59..37e0d57f2 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java
@@ -5,6 +5,8 @@ import android.content.SharedPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import nodomain.freeyourgadget.gadgetbridge.R;
+
public final class MiBandConst {
private static final Logger LOG = LoggerFactory.getLogger(MiBandConst.class);
@@ -13,9 +15,11 @@ public final class MiBandConst {
public static final String PREF_USER_GENDER = "mi_user_gender";
public static final String PREF_USER_HEIGHT_CM = "mi_user_height_cm";
public static final String PREF_USER_WEIGHT_KG = "mi_user_weight_kg";
+ public static final String PREF_MIBAND_WEARSIDE = "mi_wearside";
public static final String PREF_MIBAND_ADDRESS = "development_miaddr"; // FIXME: should be prefixed mi_
public static final String ORIGIN_SMS = "sms";
+ public static final String ORIGIN_INCOMING_CALL = "incoming_call";
public static final String ORIGIN_K9MAIL = "k9mail";
public static final String ORIGIN_PEBBLEMSG = "pebblemsg";
public static final String ORIGIN_GENERIC = "generic";
@@ -32,10 +36,16 @@ public final class MiBandConst {
}
}
+ public static String getNotificationPrefStringValue(String pref, String origin, SharedPreferences prefs, String defaultValue) {
+ String key = getNotificationPrefKey(pref, origin);
+ return prefs.getString(key, defaultValue);
+ }
+
public static final String getNotificationPrefKey(String pref, String origin) {
return new StringBuilder(pref).append('_').append(origin).toString();
}
+ public static final String VIBRATION_PROFILE = "mi_vibration_profile";
public static final String VIBRATION_COUNT = "mi_vibration_count";
public static final String VIBRATION_DURATION = "mi_vibration_duration";
public static final String VIBRATION_PAUSE = "mi_vibration_pause";
@@ -45,7 +55,8 @@ public final class MiBandConst {
public static final String FLASH_COLOUR = "mi_flash_colour";
public static final String FLASH_ORIGINAL_COLOUR = "mi_flash_original_colour";
- public static final int DEFAULT_VALUE_VIBRATION_COUNT = 6;
+ public static final String DEFAULT_VALUE_VIBRATION_PROFILE = "short";
+ public static final int DEFAULT_VALUE_VIBRATION_COUNT = 3;
public static final int DEFAULT_VALUE_VIBRATION_DURATION = 500; // ms
public static final int DEFAULT_VALUE_VIBRATION_PAUSE = 500; // ms
public static final int DEFAULT_VALUE_FLASH_COUNT = 10; // ms
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandPreferencesActivity.java
index 6def19aff..a8e03ac4d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandPreferencesActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandPreferencesActivity.java
@@ -10,16 +10,19 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractSettingsActivity;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_GENERIC;
+import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_INCOMING_CALL;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_K9MAIL;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_PEBBLEMSG;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_SMS;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ADDRESS;
+import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_WEARSIDE;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_USER_ALIAS;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_USER_GENDER;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_USER_HEIGHT_CM;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_USER_WEIGHT_KG;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_USER_YEAR_OF_BIRTH;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_COUNT;
+import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_PROFILE;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.getNotificationPrefKey;
public class MiBandPreferencesActivity extends AbstractSettingsActivity {
@@ -51,10 +54,17 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity {
PREF_USER_GENDER,
PREF_USER_HEIGHT_CM,
PREF_USER_WEIGHT_KG,
+ PREF_MIBAND_WEARSIDE,
PREF_MIBAND_ADDRESS,
+ getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_SMS),
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_SMS),
+ getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_INCOMING_CALL),
+ getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL),
+ getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_K9MAIL),
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_K9MAIL),
+ getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_PEBBLEMSG),
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_PEBBLEMSG),
+ getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_GENERIC),
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_GENERIC),
};
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java
index 575e29ba8..b0dd21a6c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java
@@ -22,11 +22,14 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBCommand;
import nodomain.freeyourgadget.gadgetbridge.GBDevice.State;
import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.btle.AbortTransactionAction;
import nodomain.freeyourgadget.gadgetbridge.btle.AbstractBTLEDeviceSupport;
+import nodomain.freeyourgadget.gadgetbridge.btle.BtLEAction;
import nodomain.freeyourgadget.gadgetbridge.btle.SetDeviceBusyAction;
import nodomain.freeyourgadget.gadgetbridge.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler;
+import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_PROFILE;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_COUNT;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.DEFAULT_VALUE_FLASH_DURATION;
@@ -44,7 +47,9 @@ import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_SMS
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_COUNT;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_DURATION;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_PAUSE;
+import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_PROFILE;
import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.getNotificationPrefIntValue;
+import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.getNotificationPrefStringValue;
public class MiBandSupport extends AbstractBTLEDeviceSupport {
@@ -63,6 +68,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
private GregorianCalendar activityDataTimestampProgress = null;
//same as above, but remains untouched for the ack message
private GregorianCalendar activityDataTimestampToAck = null;
+ private volatile boolean telephoneRinging;
public MiBandSupport() {
@@ -135,6 +141,42 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
builder.write(characteristic, getDefaultNotification()).queue(getQueue());
}
+ /**
+ * Sends a custom notification to the Mi Band.
+ * @param vibrationProfile specifies how and how often the Band shall vibrate.
+ * @param flashTimes
+ * @param flashColour
+ * @param originalColour
+ * @param flashDuration
+ * @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example.
+ * @param builder
+ */
+ private void sendCustomNotification(VibrationProfile vibrationProfile, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) {
+ BluetoothGattCharacteristic controlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
+ for (byte i = 0; i < vibrationProfile.getRepeat(); i++) {
+ int[] onOffSequence = vibrationProfile.getOnOffSequence();
+ for (int j = 0; j < onOffSequence.length; j++) {
+ int on = onOffSequence[j];
+ on = Math.min(500, on); // longer than 500ms is not possible
+ builder.write(controlPoint, startVibrate);
+ builder.wait(on);
+ builder.write(controlPoint, stopVibrate);
+
+ if (++j < onOffSequence.length) {
+ int off = Math.max(onOffSequence[j], 25); // wait at least 25ms
+ builder.wait(off);
+ }
+
+ if (extraAction != null) {
+ builder.add(extraAction);
+ }
+ }
+ }
+
+ LOG.info("Sending notification to MiBand: " + controlPoint);
+ builder.queue(getQueue());
+ }
+
private void sendCustomNotification(int vibrateDuration, int vibrateTimes, int pause, int flashTimes, int flashColour, int originalColour, long flashDuration, TransactionBuilder builder) {
BluetoothGattCharacteristic controlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
int vDuration = Math.min(500, vibrateDuration); // longer than 500ms is not possible
@@ -221,20 +263,22 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
// }
// }
- private void performPreferredNotification(String task, String notificationOrigin) {
+ private void performPreferredNotification(String task, String notificationOrigin, BtLEAction extraAction) {
try {
TransactionBuilder builder = performInitialized(task);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
int vibrateDuration = getPreferredVibrateDuration(notificationOrigin, prefs);
- int vibrateTimes = getPreferredVibrateCount(notificationOrigin, prefs);
int vibratePause = getPreferredVibratePause(notificationOrigin, prefs);
+ int vibrateTimes = getPreferredVibrateCount(notificationOrigin, prefs);
+ VibrationProfile profile = getPreferredVibrateProfile(notificationOrigin, prefs, vibrateTimes);
int flashTimes = getPreferredFlashCount(notificationOrigin, prefs);
int flashColour = getPreferredFlashColour(notificationOrigin, prefs);
int originalColour = getPreferredOriginalColour(notificationOrigin, prefs);
int flashDuration = getPreferredFlashDuration(notificationOrigin, prefs);
- sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder);
+ sendCustomNotification(profile, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder);
+// sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder);
} catch (IOException ex) {
LOG.error("Unable to send notification to MI device", ex);
}
@@ -268,20 +312,24 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
return getNotificationPrefIntValue(VIBRATION_DURATION, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_DURATION);
}
+ private VibrationProfile getPreferredVibrateProfile(String notificationOrigin, SharedPreferences prefs, int repeat) {
+ String profileId = getNotificationPrefStringValue(VIBRATION_PROFILE, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_PROFILE);
+ return VibrationProfile.getProfile(profileId, (byte) repeat);
+ }
+
@Override
public void onSMS(String from, String body) {
-// performCustomNotification("sms received", 500, 3, 2000, 0, 0, 0, 0);
- performPreferredNotification("sms received", ORIGIN_SMS);
+ performPreferredNotification("sms received", ORIGIN_SMS, null);
}
@Override
public void onEmail(String from, String subject, String body) {
- performPreferredNotification("email received", ORIGIN_K9MAIL);
+ performPreferredNotification("email received", ORIGIN_K9MAIL, null);
}
@Override
public void onGenericNotification(String title, String details) {
- performPreferredNotification("generic notification received", ORIGIN_GENERIC);
+ performPreferredNotification("generic notification received", ORIGIN_GENERIC, null);
}
@Override
@@ -328,10 +376,24 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
@Override
public void onSetCallState(String number, String name, GBCommand command) {
if (GBCommand.CALL_INCOMING.equals(command)) {
- performDefaultNotification("incoming call");
+ telephoneRinging = true;
+ AbortTransactionAction abortAction = new AbortTransactionAction() {
+ @Override
+ protected boolean shouldAbort() {
+ return !isTelephoneRinging();
+ }
+ };
+ performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, abortAction);
+ } else if (GBCommand.CALL_START.equals(command) || GBCommand.CALL_END.equals(command)) {
+ telephoneRinging = false;
}
}
+ private boolean isTelephoneRinging() {
+ // don't synchronize, this is not really important
+ return telephoneRinging;
+ }
+
@Override
public void onSetMusicInfo(String artist, String album, String track) {
// not supported
@@ -556,8 +618,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
unsetBusy();
}
}
- for (byte b : value) {
- LOG.info("handleControlPoint GOT DATA:" + String.format("0x%8x", b));
+ if (value != null) {
+ for (byte b : value) {
+ LOG.info("handleControlPoint GOT DATA:" + String.format("0x%8x", b));
+ }
+ } else {
+ LOG.warn("handleControlPoint GOT null");
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/VibrationProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/VibrationProfile.java
new file mode 100644
index 000000000..7144f80ca
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/VibrationProfile.java
@@ -0,0 +1,69 @@
+package nodomain.freeyourgadget.gadgetbridge.miband;
+
+import android.content.Context;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+
+public class VibrationProfile {
+ public static final Context CONTEXT = GBApplication.getContext();
+ public static final String ID_STACCATO = CONTEXT.getString(R.string.p_staccato);
+ public static final String ID_SHORT = CONTEXT.getString(R.string.p_short);
+ public static final String ID_MEDIUM = CONTEXT.getString(R.string.p_medium);
+ public static final String ID_LONG = CONTEXT.getString(R.string.p_long);
+ public static final String ID_WATERDROP = CONTEXT.getString(R.string.p_waterdrop);
+ public static final String ID_RING = CONTEXT.getString(R.string.p_ring);
+ public static final String ID_ALARM_CLOCK = CONTEXT.getString(R.string.p_alarm_clock);
+
+ public static VibrationProfile getProfile(String id, byte repeat) {
+ if (ID_STACCATO.equals(id)) {
+ return new VibrationProfile(id, new int[]{100, 0}, repeat);
+ }
+ if (ID_SHORT.equals(id)) {
+ return new VibrationProfile(id, new int[] {200, 200}, repeat);
+ }
+ if (ID_LONG.equals(id)) {
+ return new VibrationProfile(id, new int[] {500, 1000}, repeat);
+ }
+ if (ID_WATERDROP.equals(id)) {
+ return new VibrationProfile(id, new int[] {100, 1500}, repeat);
+ }
+ if (ID_RING.equals(id)) {
+ return new VibrationProfile(id, new int[]{300, 200, 600, 2000}, repeat);
+ }
+ if (ID_ALARM_CLOCK.equals(id)) {
+ return new VibrationProfile(id, new int[] {30, 35, 30, 35, 30, 35, 30, 800}, repeat);
+ }
+ // medium
+ return new VibrationProfile(id, new int[] {300, 600}, repeat);
+ }
+
+ private final String id;
+
+ private int[] onOffSequence;
+ private byte repeat; // 1-10
+
+ /**
+ * Creates a new profile instance.
+ * @param id the ID, used as preference key.
+ * @param onOffSequence a sequence of alternating on and off durations, in milliseconds
+ * @param repeat how ofoften the sequence shall be repeated
+ */
+ public VibrationProfile(String id, int[] onOffSequence, byte repeat) {
+ this.id = id;
+ this.repeat = repeat;
+ this.onOffSequence = onOffSequence;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int[] getOnOffSequence() {
+ return onOffSequence;
+ }
+
+ public byte getRepeat() {
+ return repeat;
+ }
+}
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 7d88bcb66..88b4f5f34 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -21,4 +21,33 @@
- female
- other
+
+ - @string/left
+ - @string/right
+
+
+ - left
+ - right
+
+
+
+ - @string/vibration_profile_staccato
+ - @string/vibration_profile_short
+ - @string/vibration_profile_medium
+ - @string/vibration_profile_long
+ - @string/vibration_profile_waterdrop
+ - @string/vibration_profile_ring
+ - @string/vibration_profile_alarm_clock
+
+
+
+ - @string/p_staccato
+ - @string/p_short
+ - @string/p_medium
+ - @string/p_long
+ - @string/p_waterdrop
+ - @string/p_ring
+ - @string/p_alarm_clock
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 088b793c0..23b6a6d9f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -28,7 +28,9 @@
Sync time to device when connecting and when time or timezone changes on Android
Notifications
+ Repetitions
SMS
+ Incoming Calls
K9-Mail
Pebble Messages
Support for applications which send Notifications to the Pebble via Intent. Can be used for Conversations.
@@ -91,6 +93,8 @@
male
female
other
+ left
+ right
No valid user data given, using dummy user data for now.
When your Mi Band vibrates and blinks, tap it a few times in a row.
Install
@@ -112,5 +116,22 @@
Fetch Activity Data
Disconnect
From %1$s to %2$s
+ Wearing left or right?
+ Vibration Profile
+
+ Staccato
+ Short
+ Medium
+ Long
+ Waterdrop
+ Ring
+ Alarm Clock
+ Vibration
+ SMS Notification
+ Vibration Settings
+ Generic Notification
+ Pebble Notification
+ K9 Mail Notification
+ Incoming Call Notification
diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml
new file mode 100644
index 000000000..a89f911ca
--- /dev/null
+++ b/app/src/main/res/values/values.xml
@@ -0,0 +1,12 @@
+
+
+
+ - staccato
+ - short
+ - medium
+ - long
+ - waterdrop
+ - ring
+ - alarm_clock
+
+
diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml
index 4a77cb90a..1ac586b22 100644
--- a/app/src/main/res/xml/miband_preferences.xml
+++ b/app/src/main/res/xml/miband_preferences.xml
@@ -34,35 +34,118 @@
android:maxLength="3"
android:title="@string/miband_prefs_weight_kg" />
+
-
-
-
-
+ android:title="@string/pref_header_vibration_settings">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+