Mi2: Initial support for textual notifications #560

master
cpfeiffer 2017-03-02 00:27:54 +01:00
parent aa6e9608bd
commit 4419200624
18 changed files with 337 additions and 50 deletions

View File

@ -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<>();

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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<T extends AbstractBTLEDeviceSupport> extends AbstractBleProfile<T> {
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");
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification;
public enum OverflowStrategy {
TRUNCATE,
MAKE_MULTIPLE
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 "";
}
}

View File

@ -394,4 +394,6 @@
<string name="timeformat_24h">24H</string>
<string name="timeformat_am_pm">AM/PM</string>
<string name="pref_screen_notification_profile_alarm_clock">Alarm Clock</string>
<string name="StringUtils_sender"> (%1$s)</string>
<string name="find_device_you_found_it">You found it!</string>
</resources>