1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-25 01:55:50 +01:00

Huami: Display native alarm notification

This commit is contained in:
José Rebelo 2022-06-14 19:15:51 +01:00 committed by Gitea
parent cb0dfaae79
commit 7512147c34
12 changed files with 153 additions and 202 deletions

View File

@ -160,7 +160,7 @@ public class DebugActivity extends AbstractGBActivity {
editContent = findViewById(R.id.editContent);
final ArrayList<String> spinnerArray = new ArrayList<>();
for (NotificationType notificationType : NotificationType.values()) {
for (NotificationType notificationType : NotificationType.sortedValues()) {
spinnerArray.add(notificationType.name());
}
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, spinnerArray);
@ -177,7 +177,7 @@ public class DebugActivity extends AbstractGBActivity {
notificationSpec.body = testString;
notificationSpec.sender = testString;
notificationSpec.subject = testString;
notificationSpec.type = NotificationType.values()[sendTypeSpinner.getSelectedItemPosition()];
notificationSpec.type = NotificationType.sortedValues()[sendTypeSpinner.getSelectedItemPosition()];
notificationSpec.pebbleColor = notificationSpec.type.color;
GBApplication.deviceService().onNotification(notificationSpec);
}

View File

@ -18,6 +18,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.model;
import java.util.Arrays;
import java.util.Comparator;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID;
@ -136,4 +139,22 @@ public enum NotificationType {
return "generic";
}
}
public static NotificationType[] sortedValues() {
final NotificationType[] sorted = NotificationType.values();
Arrays.sort(sorted, new Comparator<NotificationType>() {
@Override public int compare(final NotificationType n1, final NotificationType n2) {
// Keep unknown first
if (n1.equals(NotificationType.UNKNOWN)) {
return -1;
} else if (n2.equals(NotificationType.UNKNOWN)) {
return 1;
}
return n1.name().compareToIgnoreCase(n2.name());
}
});
return sorted;
}
}

View File

@ -65,6 +65,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfittrex.Am
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfittrexpro.AmazfitTRexProSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitvergel.AmazfitVergeLSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitx.AmazfitXSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband4.MiBand4Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband5.MiBand5Support;
@ -168,7 +169,7 @@ public class DeviceSupportFactory {
case MIBAND:
return new ServiceDeviceSupport(new MiBandSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case MIBAND2:
return new ServiceDeviceSupport(new HuamiSupport(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
return new ServiceDeviceSupport(new MiBand2Support(), ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING);
case MIBAND3:
return new ServiceDeviceSupport(new MiBand3Support());
case MIBAND4:

View File

@ -251,7 +251,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_YEAR_OF_BIRTH;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL;
public class HuamiSupport extends AbstractBTLEDeviceSupport {
public abstract class HuamiSupport extends AbstractBTLEDeviceSupport {
// We introduce key press counter for notification purposes
private static int currentButtonActionId = 0;
@ -296,7 +296,6 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
private final GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
private RealtimeSamplesSupport realtimeSamplesSupport;
private boolean alarmClockRinging;
protected boolean isMusicAppStarted = false;
protected MusicSpec bufferMusicSpec = null;
@ -738,7 +737,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
}
}
private void performPreferredNotification(String task, String notificationOrigin, SimpleNotification simpleNotification, int alertLevel, BtLEAction extraAction) {
protected void performPreferredNotification(String task, String notificationOrigin, SimpleNotification simpleNotification, int alertLevel, BtLEAction extraAction) {
try {
TransactionBuilder builder = performInitialized(task);
Prefs prefs = GBApplication.getPrefs();
@ -783,18 +782,10 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
}
}
/*
This works on all Huami devices except Mi Band 2
*/
protected void sendNotificationNew(NotificationSpec notificationSpec, boolean hasExtraHeader) {
sendNotificationNew(notificationSpec, hasExtraHeader, 230);
}
protected void sendNotificationNew(NotificationSpec notificationSpec, boolean hasExtraHeader, int maxLength) {
if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) {
onAlarmClock(notificationSpec);
return;
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
final boolean hasExtraHeader = notificationHasExtraHeader();
final int maxLength = notificationMaxLength();
String senderOrTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title);
@ -853,6 +844,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
prefixlength += 4;
}
// final step: build command
byte[] rawmessage = message.getBytes();
int length = Math.min(rawmessage.length, maxLength - prefixlength);
if (length < rawmessage.length) {
@ -889,6 +881,14 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
}
}
protected boolean notificationHasExtraHeader() {
return false;
}
protected int notificationMaxLength() {
return 230;
}
@Override
public void onSetReminders(ArrayList<? extends Reminder> reminders) {
final TransactionBuilder builder;
@ -1116,38 +1116,9 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
}
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) {
onAlarmClock(notificationSpec);
return;
}
int alertLevel = HuamiService.ALERT_LEVEL_MESSAGE;
if (notificationSpec.type == NotificationType.UNKNOWN) {
alertLevel = HuamiService.ALERT_LEVEL_VIBRATE_ONLY;
}
String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()).trim();
String origin = notificationSpec.type.getGenericType();
SimpleNotification simpleNotification = new SimpleNotification(message, BLETypeConversions.toAlertCategory(notificationSpec.type), notificationSpec.type);
performPreferredNotification(origin + " received", origin, simpleNotification, alertLevel, null);
}
protected void onAlarmClock(NotificationSpec notificationSpec) {
alarmClockRinging = true;
AbortTransactionAction abortAction = new StopNotificationAction(getCharacteristic(UUID_CHARACTERISTIC_ALERT_LEVEL)) {
@Override
protected boolean shouldAbort() {
return !isAlarmClockRinging();
}
};
String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext());
SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.HighPriorityAlert, notificationSpec.type);
performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, simpleNotification, HuamiService.ALERT_LEVEL_VIBRATE_ONLY, abortAction);
}
@Override
public void onDeleteNotification(int id) {
alarmClockRinging = false; // we should have the notificationtype at least to check
}
@Override
@ -1255,11 +1226,6 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
}
}
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;
@ -2854,8 +2820,8 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
@Override
public void onSendWeather(WeatherSpec weatherSpec) {
// FIXME: currently HuamiSupport *is* MiBand2 support, so return if we are using Mi Band 2
if (gbDevice.getType() == DeviceType.MIBAND2) {
final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
if (!coordinator.supportsWeather()) {
return;
}
@ -3913,9 +3879,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
requestAlarms(builder);
}
public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException {
return new MiBand2FWHelper(uri, context);
}
public abstract HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException;
public UpdateFirmwareOperation createUpdateFirmwareOperation(Uri uri) {
return new UpdateFirmwareOperation(uri, this);

View File

@ -53,11 +53,6 @@ public class AmazfitBipSupport extends HuamiSupport {
return new AmazfitBipTextNotificationStrategy(this);
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
super.sendNotificationNew(notificationSpec, false);
}
@Override
public void onFindDevice(boolean start) {
CallSpec callSpec = new CallSpec();

View File

@ -47,8 +47,13 @@ public class AmazfitBipSSupport extends AmazfitBipSupport {
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
super.sendNotificationNew(notificationSpec, true, 512);
protected boolean notificationHasExtraHeader() {
return true;
}
@Override
protected int notificationMaxLength() {
return 512;
}
@Override

View File

@ -49,11 +49,10 @@ public class AmazfitGTSSupport extends AmazfitBipSupport {
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
super.sendNotificationNew(notificationSpec, true);
protected boolean notificationHasExtraHeader() {
return true;
}
@Override
public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException {
return new AmazfitGTSFWHelper(uri, context);

View File

@ -18,8 +18,6 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import org.slf4j.Logger;
@ -31,14 +29,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts2.AmazfitGTS2MiniFWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiIcon;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts.AmazfitGTSSupport;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class AmazfitGTS2MiniSupport extends AmazfitGTS2Support {
@ -53,122 +43,4 @@ public class AmazfitGTS2MiniSupport extends AmazfitGTS2Support {
public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException {
return new AmazfitGTS2MiniFWHelper(uri, context);
}
@Override
protected void sendNotificationNew(NotificationSpec notificationSpec, boolean hasExtraHeader, int maxLength) {
// step 1: bail out if this is an alarm clock notification
if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) {
onAlarmClock(notificationSpec);
return;
}
// step 2: (formerly in try block) get notification type
AlertCategory alertCategory = AlertCategory.CustomHuami;
byte customIconId = HuamiIcon.mapToIconId(notificationSpec.type);
// step 3: build notification (sender+body)
/*
* Format followed by the device:
* <SENDER> \0 <BODY> \0 <APP SUFFIX>
* sender will get ignored except for the icons
* specified on the HuamiIcon class.
* for email, App Suffix will be taken as sender
*/
String senderOrTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title);
boolean acceptsSender = HuamiIcon.acceptsSender(customIconId);
String message;
if (!acceptsSender && !senderOrTitle.equals(notificationSpec.sourceName)) {
// make sure we always include the notification sender/title
message = "-\0"; //leave title blank, it's useless
message += StringUtils.truncate(senderOrTitle, 64) + "\n";
} else {
message = StringUtils.truncate(senderOrTitle, 64) + "\0";
}
if (notificationSpec.subject != null) {
message += StringUtils.truncate(notificationSpec.subject, 128) + "\n\n";
}
if (notificationSpec.body != null) {
message += StringUtils.truncate(notificationSpec.body, 512);
}
if (notificationSpec.body == null && notificationSpec.subject == null) {
message += " "; // if we have no body we have to send at least something on some devices, else they reboot (Bip S)
}
try {
TransactionBuilder builder = performInitialized("new notification");
// step 4: append suffix
byte[] appSuffix = "\0 \0".getBytes();
int suffixlength = appSuffix.length;
// The SMS icon for AlertCategory.SMS is unique and not available as iconId
if (notificationSpec.type == NotificationType.GENERIC_SMS) {
alertCategory = AlertCategory.SMS;
}
// EMAIL icon does not work in FW 0.0.8.74, it did in 0.0.7.90 (old comment)
// EMAIL will take the sender from the suffix instead
else if (customIconId == HuamiIcon.EMAIL) {
alertCategory = AlertCategory.Email;
appSuffix = ("\0"+senderOrTitle+"\0").getBytes();
suffixlength = appSuffix.length;
}
// if I understood correctly, we don't need the extra logic for mi band 2 here
int prefixlength = 2;
if (alertCategory == AlertCategory.CustomHuami) {
String appName;
prefixlength = 3;
final PackageManager pm = getContext().getPackageManager();
ApplicationInfo ai = null;
try {
ai = pm.getApplicationInfo(notificationSpec.sourceAppId, 0);
} catch (PackageManager.NameNotFoundException ignored) {
}
if (ai == null) {
appName = "\0" + "UNKNOWN" + "\0";
} else {
appName = "\0" + pm.getApplicationLabel(ai) + "\0";
}
appSuffix = appName.getBytes();
suffixlength = appSuffix.length;
}
if (hasExtraHeader) {
prefixlength += 4;
}
// final step: build command
byte[] rawmessage = message.getBytes();
int length = Math.min(rawmessage.length, maxLength - prefixlength);
if (length < rawmessage.length) {
length = StringUtils.utf8ByteLength(message, length);
}
byte[] command = new byte[length + prefixlength + suffixlength];
int pos = 0;
command[pos++] = (byte) alertCategory.getId();
if (hasExtraHeader) {
command[pos++] = 0; // TODO
command[pos++] = 0;
command[pos++] = 0;
command[pos++] = 0;
}
command[pos++] = 1;
if (alertCategory == AlertCategory.CustomHuami) {
command[pos] = customIconId;
}
System.arraycopy(rawmessage, 0, command, prefixlength, length);
System.arraycopy(appSuffix, 0, command, prefixlength + length, appSuffix.length);
writeToChunked(builder, 0, command);
builder.queue(getQueue());
} catch (IOException ex) {
LOG.error("Unable to send notification to device", ex);
}
}
}

View File

@ -38,8 +38,8 @@ public class AmazfitNeoSupport extends MiBand5Support {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitNeoSupport.class);
@Override
public void onNotification(NotificationSpec notificationSpec) {
super.sendNotificationNew(notificationSpec, false);
protected boolean notificationHasExtraHeader() {
return false;
}
@Override

View File

@ -53,8 +53,8 @@ public class AmazfitVergeLSupport extends AmazfitBipSupport {
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
super.sendNotificationNew(notificationSpec, true);
protected boolean notificationHasExtraHeader() {
return true;
}
@Override

View File

@ -0,0 +1,94 @@
/* Copyright (C) 2017-2021 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic.UUID_CHARACTERISTIC_ALERT_LEVEL;
import android.content.Context;
import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2FWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory;
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.actions.StopNotificationAction;
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
public class MiBand2Support extends HuamiSupport {
private static final Logger LOG = LoggerFactory.getLogger(MiBand2Support.class);
private boolean alarmClockRinging;
@Override
public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException {
return new MiBand2FWHelper(uri, context);
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
if (notificationSpec.type == NotificationType.GENERIC_ALARM_CLOCK) {
onAlarmClock(notificationSpec);
return;
}
int alertLevel = HuamiService.ALERT_LEVEL_MESSAGE;
if (notificationSpec.type == NotificationType.UNKNOWN) {
alertLevel = HuamiService.ALERT_LEVEL_VIBRATE_ONLY;
}
String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()).trim();
String origin = notificationSpec.type.getGenericType();
SimpleNotification simpleNotification = new SimpleNotification(message, BLETypeConversions.toAlertCategory(notificationSpec.type), notificationSpec.type);
performPreferredNotification(origin + " received", origin, simpleNotification, alertLevel, null);
}
protected void onAlarmClock(NotificationSpec notificationSpec) {
alarmClockRinging = true;
AbortTransactionAction abortAction = new StopNotificationAction(getCharacteristic(UUID_CHARACTERISTIC_ALERT_LEVEL)) {
@Override
protected boolean shouldAbort() {
return !isAlarmClockRinging();
}
};
String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext());
SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.HighPriorityAlert, notificationSpec.type);
performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, simpleNotification, HuamiService.ALERT_LEVEL_VIBRATE_ONLY, abortAction);
}
@Override
public void onDeleteNotification(int id) {
alarmClockRinging = false; // we should have the notificationtype at least to check
}
private boolean isAlarmClockRinging() {
// don't synchronize, this is not really important
return alarmClockRinging;
}
}

View File

@ -36,8 +36,8 @@ public class MiBand4Support extends MiBand3Support {
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
super.sendNotificationNew(notificationSpec, true);
protected boolean notificationHasExtraHeader() {
return true;
}
@Override