1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2025-02-16 12:26:47 +01:00

Lefun: Implement notification support

This commit is contained in:
Yukai Li 2020-10-05 05:30:07 -06:00 committed by Gitea
parent a1737c39fb
commit c2375f95f4
5 changed files with 309 additions and 5 deletions

View File

@ -36,6 +36,8 @@ public class NotificationCommand extends BaseCommand {
public static final byte EXTENDED_SERVICE_TYPE_LINE = 5;
public static final byte EXTENDED_SERVICE_TYPE_KAKAOTALK = 6;
public static final int MAX_PAYLOAD_LENGTH = 13;
private byte serviceType;
private byte totalPieces;
private byte currentPiece;
@ -49,7 +51,7 @@ public class NotificationCommand extends BaseCommand {
public void setServiceType(int type) {
if (type < 0 || type > 4)
throw new IllegalArgumentException("Invalid service type");
this.serviceType = (byte)(1 << type);
this.serviceType = (byte) (1 << type);
}
public byte getTotalPieces() {
@ -103,8 +105,9 @@ public class NotificationCommand extends BaseCommand {
@Override
protected byte serializeParams(ByteBuffer params) {
boolean hasExtendedServiceType = (serviceType & (1 << SERVICE_TYPE_EXTENDED)) != 0
&& (extendedServiceType & 0x0f) != 0 ;
int maxPayloadLength = hasExtendedServiceType ? 12 : 13;
&& (extendedServiceType & 0x0f) != 0;
int maxPayloadLength = MAX_PAYLOAD_LENGTH;
if (hasExtendedServiceType) maxPayloadLength -= 1;
if (payload.length > maxPayloadLength)
throw new IllegalStateException("Payload is too long");

View File

@ -71,6 +71,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetFi
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetPpgDataRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.GetSleepDataRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.Request;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.AbstractSendNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.SendCallNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.SendNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.SetAlarmRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.SetTimeRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.requests.StartPpgRequest;
@ -126,7 +129,16 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
@Override
public void onNotification(NotificationSpec notificationSpec) {
try {
TransactionBuilder builder = performInitialized(SetTimeRequest.class.getSimpleName());
SendNotificationRequest request = new SendNotificationRequest(this, builder);
request.setNotification(notificationSpec);
request.perform();
performConnected(builder.getTransaction());
} catch (IOException e) {
GB.toast(getContext(), "Failed to send notification", Toast.LENGTH_SHORT,
GB.ERROR, e);
}
}
@Override
@ -173,7 +185,20 @@ public class LefunDeviceSupport extends AbstractBTLEDeviceSupport {
@Override
public void onSetCallState(CallSpec callSpec) {
switch (callSpec.command) {
case CallSpec.CALL_INCOMING:
try {
TransactionBuilder builder = performInitialized(SetTimeRequest.class.getSimpleName());
SendCallNotificationRequest request = new SendCallNotificationRequest(this, builder);
request.setCallNotification(callSpec);
request.perform();
performConnected(builder.getTransaction());
} catch (IOException e) {
GB.toast(getContext(), "Failed to send call notification", Toast.LENGTH_SHORT,
GB.ERROR, e);
}
break;
}
}
@Override

View File

@ -0,0 +1,98 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
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.lefun.requests;
import android.bluetooth.BluetoothGattCharacteristic;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.NotificationCommand;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSupport;
public abstract class AbstractSendNotificationRequest extends Request {
protected AbstractSendNotificationRequest(LefunDeviceSupport support, TransactionBuilder builder) {
super(support, builder);
}
protected abstract String getMessage();
protected abstract byte getNotificationType();
protected abstract byte getExtendedNotificationType();
@Override
public byte[] createRequest() {
return new byte[0];
}
@Override
protected void doPerform() throws IOException {
byte notificationType = getNotificationType();
byte extendedNotificationType = getExtendedNotificationType();
boolean reserveSpaceForExtended = notificationType == NotificationCommand.SERVICE_TYPE_EXTENDED;
byte[] encoded = getMessage().getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.wrap(encoded);
BluetoothGattCharacteristic characteristic = getSupport()
.getCharacteristic(LefunConstants.UUID_CHARACTERISTIC_LEFUN_WRITE);
List<NotificationCommand> commandList = new ArrayList<>();
for (int i = 0; i < 0xff; ++i) {
NotificationCommand cmd = new NotificationCommand();
cmd.setServiceType(notificationType);
cmd.setExtendedServiceType(extendedNotificationType);
cmd.setCurrentPiece((byte) (i + 1));
int maxPayloadLength = NotificationCommand.MAX_PAYLOAD_LENGTH;
if (reserveSpaceForExtended) maxPayloadLength -= 1;
maxPayloadLength = Math.min(maxPayloadLength, buffer.limit() - buffer.position());
if (maxPayloadLength == 0) break;
byte[] payload = new byte[maxPayloadLength];
buffer.get(payload);
cmd.setPayload(payload);
commandList.add(cmd);
}
for (NotificationCommand cmd : commandList) {
cmd.setTotalPieces((byte) commandList.size());
builder.write(characteristic, cmd.serialize());
}
if (isSelfQueue())
getSupport().performConnected(builder.getTransaction());
}
@Override
public int getCommandId() {
return LefunConstants.CMD_NOTIFICATION;
}
@Override
public boolean expectsResponse() {
return false;
}
}

View File

@ -0,0 +1,67 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
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.lefun.requests;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.NotificationCommand;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSupport;
public class SendCallNotificationRequest extends AbstractSendNotificationRequest {
public SendCallNotificationRequest(LefunDeviceSupport support, TransactionBuilder builder) {
super(support, builder);
}
private CallSpec callNotification;
public CallSpec getCallNotification() {
return callNotification;
}
public void setCallNotification(CallSpec callNotification) {
this.callNotification = callNotification;
}
@Override
protected String getMessage() {
String message = "";
if (callNotification.number != null) {
message = callNotification.number;
}
if (callNotification.name != null) {
if (message.length() > 0) {
message += " - ";
}
message += callNotification.name;
}
return message;
}
@Override
protected byte getNotificationType() {
return NotificationCommand.SERVICE_TYPE_CALL;
}
@Override
protected byte getExtendedNotificationType() {
return 0;
}
}

View File

@ -0,0 +1,111 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
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.lefun.requests;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands.NotificationCommand;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.lefun.LefunDeviceSupport;
public class SendNotificationRequest extends AbstractSendNotificationRequest {
NotificationSpec notification;
public SendNotificationRequest(LefunDeviceSupport support, TransactionBuilder builder) {
super(support, builder);
}
@Override
protected byte getNotificationType() {
switch (notification.type) {
case GENERIC_PHONE:
return NotificationCommand.SERVICE_TYPE_CALL;
case GENERIC_SMS:
case GENERIC_EMAIL:
default:
return NotificationCommand.SERVICE_TYPE_TEXT;
case WECHAT:
return NotificationCommand.SERVICE_TYPE_WECHAT;
case FACEBOOK:
case FACEBOOK_MESSENGER:
case TWITTER:
case LINKEDIN:
case WHATSAPP:
case LINE:
case KAKAO_TALK:
return NotificationCommand.SERVICE_TYPE_EXTENDED;
}
}
@Override
protected byte getExtendedNotificationType() {
switch (notification.type) {
case GENERIC_PHONE:
case GENERIC_SMS:
case GENERIC_EMAIL:
default:
case WECHAT:
return 0;
case FACEBOOK:
case FACEBOOK_MESSENGER:
return NotificationCommand.EXTENDED_SERVICE_TYPE_FACEBOOK;
case TWITTER:
return NotificationCommand.EXTENDED_SERVICE_TYPE_TWITTER;
case LINKEDIN:
return NotificationCommand.EXTENDED_SERVICE_TYPE_LINKEDIN;
case WHATSAPP:
return NotificationCommand.EXTENDED_SERVICE_TYPE_WHATSAPP;
case LINE:
return NotificationCommand.EXTENDED_SERVICE_TYPE_LINE;
case KAKAO_TALK:
return NotificationCommand.EXTENDED_SERVICE_TYPE_KAKAOTALK;
}
}
public NotificationSpec getNotification() {
return notification;
}
public void setNotification(NotificationSpec notification) {
this.notification = notification;
}
@Override
protected String getMessage() {
// Based on nodomain.freeyourgadget.gadgetbridge.service.devices.id115.SendNotificationOperation
String message = "";
if (notification.phoneNumber != null) {
message += notification.phoneNumber + ": ";
}
if (notification.sender != null) {
message += notification.sender + " - ";
} else if (notification.title != null) {
message += notification.title + " - ";
} else if (notification.subject != null) {
message += notification.subject + " - ";
}
if (notification.body != null) {
message += notification.body;
}
return message;
}
}