From 4f0674d038ac95aab2935b9b95022e8470c4dd36 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 7 Feb 2017 23:49:10 +0100 Subject: [PATCH] Support for alarm clock notifications for Mi1 + Mi2 #538 No support for Pebble and HPlus for now. Atm relies on the CM deskclock alarm, which nicely broadcasts events about the current alarm. See https://github.com/CyanogenMod/android_packages_apps_DeskClock.git --- .../devices/miband/MiBandConst.java | 1 + .../miband/MiBandPreferencesActivity.java | 2 + .../externalevents/AlarmClockReceiver.java | 67 +++++++++++++++++++ .../gadgetbridge/model/NotificationType.java | 4 +- .../service/DeviceCommunicationService.java | 15 ++++- .../service/devices/miband/MiBandSupport.java | 25 ++++++- .../devices/miband2/MiBand2Support.java | 38 +++++++---- .../actions/StopNotificationAction.java | 28 ++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/miband_preferences.xml | 30 +++++++++ 10 files changed, 194 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java index 98877a83..b8e6f456 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java @@ -23,6 +23,7 @@ public final class MiBandConst { public static final String ORIGIN_INCOMING_CALL = "incoming_call"; + public static final String ORIGIN_ALARM_CLOCK = "alarm_clock"; public static final String MI_GENERAL_NAME_PREFIX = "MI"; public static final String MI_BAND2_NAME = "MI Band 2"; public static final String MI_1 = "1"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java index caeb4e2a..129f4afe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java @@ -17,6 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_ALARM_CLOCK; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_INCOMING_CALL; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DATEFORMAT; @@ -146,6 +147,7 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { prefKeys.add(PREF_MIBAND_FITNESS_GOAL); prefKeys.add(PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR); prefKeys.add(PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS); + prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_ALARM_CLOCK)); prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL)); for (NotificationType type : NotificationType.values()) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java new file mode 100644 index 00000000..6753c5ed --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java @@ -0,0 +1,67 @@ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; + +public class AlarmClockReceiver extends BroadcastReceiver { + /** + * AlarmActivity and AlarmService (when unbound) listen for this broadcast intent + * so that other applications can snooze the alarm (after ALARM_ALERT_ACTION and before + * ALARM_DONE_ACTION). + */ + public static final String ALARM_SNOOZE_ACTION = "com.android.deskclock.ALARM_SNOOZE"; + + /** + * AlarmActivity and AlarmService listen for this broadcast intent so that other + * applications can dismiss the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION). + */ + public static final String ALARM_DISMISS_ACTION = "com.android.deskclock.ALARM_DISMISS"; + + /** A public action sent by AlarmService when the alarm has started. */ + public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT"; + + /** A public action sent by AlarmService when the alarm has stopped for any reason. */ + public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE"; + private int lastId; + + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (ALARM_ALERT_ACTION.equals(action)) { + sendAlarm(true); + } else if (ALARM_DONE_ACTION.equals(action)) { + sendAlarm(false); + } + } + + + + private synchronized void sendAlarm(boolean on) { + dismissLastAlarm(); + if (on) { + lastId = generateId(); + NotificationSpec spec = new NotificationSpec(); + spec.type = NotificationType.GENERIC_ALARM_CLOCK; + // can we get the alarm title somehow? + GBApplication.deviceService().onNotification(spec); + } + } + + private void dismissLastAlarm() { + if (lastId != 0) { + GBApplication.deviceService().onDeleteNotification(lastId); + lastId = 0; + } + } + + private int generateId() { + // lacks negative values, but should be sufficient + return (int) (Math.random() * Integer.MAX_VALUE); + } +} 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 daae8f4c..2f012825 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java @@ -17,7 +17,8 @@ public enum NotificationType { SIGNAL(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.BlueMoon), TWITTER(PebbleIconID.NOTIFICATION_TWITTER, PebbleColor.BlueMoon), TELEGRAM(PebbleIconID.NOTIFICATION_TELEGRAM, PebbleColor.PictonBlue), - WHATSAPP(PebbleIconID.NOTIFICATION_WHATSAPP, PebbleColor.MayGreen); + WHATSAPP(PebbleIconID.NOTIFICATION_WHATSAPP, PebbleColor.MayGreen), + GENERIC_ALARM_CLOCK(PebbleIconID.ALARM_CLOCK, PebbleColor.Red); public int icon; public byte color; @@ -41,6 +42,7 @@ public enum NotificationType { case GENERIC_EMAIL: case GENERIC_NAVIGATION: case GENERIC_SMS: + case GENERIC_ALARM_CLOCK: return getFixedValue(); case FACEBOOK: case TWITTER: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 277e2223..019e245f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -27,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.OnboardingActivity; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.K9Receiver; @@ -152,8 +153,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private MusicPlaybackReceiver mMusicPlaybackReceiver = null; private TimeChangeReceiver mTimeChangeReceiver = null; private BluetoothConnectReceiver mBlueToothConnectReceiver = null; - private AlarmReceiver mAlarmReceiver = null; + private AlarmClockReceiver mAlarmClockReceiver = null; + private AlarmReceiver mAlarmReceiver = null; private Random mRandom = new Random(); /** @@ -619,6 +621,13 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mAlarmReceiver = new AlarmReceiver(); registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM")); } + if (mAlarmClockReceiver == null) { + mAlarmClockReceiver = new AlarmClockReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(AlarmClockReceiver.ALARM_ALERT_ACTION); + filter.addAction(AlarmClockReceiver.ALARM_DONE_ACTION); + registerReceiver(mAlarmClockReceiver, filter); + } } else { if (mPhoneCallReceiver != null) { unregisterReceiver(mPhoneCallReceiver); @@ -652,6 +661,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere unregisterReceiver(mAlarmReceiver); mAlarmReceiver = null; } + if (mAlarmClockReceiver != null) { + unregisterReceiver(mAlarmClockReceiver); + mAlarmClockReceiver = null; + } } } 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 be1e32fb..e5ef862a 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 @@ -50,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; 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.BtLEAction; @@ -102,6 +103,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); private RealtimeSamplesSupport realtimeSamplesSupport; + private boolean alarmClockRining; + private boolean alarmClockRinging; public MiBandSupport() { super(LOG); @@ -541,13 +544,29 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { + if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) { + onAlarmClock(notificationSpec); + return; + } + String origin = notificationSpec.type.getGenericType(); performPreferredNotification(origin + " received", origin, null); } + private void onAlarmClock(NotificationSpec notificationSpec) { + alarmClockRining = true; + AbortTransactionAction abortAction = new AbortTransactionAction() { + @Override + protected boolean shouldAbort() { + return !isAlarmClockRinging(); + } + }; + performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, abortAction); + } + @Override public void onDeleteNotification(int id) { - + alarmClockRining = false; // we should have the notificationtype at least to check } @Override @@ -616,6 +635,10 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { } + private boolean isAlarmClockRinging() { + // don't synchronize, this is not really important + return alarmClockRinging; + } private boolean isTelephoneRinging() { // don't synchronize, this is not really important return telephoneRinging; 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 d068c2c5..09f7e6a4 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 @@ -73,6 +73,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.CheckAuthenti import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions.StopNotificationAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.FetchActivityOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation; @@ -123,6 +124,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); private RealtimeSamplesSupport realtimeSamplesSupport; + private boolean alarmClockRinging; public MiBand2Support() { super(LOG); @@ -586,6 +588,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { + if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) { + onAlarmClock(notificationSpec); + return; + } int alertLevel = MiBand2Service.ALERT_LEVEL_MESSAGE; if (notificationSpec.type == NotificationType.UNKNOWN) { alertLevel = MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY; @@ -594,9 +600,20 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { performPreferredNotification(origin + " received", origin, alertLevel, null); } + private void onAlarmClock(NotificationSpec notificationSpec) { + alarmClockRinging = true; + AbortTransactionAction abortAction = new StopNotificationAction(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL)) { + @Override + protected boolean shouldAbort() { + return !isAlarmClockRinging(); + } + }; + performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, abortAction); + } + @Override public void onDeleteNotification(int id) { - + alarmClockRinging = false; // we should have the notificationtype at least to check } @Override @@ -616,23 +633,11 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void onSetCallState(CallSpec callSpec) { if (callSpec.command == CallSpec.CALL_INCOMING) { telephoneRinging = true; - AbortTransactionAction abortAction = new AbortTransactionAction() { + AbortTransactionAction abortAction = new StopNotificationAction(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL)) { @Override protected boolean shouldAbort() { return !isTelephoneRinging(); } - - @Override - public boolean run(BluetoothGatt gatt) { - if (!super.run(gatt)) { - // send a signal to stop the vibration - BluetoothGattCharacteristic characteristic = MiBand2Support.this.getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL); - characteristic.setValue(new byte[] {MiBand2Service.ALERT_LEVEL_NONE}); - gatt.writeCharacteristic(characteristic); - return false; - } - return true; - } }; performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, MiBand2Service.ALERT_LEVEL_PHONE_CALL, abortAction); } else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) { @@ -644,6 +649,11 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { } + private boolean isAlarmClockRinging() { + // don't synchronize, this is not really important + return alarmClockRinging; + } + private boolean isTelephoneRinging() { // don't synchronize, this is not really important return telephoneRinging; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java new file mode 100644 index 00000000..1de8ad6c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java @@ -0,0 +1,28 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; + +public abstract class StopNotificationAction extends AbortTransactionAction { + + private final BluetoothGattCharacteristic alertLevelCharacteristic; + + public StopNotificationAction(BluetoothGattCharacteristic alertLevelCharacteristic) { + this.alertLevelCharacteristic = alertLevelCharacteristic; + } + + @Override + public boolean run(BluetoothGatt gatt) { + if (!super.run(gatt)) { + // send a signal to stop the vibration + alertLevelCharacteristic.setValue(new byte[]{MiBand2Service.ALERT_LEVEL_NONE}); + gatt.writeCharacteristic(alertLevelCharacteristic); + return false; + } + return true; + } +}; + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3661cbd..86095992 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -406,5 +406,6 @@ 24H AM/PM + Alarm Clock diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index ed215a5a..65bb1759 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -210,6 +210,36 @@ android:title="@string/vibration_try"/> + + + + + + + + + + +