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
This commit is contained in:
cpfeiffer 2017-02-07 23:49:10 +01:00
parent e2b3394900
commit 4f0674d038
10 changed files with 194 additions and 17 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -406,5 +406,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>
</resources>

View File

@ -210,6 +210,36 @@
android:title="@string/vibration_try"/>
</PreferenceScreen>
<PreferenceScreen
android:key="vibration_profile_key"
android:title="@string/pref_screen_notification_profile_alarm_clock"
android:persistent="false">
<!-- workaround for missing toolbar -->
<PreferenceCategory
android:title="@string/pref_screen_notification_profile_alarm_clock"
/>
<ListPreference
android:defaultValue="@string/p_alarm_clock"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="mi_vibration_profile_alarm_clock"
android:title="@string/miband_prefs_vibration"
android:summary="%s" />
<EditTextPreference
android:defaultValue="3"
android:inputType="number"
android:key="mi_vibration_count_alarm_clock"
android:maxLength="2"
android:title="@string/pref_title_notifications_repetitions" />
<Preference
android:key="mi_try_generic_alarm_clock"
android:persistent="false"
android:title="@string/vibration_try"/>
</PreferenceScreen>
<PreferenceScreen
android:key="vibration_profile_key"
android:title="@string/pref_screen_notification_profile_generic_navigation"