1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-05 09:47:01 +01:00

Mi2: initial support for text notifications and icons

See #560
This commit is contained in:
cpfeiffer 2017-03-14 00:44:59 +01:00
parent 8fc6dfeca7
commit 17ecee0cab
12 changed files with 229 additions and 71 deletions

View File

@ -53,6 +53,7 @@ public class MiBand2Service {
public static final int ALERT_LEVEL_MESSAGE = 1; public static final int ALERT_LEVEL_MESSAGE = 1;
public static final int ALERT_LEVEL_PHONE_CALL = 2; public static final int ALERT_LEVEL_PHONE_CALL = 2;
public static final int ALERT_LEVEL_VIBRATE_ONLY = 3; public static final int ALERT_LEVEL_VIBRATE_ONLY = 3;
public static final int ALERT_LEVEL_CUSTOM = 0xfa; // followed by another uin8 to select the actual icon
// set metric distance // set metric distance
// set 12 hour time mode // set 12 hour time mode
@ -109,6 +110,41 @@ public class MiBand2Service {
public static final byte[] COMMAND_SET_FITNESS_GOAL_START = new byte[] { 0x10, 0x0, 0x0 }; public static final byte[] COMMAND_SET_FITNESS_GOAL_START = new byte[] { 0x10, 0x0, 0x0 };
public static final byte[] COMMAND_SET_FITNESS_GOAL_END = new byte[] { 0, 0 }; public static final byte[] COMMAND_SET_FITNESS_GOAL_END = new byte[] { 0, 0 };
public static final byte ICON_CHAT = 0x00;
public static final byte ICON_PENGUIN = 0x01;
public static final byte ICON_CHAT_MI = 0x02;
public static final byte ICON_FB = 0x03;
public static final byte ICON_TWITTER = 0x04;
public static final byte ICON_MIBAND = 0x05;
public static final byte ICON_SNAPCHAT = 0x06;
public static final byte ICON_WHATSAPP = 0x07;
public static final byte ICON_MANTA = 0x08;
public static final byte ICON_XX0 = 0x09;
public static final byte ICON_ALARM = 0x10;
public static final byte ICON_SHATTERED_GLASS = 0x11;
public static final byte ICON_INSTAGRAM = 0x12;
public static final byte ICON_CHAT_GHOST = 0x13;
public static final byte ICON_COW = 0x14;
public static final byte ICON_XX2 = 0x15;
public static final byte ICON_XX3 = 0x16;
public static final byte ICON_XX4 = 0x17;
public static final byte ICON_XX5 = 0x18;
public static final byte ICON_XX6 = 0x19;
public static final byte ICON_EGALE = 0x1a;
public static final byte ICON_CALENDAR = 0x1b;
public static final byte ICON_XX7 = 0x1c;
public static final byte ICON_PHONE_CALL = 0x1d;
public static final byte ICON_CHAT_LINE = 0x1e;
public static final byte ICON_TELEGRAM = 0x1f;
public static final byte ICON_CHAT_TALK = 0x20;
public static final byte ICON_SKYPE = 0x21;
public static final byte ICON_VK = 0x22;
public static final byte ICON_CIRCLES = 0x23;
public static final byte ICON_HANGOUTS = 0x24;
public static final byte ICON_MI = 0x25;
public static final byte ICON_HIGH_PRIORITY = 0x7;
public static byte ENDPOINT_DISPLAY = 0x06; public static byte ENDPOINT_DISPLAY = 0x06;
@ -119,7 +155,7 @@ public class MiBand2Service {
public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01}; public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01};
public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x00}; public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x00};
public static final byte[] DISPLAY_XXX = new byte[] {ENDPOINT_DISPLAY, 0x03, 0x0, 0x0 }; 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, 0x0 }; public static final byte[] DISPLAY_YYY = new byte[] {ENDPOINT_DISPLAY, 0x10, 0x0, 0x1, 0x1 };
public static final byte RESPONSE = 0x10; public static final byte RESPONSE = 0x10;
@ -148,7 +184,6 @@ public class MiBand2Service {
public static final byte[] COMMAND_DISABLE_HR_SLEEP_MEASUREMENT = new byte[]{0x15, 0x00, 0x00}; public static final byte[] COMMAND_DISABLE_HR_SLEEP_MEASUREMENT = new byte[]{0x15, 0x00, 0x00};
public static final byte[] COMMAND_TEXT_NOTIFICATION = new byte[] {0x05, 0x01}; public static final byte[] COMMAND_TEXT_NOTIFICATION = new byte[] {0x05, 0x01};
public static final byte COMMAND_ALERT_CATEGORY_CHAT = (byte) 0xfa;
static { static {
MIBAND_DEBUG = new HashMap<>(); MIBAND_DEBUG = new HashMap<>();

View File

@ -36,7 +36,8 @@ public enum AlertCategory {
InstantMessage(9), InstantMessage(9),
// 10-250 reserved for future use // 10-250 reserved for future use
// 251-255 defined by service specification // 251-255 defined by service specification
Any(255); Any(255),
Custom(-1);
private final int id; private final int id;

View File

@ -56,7 +56,7 @@ public class AlertNotificationProfile<T extends AbstractBTLEDeviceSupport> exten
public void newAlert(TransactionBuilder builder, NewAlert alert, OverflowStrategy strategy) { public void newAlert(TransactionBuilder builder, NewAlert alert, OverflowStrategy strategy) {
BluetoothGattCharacteristic characteristic = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_NEW_ALERT); BluetoothGattCharacteristic characteristic = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_NEW_ALERT);
if (characteristic != null) { if (characteristic != null) {
String message = alert.getMessage(); String message = StringUtils.ensureNotNull(alert.getMessage());
if (message.length() > MAX_MSG_LENGTH && strategy == OverflowStrategy.TRUNCATE) { if (message.length() > MAX_MSG_LENGTH && strategy == OverflowStrategy.TRUNCATE) {
message = StringUtils.truncate(message, MAX_MSG_LENGTH); message = StringUtils.truncate(message, MAX_MSG_LENGTH);
} }
@ -66,6 +66,7 @@ public class AlertNotificationProfile<T extends AbstractBTLEDeviceSupport> exten
numChunks++; numChunks++;
} }
try {
boolean hasAlerted = false; boolean hasAlerted = false;
for (int i = 0; i < numChunks; i++) { for (int i = 0; i < numChunks; i++) {
int offset = i * MAX_MSG_LENGTH; int offset = i * MAX_MSG_LENGTH;
@ -75,19 +76,22 @@ public class AlertNotificationProfile<T extends AbstractBTLEDeviceSupport> exten
// no need to do it again when there is no text content // no need to do it again when there is no text content
break; break;
} }
writeAlertMessage(builder, characteristic, alert, message, i); builder.write(characteristic, getAlertMessage(alert, message, 1));
hasAlerted = true; hasAlerted = true;
} }
if (!hasAlerted) { if (!hasAlerted) {
writeAlertMessage(builder, characteristic, alert, "", 1); builder.write(characteristic, getAlertMessage(alert, "", 1));
}
} catch (IOException ex) {
// ain't gonna happen
LOG.error("Error writing alert message to ByteArrayOutputStream");
} }
} else { } else {
LOG.warn("NEW_ALERT characteristic not available"); LOG.warn("NEW_ALERT characteristic not available");
} }
} }
protected void writeAlertMessage(TransactionBuilder builder, BluetoothGattCharacteristic characteristic, NewAlert alert, String message, int chunk) { protected byte[] getAlertMessage(NewAlert alert, String message, int chunk) throws IOException {
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream(100); ByteArrayOutputStream stream = new ByteArrayOutputStream(100);
stream.write(BLETypeConversions.fromUint8(alert.getCategory().getId())); stream.write(BLETypeConversions.fromUint8(alert.getCategory().getId()));
stream.write(BLETypeConversions.fromUint8(alert.getNumAlerts())); stream.write(BLETypeConversions.fromUint8(alert.getNumAlerts()));
@ -98,10 +102,6 @@ public class AlertNotificationProfile<T extends AbstractBTLEDeviceSupport> exten
// some write a null byte instead of leaving out this optional value // some write a null byte instead of leaving out this optional value
// stream.write(new byte[] {0}); // stream.write(new byte[] {0});
} }
builder.write(characteristic, stream.toByteArray()); return stream.toByteArray();
} catch (IOException ex) {
// ain't gonna happen
LOG.error("Error writing alert message to ByteArrayOutputStream");
}
} }
} }

View File

@ -22,6 +22,7 @@ import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast; import android.widget.Toast;
@ -72,7 +73,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
@ -248,7 +248,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
* @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example. * @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example.
* @param builder * @param builder
*/ */
private MiBandSupport sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { private MiBandSupport sendCustomNotification(VibrationProfile vibrationProfile, @Nullable SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) {
getNotificationStrategy().sendCustomNotification(vibrationProfile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); getNotificationStrategy().sendCustomNotification(vibrationProfile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder);
LOG.info("Sending notification to MiBand"); LOG.info("Sending notification to MiBand");
return this; return this;
@ -487,7 +487,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
} }
} }
private void performPreferredNotification(String task, SimpleNotification simpleNotification, String notificationOrigin, BtLEAction extraAction) { private void performPreferredNotification(String task, @Nullable SimpleNotification simpleNotification, String notificationOrigin, BtLEAction extraAction) {
try { try {
TransactionBuilder builder = performInitialized(task); TransactionBuilder builder = performInitialized(task);
Prefs prefs = GBApplication.getPrefs(); Prefs prefs = GBApplication.getPrefs();
@ -572,11 +572,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
return; return;
} }
String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()).trim();
SimpleNotification simpleNotification = new SimpleNotification(message, BLETypeConversions.toAlertCategory(notificationSpec.type));
String origin = notificationSpec.type.getGenericType(); String origin = notificationSpec.type.getGenericType();
performPreferredNotification(origin + " received", simpleNotification, origin, null); performPreferredNotification(origin + " received", null, origin, null);
} }
private void onAlarmClock(NotificationSpec notificationSpec) { private void onAlarmClock(NotificationSpec notificationSpec) {

View File

@ -39,4 +39,9 @@ public class NoNotificationStrategy implements NotificationStrategy {
public void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { public void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) {
LOG.info("dummy notification stragegy: custom notification: " + simpleNotification); LOG.info("dummy notification stragegy: custom notification: " + simpleNotification);
} }
@Override
public void stopCurrentNotification(TransactionBuilder builder) {
LOG.info("dummy notification stragegy: stop notification");
}
} }

View File

@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */ along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
import android.support.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
@ -27,7 +29,7 @@ public interface NotificationStrategy {
/** /**
* Adds a custom notification to the given transaction builder * Adds a custom notification to the given transaction builder
* @param vibrationProfile specifies how and how often the Band shall vibrate. * @param vibrationProfile specifies how and how often the Band shall vibrate.
* @param simpleNotification * @param simpleNotification an optional notification containing a type and text message
* @param flashTimes * @param flashTimes
* @param flashColour * @param flashColour
* @param originalColour * @param originalColour
@ -35,5 +37,11 @@ public interface NotificationStrategy {
* @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example. * @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example.
* @param builder * @param builder
*/ */
void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder); void sendCustomNotification(VibrationProfile vibrationProfile, @Nullable SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder);
/**
* Stops any current notification.
* @param builder
*/
void stopCurrentNotification(TransactionBuilder builder);
} }

View File

@ -103,6 +103,12 @@ public class V1NotificationStrategy implements NotificationStrategy {
} }
} }
@Override
public void stopCurrentNotification(TransactionBuilder builder) {
BluetoothGattCharacteristic controlPoint = support.getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
builder.write(controlPoint, stopVibrate);
}
// private void sendCustomNotification(int vibrateDuration, int vibrateTimes, int pause, int flashTimes, int flashColour, int originalColour, long flashDuration, TransactionBuilder builder) { // 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); // BluetoothGattCharacteristic controlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
// int vDuration = Math.min(500, vibrateDuration); // longer than 500ms is not possible // int vDuration = Math.min(500, vibrateDuration); // longer than 500ms is not possible

View File

@ -17,25 +17,23 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; package nodomain.freeyourgadget.gadgetbridge.service.devices.miband;
import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattCharacteristic;
import android.support.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
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.common.SimpleNotification;
public class V2NotificationStrategy implements NotificationStrategy { public class V2NotificationStrategy<T extends AbstractBTLEDeviceSupport> implements NotificationStrategy {
private final AbstractBTLEDeviceSupport support; private final T support;
public V2NotificationStrategy(AbstractBTLEDeviceSupport support) { public V2NotificationStrategy(T support) {
this.support = support; this.support = support;
} }
protected AbstractBTLEDeviceSupport getSupport() { protected T getSupport() {
return support; return support;
} }
@ -45,7 +43,7 @@ public class V2NotificationStrategy implements NotificationStrategy {
sendCustomNotification(profile, simpleNotification, extraAction, builder); sendCustomNotification(profile, simpleNotification, extraAction, builder);
} }
protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) { protected void sendCustomNotification(VibrationProfile vibrationProfile, @Nullable SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) {
//use the new alert characteristic //use the new alert characteristic
BluetoothGattCharacteristic alert = support.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL); BluetoothGattCharacteristic alert = support.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL);
for (short i = 0; i < vibrationProfile.getRepeat(); i++) { for (short i = 0; i < vibrationProfile.getRepeat(); i++) {
@ -69,13 +67,6 @@ public class V2NotificationStrategy implements NotificationStrategy {
} }
} }
} }
// sendAlert(simpleNotification, builder);
}
protected void sendAlert(SimpleNotification simpleNotification, TransactionBuilder builder) {
AlertNotificationProfile<?> profile = new AlertNotificationProfile<>(getSupport());
NewAlert alert = new NewAlert(simpleNotification.getAlertCategory(), 1, simpleNotification.getMessage());
profile.newAlert(builder, alert, OverflowStrategy.MAKE_MULTIPLE);
} }
@Override @Override
@ -83,4 +74,10 @@ public class V2NotificationStrategy implements NotificationStrategy {
// all other parameters are unfortunately not supported anymore ;-( // all other parameters are unfortunately not supported anymore ;-(
sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder); sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder);
} }
@Override
public void stopCurrentNotification(TransactionBuilder builder) {
BluetoothGattCharacteristic alert = support.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL);
builder.write(alert, new byte[]{GattCharacteristic.NO_ALERT});
}
} }

View File

@ -17,34 +17,34 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2;
import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattCharacteristic;
import android.support.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile;
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.V2NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.V2NotificationStrategy;
public class Mi2NotificationStrategy extends V2NotificationStrategy { public class Mi2NotificationStrategy extends V2NotificationStrategy<MiBand2Support> {
public Mi2NotificationStrategy(AbstractBTLEDeviceSupport support) { private final BluetoothGattCharacteristic alertLevelCharacteristic;
public Mi2NotificationStrategy(MiBand2Support support) {
super(support); super(support);
alertLevelCharacteristic = support.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL);
} }
@Override @Override
protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) { protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) {
//use the new alert characteristic
BluetoothGattCharacteristic alert = getSupport().getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL);
for (short i = 0; i < vibrationProfile.getRepeat(); i++) { for (short i = 0; i < vibrationProfile.getRepeat(); i++) {
int[] onOffSequence = vibrationProfile.getOnOffSequence(); int[] onOffSequence = vibrationProfile.getOnOffSequence();
for (int j = 0; j < onOffSequence.length; j++) { for (int j = 0; j < onOffSequence.length; j++) {
int on = onOffSequence[j]; int on = onOffSequence[j];
on = Math.min(500, on); // longer than 500ms is not possible on = Math.min(500, on); // longer than 500ms is not possible
builder.write(alert, new byte[]{(byte) vibrationProfile.getAlertLevel()}); startNotify(builder, vibrationProfile.getAlertLevel(), simpleNotification);
builder.wait(on); builder.wait(on);
builder.write(alert, new byte[]{GattCharacteristic.NO_ALERT}); stopNotify(builder);
if (++j < onOffSequence.length) { if (++j < onOffSequence.length) {
int off = Math.max(onOffSequence[j], 25); // wait at least 25ms int off = Math.max(onOffSequence[j], 25); // wait at least 25ms
@ -56,12 +56,19 @@ public class Mi2NotificationStrategy extends V2NotificationStrategy {
} }
} }
} }
}
sendAlert(simpleNotification, builder); protected void startNotify(TransactionBuilder builder, int alertLevel, @Nullable SimpleNotification simpleNotification) {
builder.write(alertLevelCharacteristic, new byte[] {(byte) alertLevel});
}
protected void stopNotify(TransactionBuilder builder) {
builder.write(alertLevelCharacteristic, new byte[]{GattCharacteristic.NO_ALERT});
} }
@Override @Override
public void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { public void sendCustomNotification(VibrationProfile vibrationProfile, @Nullable SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) {
// all other parameters are unfortunately not supported anymore ;-( // all other parameters are unfortunately not supported anymore ;-(
sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder); sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder);
} }

View File

@ -0,0 +1,74 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2;
import android.bluetooth.BluetoothGattCharacteristic;
import android.support.annotation.NonNull;
import android.util.Log;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
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.util.StringUtils;
public class Mi2TextNotificationStrategy extends Mi2NotificationStrategy {
private final BluetoothGattCharacteristic newAlertCharacteristic;
public Mi2TextNotificationStrategy(MiBand2Support support) {
super(support);
newAlertCharacteristic = support.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_NEW_ALERT);
}
@Override
protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) {
if (simpleNotification != null && simpleNotification.getAlertCategory() == AlertCategory.IncomingCall) {
// incoming calls are notified solely via NewAlert including caller ID
sendAlert(simpleNotification, builder);
return;
}
// announce text messages with configured alerts first
super.sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder);
// and finally send the text message, if any
if (simpleNotification != null && !StringUtils.isEmpty(simpleNotification.getMessage())) {
sendAlert(simpleNotification, builder);
}
}
@Override
protected void startNotify(TransactionBuilder builder, int alertLevel, SimpleNotification simpleNotification) {
builder.write(newAlertCharacteristic, getNotifyMessage(simpleNotification));
}
protected byte[] getNotifyMessage(SimpleNotification simpleNotification) {
int numAlerts = 1;
if (simpleNotification != null) {
switch (simpleNotification.getAlertCategory()) {
case Email:
return new byte[] { BLETypeConversions.fromUint8(MiBand2Service.ALERT_LEVEL_MESSAGE), BLETypeConversions.fromUint8(numAlerts)};
case InstantMessage:
return new byte[] { BLETypeConversions.fromUint8(MiBand2Service.ALERT_LEVEL_CUSTOM), BLETypeConversions.fromUint8(numAlerts), MiBand2Service.ICON_CHAT};
case News:
return new byte[] { BLETypeConversions.fromUint8(MiBand2Service.ALERT_LEVEL_CUSTOM), BLETypeConversions.fromUint8(numAlerts), MiBand2Service.ICON_PENGUIN};
}
}
return new byte[] { BLETypeConversions.fromUint8(AlertCategory.SMS.getId()), BLETypeConversions.fromUint8(numAlerts)};
}
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;
if (simpleNotification.getAlertCategory() == AlertCategory.IncomingCall) {
category = simpleNotification.getAlertCategory();
}
NewAlert alert = new NewAlert(category, 1, simpleNotification.getMessage());
profile.newAlert(builder, alert, OverflowStrategy.MAKE_MULTIPLE);
}
}

View File

@ -84,6 +84,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; 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.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile;
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
@ -301,7 +304,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
} }
private NotificationStrategy getNotificationStrategy() { private NotificationStrategy getNotificationStrategy() {
return new Mi2NotificationStrategy(this); // return new Mi2NotificationStrategy(this);
return new Mi2TextNotificationStrategy(this);
} }
private static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1}; private static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1};
@ -440,6 +444,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
int flashDuration = getPreferredFlashDuration(notificationOrigin, prefs); int flashDuration = getPreferredFlashDuration(notificationOrigin, prefs);
sendCustomNotification(profile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); sendCustomNotification(profile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder);
// sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder); // sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder);
builder.queue(getQueue()); builder.queue(getQueue());
} catch (IOException ex) { } catch (IOException ex) {
@ -563,6 +568,17 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, simpleNotification, MiBand2Service.ALERT_LEVEL_PHONE_CALL, abortAction); performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, simpleNotification, MiBand2Service.ALERT_LEVEL_PHONE_CALL, abortAction);
} else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) { } else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) {
telephoneRinging = false; telephoneRinging = false;
stopCurrentNotification();
}
}
private void stopCurrentNotification() {
try {
TransactionBuilder builder = performInitialized("stop notification");
getNotificationStrategy().stopCurrentNotification(builder);
builder.queue(getQueue());
} catch (IOException e) {
LOG.error("Error stopping notification");
} }
} }
@ -642,7 +658,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
return !isLocatingDevice; return !isLocatingDevice;
} }
}; };
SimpleNotification simpleNotification = new SimpleNotification(getContext().getString(R.string.find_device_you_found_it), AlertCategory.HighPriorityAlert.HighPriorityAlert); SimpleNotification simpleNotification = new SimpleNotification(getContext().getString(R.string.find_device_you_found_it), AlertCategory.HighPriorityAlert);
performDefaultNotification("locating device", simpleNotification, (short) 255, abortAction); performDefaultNotification("locating device", simpleNotification, (short) 255, abortAction);
} }
} }
@ -1044,11 +1060,12 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
@Override @Override
public void onTestNewFunction() { public void onTestNewFunction() {
try { try {
performInitialized("read characteristic 10") TransactionBuilder builder = performInitialized("incoming call from peter");
.read(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_10_BUTTON)) NewAlert alert = new NewAlert(AlertCategory.Custom, 1, new String(new byte[] {0x19}));
.queue(getQueue()); AlertNotificationProfile<MiBand2Support> profile = new AlertNotificationProfile<>(this);
profile.newAlert(builder, alert, OverflowStrategy.MAKE_MULTIPLE);
builder.queue(getQueue());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace();
} }
} }

View File

@ -81,4 +81,15 @@ public class StringUtils {
} }
return ""; return "";
} }
public static boolean isEmpty(String string) {
return string != null && string.length() == 0;
}
public static String ensureNotNull(String message) {
if (message != null) {
return message;
}
return "";
}
} }