diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index fb3e9a10..be244dff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -128,6 +128,8 @@ public class MiBand2Service { public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{0x06, 0x05, 0x00, 0x01}; public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{0x06, 0x05, 0x00, 0x00}; + public static final byte[] COMMAND_TEXT_NOTIFICATION = new byte[] {0x05, 0x01}; + public static final byte[] COMMAND_TEXT_NOTIFICATION_CONTINUATION = new byte[] {(byte) 0xfa, 0x01, 0x00}; static { MIBAND_DEBUG = new HashMap<>(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java index 2f012825..4af9af6c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java @@ -5,6 +5,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; public enum NotificationType { + // TODO: this this pebbleism needs to be moved somewhere else UNKNOWN(PebbleIconID.NOTIFICATION_GENERIC, PebbleColor.Red), CONVERSATIONS(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.Inchworm), @@ -19,6 +20,7 @@ public enum NotificationType { TELEGRAM(PebbleIconID.NOTIFICATION_TELEGRAM, PebbleColor.PictonBlue), WHATSAPP(PebbleIconID.NOTIFICATION_WHATSAPP, PebbleColor.MayGreen), GENERIC_ALARM_CLOCK(PebbleIconID.ALARM_CLOCK, PebbleColor.Red); + // Note: if you add any more constants, update all clients as well public int icon; public byte color; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index 90ccc4db..56e59bc0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -1,10 +1,13 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle; +import java.nio.charset.StandardCharsets; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; /** * Provides methods to convert standard BLE units to byte sequences and vice versa. @@ -233,4 +236,33 @@ public class BLETypeConversions { } return 0; } + + public static byte[] toUtf8s(String message) { + return message.getBytes(StandardCharsets.UTF_8); + } + + public static AlertCategory toAlertCategory(NotificationType type) { + switch (type) { + case GENERIC_ALARM_CLOCK: + return AlertCategory.HighPriorityAlert; + case GENERIC_SMS: + return AlertCategory.SMS; + case GENERIC_EMAIL: + return AlertCategory.Email; + case GENERIC_NAVIGATION: + return AlertCategory.Simple; + case RIOT: + case SIGNAL: + case TELEGRAM: + case WHATSAPP: + case CONVERSATIONS: + case FACEBOOK: + case FACEBOOK_MESSENGER: + case TWITTER: + return AlertCategory.InstantMessage; + case UNKNOWN: + return AlertCategory.Simple; + } + return AlertCategory.Simple; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java index 1767c15e..be13a084 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java @@ -21,6 +21,7 @@ public class GattCharacteristic { public static final UUID UUID_CHARACTERISTIC_ALERT_CATEGORY_ID = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A43"))); public static final UUID UUID_CHARACTERISTIC_ALERT_CATEGORY_ID_BIT_MASK = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A42"))); public static final UUID UUID_CHARACTERISTIC_ALERT_LEVEL = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2A06"))); + public static final UUID UUID_CHARACTERISTIC_CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString((String.format(AbstractBTLEDeviceSupport.BASE_UUID, "2902"))); public static final byte NO_ALERT = 0x0; public static final byte MILD_ALERT = 0x1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java index 1f37e42a..3ce3cc7c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java @@ -1,12 +1,66 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification; +import android.bluetooth.BluetoothGattCharacteristic; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile; +import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class AlertNotificationProfile extends AbstractBleProfile { + private static final Logger LOG = LoggerFactory.getLogger(AlertNotificationProfile.class); + private static final int MAX_MSG_LENGTH = 18; + public AlertNotificationProfile(T support) { super(support); } + public void newAlert(TransactionBuilder builder, NewAlert alert, OverflowStrategy strategy) { + BluetoothGattCharacteristic characteristic = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_NEW_ALERT); + if (characteristic != null) { + String message = alert.getMessage(); + if (message.length() > MAX_MSG_LENGTH && strategy == OverflowStrategy.TRUNCATE) { + message = StringUtils.truncate(message, MAX_MSG_LENGTH); + } + int numChunks = message.length() / MAX_MSG_LENGTH; + if (message.length() % MAX_MSG_LENGTH > 0) { + numChunks++; + } + + for (int i = 0; i < numChunks; i++) { + int offset = i * MAX_MSG_LENGTH; + int restLength = message.length() - offset; + message = message.substring(offset, offset + Math.min(MAX_MSG_LENGTH, restLength)); + if (message.length() == 0) { + break; + } + writeAlertMessage(builder, characteristic, alert, message, i); + } + } else { + LOG.warn("NEW_ALERT characteristic not available"); + } + } + + protected void writeAlertMessage(TransactionBuilder builder, BluetoothGattCharacteristic characteristic, NewAlert alert, String message, int chunk) { + try { + ByteArrayOutputStream stream = new ByteArrayOutputStream(100); + stream.write(alert.getCategory().getId()); + stream.write(alert.getNumAlerts()); + stream.write(BLETypeConversions.toUtf8s(message)); + + builder.write(characteristic, stream.toByteArray()); + } catch (IOException ex) { + // aint gonna happen + LOG.error("Error writing alert message to ByteArrayOutputStream"); + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/NewAlert.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/NewAlert.java new file mode 100644 index 00000000..c6232733 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/NewAlert.java @@ -0,0 +1,52 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification; + +/** + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.new_alert.xml&u=org.bluetooth.characteristic.new_alert.xml + * + Recommended Usage for Text String Information Field in New Incoming Alert: + + The usage of this text is up to the implementation, but the recommended text for the category is defined as following for best user experience: + + Category: Simple Alert - The title of the alert + + Category: Email - Sender name + + Category: News - Title of the news feed + + Category: Call - Caller name or caller ID + + Category: Missed call - Caller name or caller ID + + Category: SMS - Sender name or caller ID + + Category: Voice mail - Sender name or caller ID + + Category: Schedule - Title of the schedule + + Category Hig:h Prioritized Aler - Title of the alert + + Category: Instant Messaging - Sender name + */ +public class NewAlert { + private final AlertCategory category; + private final int numAlerts; + private final String message; + + public NewAlert(AlertCategory category, int /*uint8*/ numAlerts, String /*utf8s*/ message) { + this.category = category; + this.numAlerts = numAlerts; + this.message = message; + } + + public AlertCategory getCategory() { + return category; + } + + public int getNumAlerts() { + return numAlerts; + } + + public String getMessage() { + return message; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/OverflowStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/OverflowStrategy.java new file mode 100644 index 00000000..5fbbcac5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/OverflowStrategy.java @@ -0,0 +1,6 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification; + +public enum OverflowStrategy { + TRUNCATE, + MAKE_MULTIPLE +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/common/SimpleNotification.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/common/SimpleNotification.java new file mode 100644 index 00000000..1aed3bb6 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/common/SimpleNotification.java @@ -0,0 +1,21 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.common; + +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; + +public class SimpleNotification { + private final String message; + private final AlertCategory alertCategory; + + public SimpleNotification(String message, AlertCategory alertCategory) { + this.message = message; + this.alertCategory = alertCategory; + } + + public AlertCategory getAlertCategory() { + return alertCategory; + } + + public String getMessage() { + return message; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index e5ef862a..e58dec76 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -53,6 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; 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.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; @@ -61,10 +62,13 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactio import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; +import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.FetchActivityOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.UpdateFirmwareOperation; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR; @@ -205,19 +209,19 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return mDeviceInfo; } - private MiBandSupport sendDefaultNotification(TransactionBuilder builder, short repeat, BtLEAction extraAction) { + private MiBandSupport sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) { LOG.info("Sending notification to MiBand: (" + repeat + " times)"); NotificationStrategy strategy = getNotificationStrategy(); for (short i = 0; i < repeat; i++) { - strategy.sendDefaultNotification(builder, extraAction); + strategy.sendDefaultNotification(builder, simpleNotification, extraAction); } return this; } /** * 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 flashTimes * @param flashColour * @param originalColour @@ -225,8 +229,8 @@ 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 builder */ - private MiBandSupport sendCustomNotification(VibrationProfile vibrationProfile, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { - getNotificationStrategy().sendCustomNotification(vibrationProfile, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); + private MiBandSupport sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { + getNotificationStrategy().sendCustomNotification(vibrationProfile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); LOG.info("Sending notification to MiBand"); return this; } @@ -454,17 +458,17 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return this; } - private void performDefaultNotification(String task, short repeat, BtLEAction extraAction) { + private void performDefaultNotification(String task, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) { try { TransactionBuilder builder = performInitialized(task); - sendDefaultNotification(builder, repeat, extraAction); + sendDefaultNotification(builder, simpleNotification, repeat, extraAction); builder.queue(getQueue()); } catch (IOException ex) { LOG.error("Unable to send notification to MI device", ex); } } - private void performPreferredNotification(String task, String notificationOrigin, BtLEAction extraAction) { + private void performPreferredNotification(String task, SimpleNotification simpleNotification, String notificationOrigin, BtLEAction extraAction) { try { TransactionBuilder builder = performInitialized(task); Prefs prefs = GBApplication.getPrefs(); @@ -479,7 +483,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { int flashDuration = getPreferredFlashDuration(notificationOrigin, prefs); // setLowLatency(builder); - sendCustomNotification(profile, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); + sendCustomNotification(profile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); // setHighLatency(builder); // sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder); builder.queue(getQueue()); @@ -549,8 +553,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return; } + String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()).trim(); + SimpleNotification simpleNotification = new SimpleNotification(message, BLETypeConversions.toAlertCategory(notificationSpec.type)); + String origin = notificationSpec.type.getGenericType(); - performPreferredNotification(origin + " received", origin, null); + performPreferredNotification(origin + " received", simpleNotification, origin, null); } private void onAlarmClock(NotificationSpec notificationSpec) { @@ -561,7 +568,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return !isAlarmClockRinging(); } }; - performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, abortAction); + String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()); + SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.HighPriorityAlert); + performPreferredNotification("alarm clock ringing", simpleNotification, MiBandConst.ORIGIN_ALARM_CLOCK, abortAction); } @Override @@ -625,7 +634,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return !isTelephoneRinging(); } }; - performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, abortAction); + String message = NotificationUtils.getPreferredTextFor(callSpec); + SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.IncomingCall); + performPreferredNotification("incoming call", simpleNotification, MiBandConst.ORIGIN_INCOMING_CALL, abortAction); } else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) { telephoneRinging = false; } @@ -716,7 +727,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return !isLocatingDevice; } }; - performDefaultNotification("locating device", (short) 255, abortAction); + SimpleNotification simpleNotification = new SimpleNotification(getContext().getString(R.string.find_device_you_found_it), AlertCategory.HighPriorityAlert); + performDefaultNotification("locating device", simpleNotification, (short) 255, abortAction); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java index 45a8e45b..e72add17 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; /** * Does not do anything. @@ -14,12 +15,12 @@ public class NoNotificationStrategy implements NotificationStrategy { private static final Logger LOG = LoggerFactory.getLogger(NoNotificationStrategy.class); @Override - public void sendDefaultNotification(TransactionBuilder builder, BtLEAction extraAction) { + public void sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, BtLEAction extraAction) { LOG.info("dummy notification stragegy: default notification"); } @Override - public void sendCustomNotification(VibrationProfile vibrationProfile, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { - LOG.info("dummy notification stragegy: custom notification"); + 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); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java index 1007f7ce..d8cbca95 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java @@ -3,14 +3,15 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; public interface NotificationStrategy { - void sendDefaultNotification(TransactionBuilder builder, BtLEAction extraAction); + void sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, BtLEAction extraAction); /** * Adds a custom notification to the given transaction builder - * * @param vibrationProfile specifies how and how often the Band shall vibrate. + * @param simpleNotification * @param flashTimes * @param flashColour * @param originalColour @@ -18,5 +19,5 @@ 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 builder */ - void sendCustomNotification(VibrationProfile vibrationProfile, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder); + void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java index ab28be7b..b7d74d15 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java @@ -10,6 +10,7 @@ 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.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; public class V1NotificationStrategy implements NotificationStrategy { private static final Logger LOG = LoggerFactory.getLogger(V1NotificationStrategy.class); @@ -24,7 +25,7 @@ public class V1NotificationStrategy implements NotificationStrategy { } @Override - public void sendDefaultNotification(TransactionBuilder builder, BtLEAction extraAction) { + public void sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, BtLEAction extraAction) { BluetoothGattCharacteristic characteristic = support.getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); builder.write(characteristic, getDefaultNotification()); builder.add(extraAction); @@ -53,8 +54,8 @@ public class V1NotificationStrategy implements NotificationStrategy { /** * Adds a custom notification to the given transaction builder - * * @param vibrationProfile specifies how and how often the Band shall vibrate. + * @param simpleNotification * @param flashTimes * @param flashColour * @param originalColour @@ -63,7 +64,7 @@ public class V1NotificationStrategy implements NotificationStrategy { * @param builder */ @Override - public void sendCustomNotification(VibrationProfile vibrationProfile, 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) { BluetoothGattCharacteristic controlPoint = support.getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); for (short i = 0; i < vibrationProfile.getRepeat(); i++) { int[] onOffSequence = vibrationProfile.getOnOffSequence(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java index 8d213543..65957f09 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java @@ -7,6 +7,10 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSuppo 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.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; public class V2NotificationStrategy implements NotificationStrategy { private final AbstractBTLEDeviceSupport support; @@ -20,12 +24,12 @@ public class V2NotificationStrategy implements NotificationStrategy { } @Override - public void sendDefaultNotification(TransactionBuilder builder, BtLEAction extraAction) { + public void sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, BtLEAction extraAction) { VibrationProfile profile = VibrationProfile.getProfile(VibrationProfile.ID_MEDIUM, (short) 3); - sendCustomNotification(profile, extraAction, builder); + sendCustomNotification(profile, simpleNotification, extraAction, builder); } - protected void sendCustomNotification(VibrationProfile vibrationProfile, BtLEAction extraAction, TransactionBuilder builder) { + protected void sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, BtLEAction extraAction, TransactionBuilder builder) { //use the new alert characteristic BluetoothGattCharacteristic alert = support.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL); for (short i = 0; i < vibrationProfile.getRepeat(); i++) { @@ -49,11 +53,18 @@ 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 - public void sendCustomNotification(VibrationProfile vibrationProfile, 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) { // all other parameters are unfortunately not supported anymore ;-( - sendCustomNotification(vibrationProfile, extraAction, builder); + sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2NotificationStrategy.java index 3cfed0a9..d5a48330 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2NotificationStrategy.java @@ -7,6 +7,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSuppo 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.AlertNotificationProfile; +import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.V2NotificationStrategy; public class Mi2NotificationStrategy extends V2NotificationStrategy { @@ -16,7 +18,7 @@ public class Mi2NotificationStrategy extends V2NotificationStrategy { } @Override - protected void sendCustomNotification(VibrationProfile vibrationProfile, 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++) { @@ -38,11 +40,13 @@ public class Mi2NotificationStrategy extends V2NotificationStrategy { } } } + + } @Override - public void sendCustomNotification(VibrationProfile vibrationProfile, 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) { // all other parameters are unfortunately not supported anymore ;-( - sendCustomNotification(vibrationProfile, extraAction, builder); + sendCustomNotification(vibrationProfile, simpleNotification, extraAction, builder); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java index 6d9a83fc..444d0e59 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; +import android.support.annotation.NonNull; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; @@ -67,8 +68,10 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile; +import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.CheckAuthenticationNeededAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; @@ -79,7 +82,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.I import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; +import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COUNT; @@ -301,19 +306,19 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return mDeviceInfo; } - private MiBand2Support sendDefaultNotification(TransactionBuilder builder, short repeat, BtLEAction extraAction) { + private MiBand2Support sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) { LOG.info("Sending notification to MiBand: (" + repeat + " times)"); NotificationStrategy strategy = getNotificationStrategy(); for (short i = 0; i < repeat; i++) { - strategy.sendDefaultNotification(builder, extraAction); + strategy.sendDefaultNotification(builder, simpleNotification, extraAction); } return this; } /** * Adds a custom notification to the given transaction builder - * * @param vibrationProfile specifies how and how often the Band shall vibrate. + * @param simpleNotification * @param flashTimes * @param flashColour * @param originalColour @@ -321,8 +326,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { * @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example. * @param builder */ - private MiBand2Support sendCustomNotification(VibrationProfile vibrationProfile, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { - getNotificationStrategy().sendCustomNotification(vibrationProfile, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); + private MiBand2Support sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) { + getNotificationStrategy().sendCustomNotification(vibrationProfile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); LOG.info("Sending notification to MiBand"); return this; } @@ -500,17 +505,17 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return this; } - private void performDefaultNotification(String task, short repeat, BtLEAction extraAction) { + private void performDefaultNotification(String task, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) { try { TransactionBuilder builder = performInitialized(task); - sendDefaultNotification(builder, repeat, extraAction); + sendDefaultNotification(builder, simpleNotification, repeat, extraAction); builder.queue(getQueue()); } catch (IOException ex) { LOG.error("Unable to send notification to MI device", ex); } } - private void performPreferredNotification(String task, String notificationOrigin, int alertLevel, BtLEAction extraAction) { + private void performPreferredNotification(String task, String notificationOrigin, SimpleNotification simpleNotification, int alertLevel, BtLEAction extraAction) { try { TransactionBuilder builder = performInitialized(task); Prefs prefs = GBApplication.getPrefs(); @@ -525,7 +530,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { int originalColour = getPreferredOriginalColour(notificationOrigin, prefs); int flashDuration = getPreferredFlashDuration(notificationOrigin, prefs); - sendCustomNotification(profile, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); + sendCustomNotification(profile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder); // sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder); builder.queue(getQueue()); } catch (IOException ex) { @@ -597,8 +602,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { if (notificationSpec.type == NotificationType.UNKNOWN) { alertLevel = MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY; } + String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()).trim(); String origin = notificationSpec.type.getGenericType(); - performPreferredNotification(origin + " received", origin, alertLevel, null); + SimpleNotification simpleNotification = new SimpleNotification(message, BLETypeConversions.toAlertCategory(notificationSpec.type)); + performPreferredNotification(origin + " received", origin, simpleNotification, alertLevel, null); } private void onAlarmClock(NotificationSpec notificationSpec) { @@ -609,7 +616,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return !isAlarmClockRinging(); } }; - performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, abortAction); + String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()); + SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.HighPriorityAlert); + performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, simpleNotification, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, abortAction); } @Override @@ -640,7 +649,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return !isTelephoneRinging(); } }; - performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, MiBand2Service.ALERT_LEVEL_PHONE_CALL, abortAction); + String message = NotificationUtils.getPreferredTextFor(callSpec); + SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.IncomingCall); + 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)) { telephoneRinging = false; } @@ -722,7 +733,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return !isLocatingDevice; } }; - performDefaultNotification("locating device", (short) 255, abortAction); + SimpleNotification simpleNotification = new SimpleNotification(getContext().getString(R.string.find_device_you_found_it), AlertCategory.HighPriorityAlert.HighPriorityAlert); + performDefaultNotification("locating device", simpleNotification, (short) 255, abortAction); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/NotificationUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/NotificationUtils.java new file mode 100644 index 00000000..124b4bcd --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/NotificationUtils.java @@ -0,0 +1,46 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +import android.content.Context; +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; + +public class NotificationUtils { + @NonNull + public static String getPreferredTextFor(NotificationSpec notificationSpec, int lengthBody, int lengthSubject, Context context) { + switch (notificationSpec.type) { + case GENERIC_ALARM_CLOCK: + return StringUtils.getFirstOf(notificationSpec.title, notificationSpec.subject); + case GENERIC_SMS: + case GENERIC_EMAIL: + return formatText(notificationSpec.sender, notificationSpec.subject, notificationSpec.body, lengthBody, lengthSubject, context); + case GENERIC_NAVIGATION: + return StringUtils.getFirstOf(notificationSpec.title, notificationSpec.body); + case RIOT: + case SIGNAL: + case TELEGRAM: + case TWITTER: + case WHATSAPP: + case CONVERSATIONS: + case FACEBOOK: + case FACEBOOK_MESSENGER: + return notificationSpec.body; + } + return ""; + } + + @NonNull + public static String formatText(String sender, String subject, String body, int lengthBody, int lengthSubject, Context context) { + StringBuilder builder = new StringBuilder(); + builder.append(StringUtils.truncate(body, lengthBody)); + builder.append(StringUtils.truncate(subject, lengthSubject)); + builder.append(StringUtils.formatSender(sender, context)); + + return builder.toString(); + } + + public static String getPreferredTextFor(CallSpec callSpec) { + return StringUtils.getFirstOf(callSpec.name, callSpec.number); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java index 8dd6d8cc..0ac480cf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java @@ -1,12 +1,21 @@ package nodomain.freeyourgadget.gadgetbridge.util; +import android.content.Context; +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.R; + public class StringUtils { public static String truncate(String s, int maxLength){ - int length = Math.min(s.length(), maxLength); - - if(length < 0) + if (s == null) { return ""; + } + + int length = Math.min(s.length(), maxLength); + if(length < 0) { + return ""; + } return s.substring(0, length); } @@ -16,10 +25,28 @@ public class StringUtils { } public static String pad(String s, int length, char padChar){ - - while(s.length() < length) + while(s.length() < length) { s += padChar; - + } return s; } + + @NonNull + public static String formatSender(String sender, Context context) { + if (sender == null || sender.length() == 0) { + return ""; + } + return context.getString(R.string.StringUtils_sender, sender); + } + + @NonNull + public static String getFirstOf(String first, String second) { + if (first != null && first.length() > 0) { + return first; + } + if (second != null) { + return second; + } + return ""; + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ec7d4600..5c974869 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -394,4 +394,6 @@ 24H AM/PM Alarm Clock + (%1$s) + You found it!