1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-19 11:30:44 +02:00

Mi Band 8: Upload custom notification icons

This commit is contained in:
José Rebelo 2023-10-24 21:17:08 +01:00
parent 9749716c23
commit 047cc71d28
3 changed files with 104 additions and 14 deletions

View File

@ -75,10 +75,20 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
}
public void setCallback(@Nullable final Callback callback) {
if (callback != null && currentBytes != null) {
LOG.warn("Already uploading {} for another callback, refusing new callback", currentType);
return;
}
this.callback = callback;
}
public void requestUpload(final byte type, final byte[] bytes) {
if (this.currentBytes != null) {
LOG.warn("Already uploading {}, refusing upload of {}", currentType, type);
return;
}
LOG.debug("Requesting upload for {} bytes of type {}", bytes.length, type);
this.currentType = type;

View File

@ -18,10 +18,16 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@ -32,11 +38,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class XiaomiNotificationService extends AbstractXiaomiService {
public class XiaomiNotificationService extends AbstractXiaomiService implements XiaomiDataUploadService.Callback {
private static final Logger LOG = LoggerFactory.getLogger(XiaomiNotificationService.class);
private static final SimpleDateFormat TIMESTAMP_SDF = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ROOT);
@ -49,6 +56,10 @@ public class XiaomiNotificationService extends AbstractXiaomiService {
public static final int CMD_CALL_IGNORE = 5;
public static final int CMD_CANNED_MESSAGES_GET = 9;
public static final int CMD_CANNED_MESSAGES_SET = 12; // also canned message reply
public static final int CMD_NOTIFICATION_ICON_REQUEST = 15;
public static final int CMD_NOTIFICATION_ICON_QUERY = 16;
private String iconPackageName;
public XiaomiNotificationService(final XiaomiSupport support) {
super(support);
@ -77,13 +88,31 @@ public class XiaomiNotificationService extends AbstractXiaomiService {
case CMD_CANNED_MESSAGES_GET:
handleCannedMessages(cmd.getNotification().getCannedMessages());
return;
}
case CMD_NOTIFICATION_ICON_REQUEST:
handleNotificationIconRequest(cmd.getNotification().getNotificationIconRequest());
return;
case CMD_NOTIFICATION_ICON_QUERY:
this.iconPackageName = cmd.getNotification().getNotificationIconQuery().getPackage();
LOG.debug("Watch querying notification icon for {}", iconPackageName);
// TODO
// TODO should we confirm that we have the icon before replying?
getSupport().sendCommand(
"send notification reply for " + iconPackageName,
XiaomiProto.Command.newBuilder()
.setType(COMMAND_TYPE)
.setSubtype(CMD_NOTIFICATION_ICON_REQUEST)
.setNotification(XiaomiProto.Notification.newBuilder()
.setNotificationIconReply(cmd.getNotification().getNotificationIconQuery())
).build()
);
return;
}
LOG.warn("Unhandled notification command {}", cmd.getSubtype());
}
public void onNotification(final NotificationSpec notificationSpec) {
final XiaomiProto.Notification3.Builder notification3 = XiaomiProto.Notification3.newBuilder()
.setId(notificationSpec.getId())
@ -260,4 +289,53 @@ public class XiaomiNotificationService extends AbstractXiaomiService {
return true;
}
}
private void handleNotificationIconRequest(final XiaomiProto.NotificationIconRequest notificationIconRequest) {
if (iconPackageName == null) {
LOG.warn("No icon package name");
return;
}
int unk1 = notificationIconRequest.getUnknown1();
int unk2 = notificationIconRequest.getUnknown2();
final int size = notificationIconRequest.getSize();
LOG.debug("Got notification icon request for size {} for {}, unk1={}, unk2={}", size, iconPackageName, unk1, unk2);
final Drawable icon = NotificationUtils.getAppIcon(getSupport().getContext(), iconPackageName);
if (icon == null) {
LOG.warn("Failed to get icon for {}", iconPackageName);
return;
}
// TODO avoid resize?
final Bitmap bmp = BitmapUtil.toBitmap(icon);
final Bitmap bmpResized = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bmpResized);
final Rect rect = new Rect(0, 0, size, size);
canvas.drawBitmap(bmp, null, rect, null);
// convert from RGBA To ABGR
final ByteBuffer buf = ByteBuffer.allocate(size * size * 4).order(ByteOrder.LITTLE_ENDIAN);
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
buf.putInt(bmpResized.getPixel(x, y));
}
}
getSupport().getDataUploader().setCallback(this);
getSupport().getDataUploader().requestUpload(XiaomiDataUploadService.TYPE_NOTIFICATION_ICON, buf.array());
}
@Override
public void onUploadFinish(final boolean success) {
LOG.debug("Notification icon upload finished: {}", success);
getSupport().getDataUploader().setCallback(null);
}
@Override
public void onUploadProgress(final int progressPercent) {
LOG.debug("Notification icon upload progress: {}", progressPercent);
}
}

View File

@ -564,11 +564,13 @@ message Notification {
optional uint32 unknown8 = 8; // 1 on canned replies request?
// 7, 9 get | 7, 12 set
optional CannedMessages cannedMessages = 9;
// 7, 16
optional NotificationIconRequest notificationIconRequest = 14;
// 7, 15
optional NotificationIconRequest notificationIconProperties = 15;
optional NotificationIconRequest notificationIconConfirm = 16;
optional NotificationIconPackage notificationIconReply = 14;
// 7, 15
optional NotificationIconRequest notificationIconRequest = 15;
// 7, 16
optional NotificationIconPackage notificationIconQuery = 16;
}
message Notification2 {
@ -605,14 +607,14 @@ message CannedMessages {
optional uint32 maxReplies = 3;
}
message NotificationIconProperties {
optional string package = 1;
message NotificationIconRequest {
optional uint32 unknown1 = 1; // 0 probably format
optional uint32 unknown2 = 2; // 3 probably format
optional uint32 size = 3;
}
message NotificationIconRequest {
optional uint32 unknown1 = 1; // 0
optional uint32 unknown2 = 2; // 3
optional uint32 unknown3 = 3; // 28, 44, 80, size?
message NotificationIconPackage {
optional string package = 1;
}
//