From 48717aaf42cf7b5b2d965e39a7dc7a1d991652af Mon Sep 17 00:00:00 2001 From: Me7c7 Date: Sat, 19 Oct 2024 19:20:12 +0300 Subject: [PATCH] Huawei: Initial music upload support --- .../devices/huawei/HuaweiCoordinator.java | 23 +++ .../devices/huawei/HuaweiInstallHandler.java | 125 +++++++++++- .../devices/huawei/HuaweiMusicUtils.java | 131 +++++++++++++ .../devices/huawei/HuaweiPacket.java | 6 + .../devices/huawei/packets/MusicControl.java | 173 +++++++++++++++++ .../devices/huawei/AsynchronousResponse.java | 6 + .../devices/huawei/HuaweiFwHelper.java | 180 +++++++++++++++--- .../devices/huawei/HuaweiMusicManager.java | 128 +++++++++++++ .../devices/huawei/HuaweiSupportProvider.java | 18 ++ .../requests/GetExtendedMusicInfoParams.java | 44 +++++ .../huawei/requests/GetMusicInfoParams.java | 44 +++++ .../SendUploadMusicFileInfoResponse.java | 33 ++++ 12 files changed, 873 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiMusicUtils.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiMusicManager.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetExtendedMusicInfoParams.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetMusicInfoParams.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendUploadMusicFileInfoResponse.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java index d7731b400..448a9a912 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiCoordinator.java @@ -74,6 +74,10 @@ public class HuaweiCoordinator { private App.AppDeviceParams appDeviceParams; + private HuaweiMusicUtils.MusicCapabilities musicDeviceParams = null; + + private HuaweiMusicUtils.MusicCapabilities musicExtendedDeviceParams = null; + private final HuaweiCoordinatorSupplier parent; private boolean transactionCrypted=true; @@ -492,6 +496,8 @@ public class HuaweiCoordinator { public boolean supportsAppParams(){ return supportsCommandForService(0x2a, 0x06);} + public boolean supportsMusicUploading(){ return supportsCommandForService(0x25, 0x04);} + public boolean supportsWeather() { return supportsCommandForService(0x0f, 0x01); } @@ -712,6 +718,23 @@ public class HuaweiCoordinator { return appDeviceParams; } + public void setExtendedMusicInfoParams(HuaweiMusicUtils.MusicCapabilities musicDeviceParams) { + LOG.info(musicDeviceParams.toString()); + this.musicExtendedDeviceParams = musicDeviceParams; + } + public HuaweiMusicUtils.MusicCapabilities getExtendedMusicInfoParams() { + return musicExtendedDeviceParams; + } + + public void setMusicInfoParams(HuaweiMusicUtils.MusicCapabilities musicDeviceParams) { + LOG.info(musicDeviceParams.toString()); + this.musicDeviceParams = musicDeviceParams; + } + public HuaweiMusicUtils.MusicCapabilities getMusicInfoParams() { + return musicDeviceParams; + } + + public Class getAppManagerActivity() { return AppManagerActivity.class; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java index 3aafb654f..cfb038c77 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiInstallHandler.java @@ -19,9 +19,13 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huawei; import android.content.Context; import android.net.Uri; +import android.text.TextUtils; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; + import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; @@ -31,6 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiAppManager; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiFwHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiMusicManager; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiWatchfaceManager; public class HuaweiInstallHandler implements InstallHandler { @@ -46,6 +51,59 @@ public class HuaweiInstallHandler implements InstallHandler { this.helper = new HuaweiFwHelper(uri, context); } + private HuaweiMusicUtils.FormatRestrictions getRestriction(HuaweiMusicUtils.MusicCapabilities capabilities, String ext) { + List restrictions = capabilities.formatsRestrictions; + if(restrictions == null) + return null; + + for(HuaweiMusicUtils.FormatRestrictions r: restrictions) { + if(ext.equals(r.getName())) { + return r; + } + } + return null; + } + + //TODO: add proper checks + private boolean checkMediaCompatibility(HuaweiMusicUtils.MusicCapabilities capabilities, HuaweiMusicManager.AudioInfo currentMusicInfo) { + if(capabilities == null) { + LOG.error("No media info from device"); + return false; + } + String ext = currentMusicInfo.getExtension(); + + List supportedFormats = capabilities.supportedFormats; + if(supportedFormats == null) { + LOG.error("Format not supported {}", ext); + return false; + } + if(!supportedFormats.contains(ext)) { + LOG.error("Format not supported {}", ext); + return false; + } + + HuaweiMusicUtils.FormatRestrictions restrictions = getRestriction(capabilities, ext); + if(restrictions == null) { + LOG.info("no restriction for: {}", ext); + return true; + } + + LOG.info("bitrate {}", restrictions.bitrate); + LOG.info("channels {}", restrictions.channels); + LOG.info("musicEncode {}", restrictions.musicEncode); + LOG.info("sampleRate {}", restrictions.sampleRate); + LOG.info("unknownBitrate {}", restrictions.unknownBitrate); + + if(currentMusicInfo.getChannels() > restrictions.channels) { + LOG.error("Not supported channels couunt {} > {}", currentMusicInfo.getChannels(), restrictions.channels); + return false; + } + + //TODO: check other restrictions. + + return true; + } + @Override public void validateInstallation(InstallActivity installActivity, GBDevice device) { @@ -57,7 +115,7 @@ public class HuaweiInstallHandler implements InstallHandler { return; } - if(helper.isWatchface()) { + if (helper.isWatchface()) { final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator; HuaweiWatchfaceManager.WatchfaceDescription description = helper.getWatchfaceDescription(); @@ -71,7 +129,6 @@ public class HuaweiInstallHandler implements InstallHandler { GenericItem installItem = new GenericItem(); - if (helper.getPreviewBitmap() != null) { installItem.setPreview(helper.getPreviewBitmap()); } @@ -79,16 +136,16 @@ public class HuaweiInstallHandler implements InstallHandler { installItem.setName(description.title); installActivity.setInstallItem(installItem); if (device.isBusy()) { - LOG.error("Firmware cannot be installed (device busy)"); - installActivity.setInfoText("Firmware cannot be installed (device busy)"); + LOG.error("Watchface cannot be installed (device busy)"); + installActivity.setInfoText("Watchface cannot be installed (device busy)"); installActivity.setInfoText(device.getBusyTask()); installActivity.setInstallEnabled(false); return; } if (!device.isConnected()) { - LOG.error("Firmware cannot be installed (not connected or wrong device)"); - installActivity.setInfoText("Firmware cannot be installed (not connected or wrong device)"); + LOG.error("Watchface cannot be installed (not connected or wrong device)"); + installActivity.setInfoText("Watchface cannot be installed (not connected or wrong device)"); installActivity.setInstallEnabled(false); return; } @@ -106,7 +163,7 @@ public class HuaweiInstallHandler implements InstallHandler { installItem.setIcon(R.drawable.ic_watchface); installActivity.setInfoText(context.getString(R.string.watchface_install_info, installItem.getName(), description.version, description.author)); - LOG.debug("Initialized HuaweiInstallHandler"); + LOG.debug("Initialized HuaweiInstallHandler: Watchface"); } else if (helper.isAPP()) { final HuaweiAppManager.AppConfig config = helper.getAppConfig(); @@ -123,7 +180,7 @@ public class HuaweiInstallHandler implements InstallHandler { installItem.setName(config.bundleName); installActivity.setInstallItem(installItem); if (device.isBusy()) { - LOG.error("Firmware cannot be installed (device busy)"); + LOG.error("App cannot be installed (device busy)"); installActivity.setInfoText("Firmware cannot be installed (device busy)"); installActivity.setInfoText(device.getBusyTask()); installActivity.setInstallEnabled(false); @@ -131,7 +188,7 @@ public class HuaweiInstallHandler implements InstallHandler { } if (!device.isConnected()) { - LOG.error("Firmware cannot be installed (not connected or wrong device)"); + LOG.error("App cannot be installed (not connected or wrong device)"); installActivity.setInfoText("Firmware cannot be installed (not connected or wrong device)"); installActivity.setInstallEnabled(false); return; @@ -149,7 +206,55 @@ public class HuaweiInstallHandler implements InstallHandler { installActivity.setInfoText(context.getString(R.string.app_install_info, installItem.getName(), config.version, config.vendor)); - LOG.debug("Initialized HuaweiInstallHandler"); + LOG.debug("Initialized HuaweiInstallHandler: App"); + } else if (helper.isMusic()) { + final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator; + + HuaweiMusicUtils.MusicCapabilities capabilities = huaweiCoordinatorSupplier.getHuaweiCoordinator().getExtendedMusicInfoParams(); + if(capabilities == null) { + capabilities = huaweiCoordinatorSupplier.getHuaweiCoordinator().getMusicInfoParams(); + } + HuaweiMusicManager.AudioInfo currentMusicInfo = helper.getMusicInfo(); + + boolean isMediaCompatible = checkMediaCompatibility(capabilities, currentMusicInfo); + + this.valid = isMediaCompatible && !TextUtils.isEmpty(helper.getMusicInfo().getFileName()) && !TextUtils.isEmpty(helper.getMusicInfo().getArtist()) && !TextUtils.isEmpty(helper.getMusicInfo().getTitle()); + + installActivity.setInstallEnabled(true); + + GenericItem installItem = new GenericItem(); + + installItem.setName(helper.getFileName()); + installActivity.setInstallItem(installItem); + if (device.isBusy()) { + LOG.error("Music cannot be uploaded (device busy)"); + installActivity.setInfoText("Music cannot be uploaded (device busy)"); + installActivity.setInfoText(device.getBusyTask()); + installActivity.setInstallEnabled(false); + return; + } + + if (!device.isConnected()) { + LOG.error("Music cannot be uploaded (not connected or wrong device)"); + installActivity.setInfoText("Music cannot be uploaded (not connected or wrong device)"); + installActivity.setInstallEnabled(false); + return; + } + + if (!this.valid) { + LOG.error("Music cannot be uploaded"); + installActivity.setInfoText("Music cannot be uploaded"); + installActivity.setInstallEnabled(false); + return; + } + + installItem.setDetails(helper.getMusicInfo().getFileName()); + + installItem.setIcon(R.drawable.ic_music_note); + + installActivity.setInfoText(context.getString(R.string.app_install_info, helper.getMusicInfo().getFileName(), helper.getMusicInfo().getTitle(), helper.getMusicInfo().getArtist())); + + LOG.debug("Initialized HuaweiInstallHandler: Music"); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiMusicUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiMusicUtils.java new file mode 100644 index 000000000..f4c9770f0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiMusicUtils.java @@ -0,0 +1,131 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei; + +import java.util.ArrayList; +import java.util.List; + +public class HuaweiMusicUtils { + + public static class PageStruct { + public short startIndex = 0; + public short endIndex = 0; + public short count = 0; + public byte[] hashCode = null; + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("PageStruct{"); + sb.append("startIndex=").append(startIndex); + sb.append(", endIndex=").append(endIndex); + sb.append(", count=").append(count); + sb.append(", hashCode="); + if (hashCode == null) sb.append("null"); + else { + sb.append('['); + for (int i = 0; i < hashCode.length; ++i) + sb.append(i == 0 ? "" : ", ").append(hashCode[i]); + sb.append(']'); + } + sb.append('}'); + return sb.toString(); + } + } + + public static class FormatRestrictions { + public byte formatIdx = -1; + public int sampleRate = -1; // TODO: not sure + public byte musicEncode = -1; // TODO: not sure + public short bitrate = -1; + public byte channels = -1; + public short unknownBitrate = -1; // TODO: not sure + + // TODO: I am not sure about this. Most of formats unknown for me. + private static final String[] formats = {"mp3", "wav", "aac", "sbc", "msbc", "hwa", "cvsd", "pcm", "ape", "m4a", "flac", "opus", "ogg", "butt", "amr", "imy"}; + + public String getName() { + return (formatIdx >= 0 && formatIdx < formats.length)?formats[formatIdx]:null; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("FormatRestrictions{"); + sb.append("formatIdx=").append(formatIdx); + sb.append(", sampleRate=").append(sampleRate); + sb.append(", musicEncode=").append(musicEncode); + sb.append(", bitrate=").append(bitrate); + sb.append(", channels=").append(channels); + sb.append(", unknownBitrate=").append(unknownBitrate); + sb.append('}'); + return sb.toString(); + } + } + + public static class MusicCapabilities { + public short availableSpace = 0; + public List supportedFormats = null; + public short maxMusicCount = 0; + public short maxPlaylistCount = 0; + public short currentMusicCount = 0; // TODO: not sure + public byte unknown = 0; // TODO: not sure + public List formatsRestrictions = null; + public List pageStruct = null; + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("MusicCapabilities{"); + sb.append("availableSpace=").append(availableSpace); + sb.append(", supportedFormats=").append(supportedFormats); + sb.append(", maxMusicCount=").append(maxMusicCount); + sb.append(", maxPlaylistCount=").append(maxPlaylistCount); + sb.append(", currentMusicCount=").append(currentMusicCount); + sb.append(", unknown=").append(unknown); + sb.append(", formatsRestrictions=").append(formatsRestrictions); + sb.append(", pageStruct=").append(pageStruct); + sb.append('}'); + return sb.toString(); + } + } + + + // TODO: I am not sure about this. Most of formats unknown for me. + private static final String[] formatsByte0 = {"mp3", "wav", "aac", "sbc", "msbc", "hwa", "cvsd"}; + private static final String[] formatsByte1 = {"pcm", "ape", "m4a", "flac", "opus", "ogg", "butt"}; + private static final String[] formatsByte2 = {"amr", "imy"}; + + public static void parseNext(int dt, String[] info, List res) { + for (byte k = 0; k < info.length; k++) { + if ((dt & (1 << k)) != 0) { + res.add(info[k]); + } + } + } + + public static List parseFormats(List data) { + List res = new ArrayList<>(); + + if (data.size() >= 1) { + parseNext(data.get(0), formatsByte0, res); + } + + if (data.size() >= 2) { + parseNext(data.get(1), formatsByte1, res); + } + + if (data.size() >= 3) { + parseNext(data.get(2), formatsByte2, res); + } + + return res; + } + + public static List parseFormatBits(byte[] formatBits) { + if (formatBits.length == 0) + return null; + List toDecode = new ArrayList<>(); + for (byte formatBit : formatBits) { + int dt = formatBit & 0xFF; + toDecode.add(dt); + if ((dt & 128) == 0) break; + } + return parseFormats(toDecode); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java index fc9142a9b..95314647e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/HuaweiPacket.java @@ -594,6 +594,12 @@ public class HuaweiPacket { return new MusicControl.MusicInfo.Response(paramsProvider).fromPacket(this); case MusicControl.Control.id: return new MusicControl.Control.Response(paramsProvider).fromPacket(this); + case MusicControl.MusicInfoParams.id: + return new MusicControl.MusicInfoParams.Response(paramsProvider).fromPacket(this); + case MusicControl.UploadMusicFileInfo.id: + return new MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest(paramsProvider).fromPacket(this); + case MusicControl.ExtendedMusicInfoParams.id: + return new MusicControl.ExtendedMusicInfoParams.Response(paramsProvider).fromPacket(this); default: this.isEncrypted = this.attemptDecrypt(); // Helps with debugging return this; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/MusicControl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/MusicControl.java index 0c0bf2b06..24ce8fbd8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/MusicControl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/MusicControl.java @@ -16,12 +16,22 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; +import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiMusicUtils.parseFormatBits; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiMusicUtils; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; public class MusicControl { public static final byte id = 0x25; + // TODO: should this be in HuaweiConstants? public static final int successValue = 0x000186A0; @@ -189,4 +199,167 @@ public class MusicControl { } } } + + public static class UploadMusicFileInfo { + public static final int id = 0x09; + + public static class UploadMusicFileInfoRequest extends HuaweiPacket { + public short songIndex; + public String songFileName; + + public UploadMusicFileInfoRequest(ParamsProvider paramsProvider) { + super(paramsProvider); + } + + @Override + public void parseTlv() throws ParseException { + this.songIndex = this.tlv.getShort(0x01); + this.songFileName = this.tlv.getString(0x02); + } + } + + public static class UploadMusicFileInfoResponse extends HuaweiPacket { + public UploadMusicFileInfoResponse(ParamsProvider paramsProvider, short songIndex, String songName, String songArtist) { + super(paramsProvider); + + this.serviceId = MusicControl.id; + this.commandId = id; + + this.tlv = new HuaweiTLV().put(0x01, songIndex).put(0x03, songName).put(0x04, songArtist); + + this.complete = true; + } + } + } + + public static class MusicInfoParams { + public static final byte id = 0x04; + + public static class Request extends HuaweiPacket { + + public Request(ParamsProvider paramsProvider) { + super(paramsProvider); + this.serviceId = MusicControl.id; + this.commandId = id; + + this.tlv = new HuaweiTLV() + .put(0x01) + .put(0x02) + .put(0x03); + + this.complete = true; + } + } + + public static class Response extends HuaweiPacket { + public HuaweiMusicUtils.MusicCapabilities params = new HuaweiMusicUtils.MusicCapabilities(); + + public Response(ParamsProvider paramsProvider) { + super(paramsProvider); + } + + @Override + public void parseTlv() throws ParseException { + + //TODO: unknown TLV +// if (this.tlv.contains(0x01)) +// LOG.info("Unknown: " + this.tlv.getShort(0x01)); + + if (this.tlv.contains(0x02)) + params.availableSpace = this.tlv.getShort(0x02); + if (this.tlv.contains(0x03)) { + byte[] formatBits = this.tlv.getBytes(0x03); + params.supportedFormats = parseFormatBits(formatBits); + } + if (this.tlv.contains(0x04)) + params.maxMusicCount = this.tlv.getShort(0x04); + if (this.tlv.contains(0x05)) + params.currentMusicCount = this.tlv.getByte(0x05); + + if (this.tlv.contains(0x86)) { + params.pageStruct = new ArrayList<>(); + List subTlvs = this.tlv.getObject(0x86).getObjects(0x87); + for (HuaweiTLV subTlv : subTlvs) { + HuaweiMusicUtils.PageStruct pageStruct = new HuaweiMusicUtils.PageStruct(); + if (subTlv.contains(0x08)) + pageStruct.startIndex = subTlv.getShort(0x08); + if (subTlv.contains(0x09)) + pageStruct.endIndex = subTlv.getShort(0x09); + if (subTlv.contains(0x0a)) + pageStruct.count = subTlv.getShort(0x0a); + if (subTlv.contains(0x0b)) + pageStruct.hashCode = subTlv.getBytes(0x0b); + params.pageStruct.add(pageStruct); + } + } + } + } + } + + public static class ExtendedMusicInfoParams { + public static final byte id = 0x0d; + + public static class Request extends HuaweiPacket { + + public Request(ParamsProvider paramsProvider) { + super(paramsProvider); + this.serviceId = MusicControl.id; + this.commandId = id; + + this.tlv = new HuaweiTLV() + .put(0x01) + .put(0x02) + .put(0x03) + .put(0x04) + .put(0x05); + + this.complete = true; + } + } + + public static class Response extends HuaweiPacket { + public HuaweiMusicUtils.MusicCapabilities params = new HuaweiMusicUtils.MusicCapabilities(); + + public Response(ParamsProvider paramsProvider) { + super(paramsProvider); + } + + @Override + public void parseTlv() throws ParseException { + if (this.tlv.contains(0x01)) + params.availableSpace = this.tlv.getShort(0x01); + if (this.tlv.contains(0x02)) { + byte[] formatBits = this.tlv.getBytes(0x02); + params.supportedFormats = parseFormatBits(formatBits); + } + if (this.tlv.contains(0x03)) + params.maxMusicCount = this.tlv.getShort(0x03); + if (this.tlv.contains(0x04)) + params.maxPlaylistCount = this.tlv.getShort(0x04); + if (this.tlv.contains(0x05)) + params.unknown = this.tlv.getByte(0x05); + + if (this.tlv.contains(0x86)) { + params.formatsRestrictions = new ArrayList<>(); + List subTlvs = this.tlv.getObject(0x86).getObjects(0x87); + for (HuaweiTLV subTlv : subTlvs) { + HuaweiMusicUtils.FormatRestrictions restriction = new HuaweiMusicUtils.FormatRestrictions(); + if (subTlv.contains(0x08)) + restriction.formatIdx = subTlv.getByte(0x08); + if (subTlv.contains(0x09)) + restriction.sampleRate = subTlv.getInteger(0x09); + if (subTlv.contains(0x0a)) + restriction.musicEncode = subTlv.getByte(0x0a); + if (subTlv.contains(0x0b)) + restriction.bitrate = subTlv.getShort(0x0b); + if (subTlv.contains(0x0c)) + restriction.channels = subTlv.getByte(0x0c); + if (subTlv.contains(0x0d)) + restriction.unknownBitrate = subTlv.getShort(0x0b); + params.formatsRestrictions.add(restriction); + } + } + } + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/AsynchronousResponse.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/AsynchronousResponse.java index d71c2b40f..e0f86daa1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/AsynchronousResponse.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/AsynchronousResponse.java @@ -343,6 +343,12 @@ public class AsynchronousResponse { this.support.sendSetMusic(); }, 100); } + } else if (response.commandId == MusicControl.UploadMusicFileInfo.id) { + if (!(response instanceof MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest)) + throw new Request.ResponseTypeMismatchException(response, MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest.class); + + MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest resp = (MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest) response; + support.getHuaweiMusicManager().uploadMusicInfo(resp.songIndex, resp.songFileName); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java index 4263dd0cb..1529f6b24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiFwHelper.java @@ -17,10 +17,17 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei; +import android.content.ContentResolver; import android.content.Context; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.media.MediaExtractor; +import android.media.MediaMetadataRetriever; +import android.media.MediaFormat; import android.net.Uri; +import android.provider.OpenableColumns; +import android.text.TextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,8 +58,11 @@ public class HuaweiFwHelper { Bitmap previewBitmap; HuaweiWatchfaceManager.WatchfaceDescription watchfaceDescription; HuaweiAppManager.AppConfig appConfig; + HuaweiMusicManager.AudioInfo musicInfo; Context mContext; + + public HuaweiFwHelper(final Uri uri, final Context context) { this.uri = uri; @@ -69,7 +79,9 @@ public class HuaweiFwHelper { } private void parseFile() { - if(parseAsApp()) { + if (parseAsMusic()) { + fileType = FileUpload.Filetype.music; + } else if (parseAsApp()) { assert appConfig.bundleName != null; fileType = FileUpload.Filetype.app; } else if (parseAsWatchFace()) { @@ -79,31 +91,136 @@ public class HuaweiFwHelper { } } + private String getNameWithoutExtension(String fileName) { + return fileName.indexOf(".") > 0?fileName.substring(0, fileName.lastIndexOf(".")): fileName; + } + + private String getExtension(String fileName) { + if (TextUtils.isEmpty(fileName)) { + return null; + } + int lastIndexOf = fileName.lastIndexOf("."); + if (lastIndexOf >= 0 && lastIndexOf + 1 < fileName.length()) { + return fileName.substring(lastIndexOf + 1); + } + return null; + } + + private HuaweiMusicManager.AudioInfo getAudioInfo(Uri selectedUri) throws IOException { + ContentResolver contentResolver = mContext.getContentResolver(); + String[] filePathColumn = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}; + + Cursor cursor = contentResolver.query(selectedUri, filePathColumn, null, null, null); + if(cursor == null) + return null; + cursor.moveToFirst(); + + int fileNameIndex = cursor.getColumnIndex(filePathColumn[0]); + String fileName = cursor.getString(fileNameIndex); + int fileSizeIndex = cursor.getColumnIndex(filePathColumn[1]); + long fileSize = cursor.getLong(fileSizeIndex); + cursor.close(); + + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); + mmr.setDataSource(mContext, selectedUri); + + String title = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); + String artist = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST); + + if(TextUtils.isEmpty(title)) { + title = getNameWithoutExtension(fileName); + } + if(TextUtils.isEmpty(artist)) { + artist = "Unknown"; + } + + MediaExtractor mex = new MediaExtractor(); + mex.setDataSource(mContext, selectedUri, null); + + + MediaFormat mf = mex.getTrackFormat(0); + + int bitrate = -1; // TODO: calculate or get bitrate + int sampleRate = mf.getInteger(MediaFormat.KEY_SAMPLE_RATE); + int channels = mf.getInteger(MediaFormat.KEY_CHANNEL_COUNT); + long duration = mf.getLong(MediaFormat.KEY_DURATION); + + LOG.info("bitRate: " + bitrate); + LOG.info("sampleRate: " + sampleRate); + LOG.info("channelCount: " + channels); + LOG.info("duration: " + duration); + + String extension = getExtension(fileName); + if(!TextUtils.isEmpty(extension)) { + extension = extension.toLowerCase(); + } + + HuaweiMusicManager.AudioInfo audioInfo = new HuaweiMusicManager.AudioInfo(fileName, fileSize, title, artist, extension); + audioInfo.setCharacteristics(duration, sampleRate, bitrate, (byte) channels); + + return audioInfo; + } + + private byte[] getFileData(UriHelper uriHelper) throws IOException { + InputStream inputStream = uriHelper.openInputStream(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[1000]; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + inputStream.close(); + return buffer.toByteArray(); + } + + boolean parseAsMusic() { + try { + final UriHelper uriHelper = UriHelper.get(uri, this.mContext); + + String mimeType = mContext.getContentResolver().getType(uri); + LOG.info("File mime type: {}", mimeType); + + if(mimeType == null || !mimeType.startsWith("audio/")) + return false; + + musicInfo = getAudioInfo(uri); + if(musicInfo == null) + return false; + + musicInfo.setMimeType(mimeType); + + byte[] musicData = getFileData(uriHelper); + + fileName = musicInfo.getFileName(); + fw = musicData; + fileSize = fw.length; + + return true; + } catch (FileNotFoundException e) { + LOG.error("Music: File was not found {}", e.getMessage()); + } catch (IOException e) { + LOG.error("Music: General IO error occurred {}", e.getMessage()); + } catch (Exception e) { + LOG.error("Music: Unknown error occurred", e); + } + return false; + } + + boolean parseAsApp() { try { final UriHelper uriHelper = UriHelper.get(uri, this.mContext); - InputStream inputStream = uriHelper.openInputStream(); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - int nRead; - byte[] data = new byte[4]; - - while ((nRead = inputStream.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); - } - - buffer.flush(); - byte[] appData = buffer.toByteArray(); - - inputStream.close(); + byte[] appData = getFileData(uriHelper); HuaweiBinAppParser app = new HuaweiBinAppParser(); app.parseData(appData); byte[] config = app.getEntryContent("config.json"); - if(config == null) + if (config == null) return false; appConfig = new HuaweiAppManager.AppConfig(new String(config)); fileName = app.getPackageName() + "_INSTALL"; //TODO: INSTALL or UPDATE suffix @@ -112,22 +229,21 @@ public class HuaweiFwHelper { fileSize = fw.length; byte[] icon = app.getEntryContent("icon_small.png"); - if(icon != null) { + if (icon != null) { previewBitmap = BitmapFactory.decodeByteArray(icon, 0, icon.length); } return true; } catch (FileNotFoundException e) { - LOG.error("The app file was not found.", e); + LOG.error("App: File was not found{}", e.getMessage()); } catch (IOException e) { - LOG.error("General IO error occurred.", e); + LOG.error("App: General IO error occurred {}", e.getMessage()); } catch (HuaweiBinAppParser.HuaweiBinAppParseError e) { - LOG.error("Error parsing app File", e); + LOG.error("App: Error parsing app File {}", e.getMessage()); } catch (Exception e) { - LOG.error("Unknown error occurred.", e); + LOG.error("App: Unknown error occurred", e); } - return false; } @@ -172,7 +288,7 @@ public class HuaweiFwHelper { byte[] watchfaceZip = watchfacePackage.getFileFromZip("com.huawei.watchface"); try { - GBZipFile watchfaceBinZip = new GBZipFile(watchfaceZip); + GBZipFile watchfaceBinZip = new GBZipFile(watchfaceZip); fw = watchfaceBinZip.getFileFromZip("watchface.bin"); } catch (ZipFileException e) { LOG.error("Unable to get watchfaceZip, it seems older already watchface.bin"); @@ -182,13 +298,13 @@ public class HuaweiFwHelper { isWatchface = true; } catch (ZipFileException e) { - LOG.error("Unable to read watchface file.", e); + LOG.error("Watchface: Unable to read file {}", e.getMessage()); } catch (FileNotFoundException e) { - LOG.error("The watchface file was not found.", e); + LOG.error("Watchface: File was not found {}", e.getMessage()); } catch (IOException e) { - LOG.error("General IO error occurred.", e); + LOG.error("Watchface: General IO error occurred {}", e.getMessage()); } catch (Exception e) { - LOG.error("Unknown error occurred.", e); + LOG.error("Watchface: Unknown error occurred", e); } return isWatchface; @@ -202,8 +318,12 @@ public class HuaweiFwHelper { return fileType == FileUpload.Filetype.app; } + public boolean isMusic() { + return fileType == FileUpload.Filetype.music; + } + public boolean isValid() { - return isWatchface() || isAPP(); + return isWatchface() || isAPP() || isMusic(); } public Bitmap getPreviewBitmap() { @@ -218,6 +338,10 @@ public class HuaweiFwHelper { return appConfig; } + public HuaweiMusicManager.AudioInfo getMusicInfo() { + return musicInfo; + } + public byte getFileType() { return fileType; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiMusicManager.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiMusicManager.java new file mode 100644 index 000000000..69e670145 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiMusicManager.java @@ -0,0 +1,128 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendUploadMusicFileInfoResponse; + +public class HuaweiMusicManager { + static Logger LOG = LoggerFactory.getLogger(HuaweiMusicManager.class); + + public static class AudioInfo { + private final String fileName; + private final long fileSize; + private final String title; + private final String artist; + private final String extension; + + private String mimeType; + + private long duration; + private int sampleRate; + private int bitrate; + private byte channels; + + //public byte musicEncode = -1; // TODO: not sure + //public short unknownBitrate = -1; // TODO: not sure + + public AudioInfo(String fileName, long fileSize, String title, String artist, String extension) { + this.fileName = fileName; + this.fileSize = fileSize; + this.title = title; + this.artist = artist; + this.extension = extension; + } + + public String getFileName() { + return fileName; + } + + public long getFileSize() { return fileSize;} + + public String getTitle() { + return title; + } + + public String getArtist() { + return artist; + } + + public String getExtension() { + return extension; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public void setCharacteristics(long duration, int sampleRate, int bitrate, byte channels) { + this.duration = duration; + this.sampleRate = sampleRate; + this.bitrate = bitrate; + this.channels = channels; + } + + public long getDuration() { + return duration; + } + + public int getSampleRate() { + return sampleRate; + } + + public int getBitrate() { + return bitrate; + } + + public byte getChannels() { + return channels; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("AudioInfo{"); + sb.append("fileName='").append(fileName).append('\''); + sb.append("fileSize='").append(fileSize).append('\''); + sb.append(", title='").append(title).append('\''); + sb.append(", artist='").append(artist).append('\''); + sb.append(", mimeType='").append(mimeType).append('\''); + sb.append('}'); + return sb.toString(); + } + } + + private final HuaweiSupportProvider support; + + private AudioInfo currentMusicInfo; + + + public HuaweiMusicManager(HuaweiSupportProvider support) { + this.support = support; + } + + + public void addUploadMusic(AudioInfo audioInfo) { + currentMusicInfo = audioInfo; + } + + public void uploadMusicInfo(short songIndex, String fileName) { + AudioInfo current = currentMusicInfo; + if(current == null || (!current.getFileName().equals(fileName))) { + LOG.error("Upload file info does not exist."); + return; + } + try { + SendUploadMusicFileInfoResponse sendUploadMusicFileInfoResponse = new SendUploadMusicFileInfoResponse(support, + songIndex, current.getTitle(), current.getArtist()); + sendUploadMusicFileInfoResponse.doPerform(); + } catch (IOException e) { + LOG.error("Could not send sendUploadMusicFileInfoResponse", e); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java index a881e7061..0f8d94c6e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiSupportProvider.java @@ -105,6 +105,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetA import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetContactsCount; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetEventAlarmList; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetGpsParameterRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetExtendedMusicInfoParams; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetMusicInfoParams; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetNotificationConstraintsRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetSmartAlarmList; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetWatchfaceParams; @@ -268,6 +270,8 @@ public class HuaweiSupportProvider { protected HuaweiEphemerisManager huaweiEphemerisManager = new HuaweiEphemerisManager(this); + protected HuaweiMusicManager huaweiMusicManager = new HuaweiMusicManager(this); + //TODO: we need only one instance of manager and all it services. protected HuaweiP2PManager huaweiP2PManager = new HuaweiP2PManager(this); @@ -299,6 +303,10 @@ public class HuaweiSupportProvider { return huaweiEphemerisManager; } + public HuaweiMusicManager getHuaweiMusicManager() { + return huaweiMusicManager; + } + public HuaweiSupportProvider(HuaweiBRSupport support) { this.brSupport = support; } @@ -824,6 +832,8 @@ public class HuaweiSupportProvider { initRequestQueue.add(new GetWatchfaceParams(this)); initRequestQueue.add(new SendCameraRemoteSetupEvent(this, CameraRemote.CameraRemoteSetup.Request.Event.ENABLE_CAMERA)); initRequestQueue.add(new GetAppInfoParams(this)); + initRequestQueue.add(new GetMusicInfoParams(this)); + initRequestQueue.add(new GetExtendedMusicInfoParams(this)); initRequestQueue.add(new SetActivateOnLiftRequest(this)); initRequestQueue.add(new SetWearLocationRequest(this)); initRequestQueue.add(new SetNavigateOnRotateRequest(this)); @@ -1959,6 +1969,10 @@ public class HuaweiSupportProvider { HuaweiUploadManager.FileUploadInfo fileInfo = new HuaweiUploadManager.FileUploadInfo(); + if(huaweiFwHelper.isMusic()) { + getHuaweiMusicManager().addUploadMusic(huaweiFwHelper.getMusicInfo()); + } + fileInfo.setFileType(huaweiFwHelper.getFileType()); if (huaweiFwHelper.isWatchface()) { fileInfo.setFileName(huaweiWatchfaceManager.getRandomName()); @@ -1989,10 +2003,14 @@ public class HuaweiSupportProvider { if (code == 140004) { LOG.error("Too many watchfaces installed"); HuaweiSupportProvider.this.handleGBDeviceEvent(new GBDeviceEventDisplayMessage(HuaweiSupportProvider.this.getContext().getString(R.string.cannot_upload_watchface_too_many_watchfaces_installed), Toast.LENGTH_LONG, GB.ERROR)); + } else if (code == 140008) { + LOG.error("File already exists"); + HuaweiSupportProvider.this.handleGBDeviceEvent(new GBDeviceEventDisplayMessage("File already exists", Toast.LENGTH_LONG, GB.ERROR)); //TODO: localization } else if (code == 140009) { LOG.error("Insufficient space for upload"); HuaweiSupportProvider.this.handleGBDeviceEvent(new GBDeviceEventDisplayMessage(HuaweiSupportProvider.this.getContext().getString(R.string.insufficient_space_for_upload), Toast.LENGTH_LONG, GB.ERROR)); } + } }); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetExtendedMusicInfoParams.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetExtendedMusicInfoParams.java new file mode 100644 index 000000000..44f8ddae1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetExtendedMusicInfoParams.java @@ -0,0 +1,44 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; + +public class GetExtendedMusicInfoParams extends Request { + private final Logger LOG = LoggerFactory.getLogger(GetExtendedMusicInfoParams.class); + public GetExtendedMusicInfoParams(HuaweiSupportProvider support) { + super(support); + this.serviceId = MusicControl.id; + this.commandId = MusicControl.ExtendedMusicInfoParams.id; + + } + + @Override + protected boolean requestSupported() { + return supportProvider.getHuaweiCoordinator().supportsMusicUploading(); + } + + @Override + protected List createRequest() throws Request.RequestCreationException { + try { + return new MusicControl.ExtendedMusicInfoParams.Request(paramsProvider).serialize(); + } catch (HuaweiPacket.CryptoException e) { + throw new Request.RequestCreationException(e); + } + } + + @Override + protected void processResponse() throws Request.ResponseParseException { + LOG.info("MusicControl.ExtendedMusicInfoParams processResponse"); + if (!(receivedPacket instanceof MusicControl.ExtendedMusicInfoParams.Response)) + throw new Request.ResponseTypeMismatchException(receivedPacket, MusicControl.ExtendedMusicInfoParams.Response.class); + + MusicControl.ExtendedMusicInfoParams.Response resp = (MusicControl.ExtendedMusicInfoParams.Response)(receivedPacket); + supportProvider.getHuaweiCoordinator().setExtendedMusicInfoParams(resp.params); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetMusicInfoParams.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetMusicInfoParams.java new file mode 100644 index 000000000..64292e3d5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/GetMusicInfoParams.java @@ -0,0 +1,44 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; + +public class GetMusicInfoParams extends Request { + private final Logger LOG = LoggerFactory.getLogger(GetMusicInfoParams.class); + public GetMusicInfoParams(HuaweiSupportProvider support) { + super(support); + this.serviceId = MusicControl.id; + this.commandId = MusicControl.MusicInfoParams.id; + + } + + @Override + protected boolean requestSupported() { + return supportProvider.getHuaweiCoordinator().supportsMusicUploading(); + } + + @Override + protected List createRequest() throws Request.RequestCreationException { + try { + return new MusicControl.MusicInfoParams.Request(paramsProvider).serialize(); + } catch (HuaweiPacket.CryptoException e) { + throw new Request.RequestCreationException(e); + } + } + + @Override + protected void processResponse() throws Request.ResponseParseException { + LOG.info("MusicControl.MusicInfoParams processResponse"); + if (!(receivedPacket instanceof MusicControl.MusicInfoParams.Response)) + throw new Request.ResponseTypeMismatchException(receivedPacket, MusicControl.MusicInfoParams.Response.class); + + MusicControl.MusicInfoParams.Response resp = (MusicControl.MusicInfoParams.Response)(receivedPacket); + supportProvider.getHuaweiCoordinator().setMusicInfoParams(resp.params); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendUploadMusicFileInfoResponse.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendUploadMusicFileInfoResponse.java new file mode 100644 index 000000000..a6853d9dc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendUploadMusicFileInfoResponse.java @@ -0,0 +1,33 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider; + +public class SendUploadMusicFileInfoResponse extends Request { + short songIndex; + String songName; + String songArtist; + + + public SendUploadMusicFileInfoResponse(HuaweiSupportProvider support, short songIndex, String songName, String songArtist) { + super(support); + this.serviceId = MusicControl.id; + this.commandId = MusicControl.UploadMusicFileInfo.id; + this.songIndex = songIndex; + this.songName = songName; + this.songArtist = songArtist; + this.addToResponse = false; + } + + @Override + protected List createRequest() throws Request.RequestCreationException { + try { + return new MusicControl.UploadMusicFileInfo.UploadMusicFileInfoResponse(this.paramsProvider, this.songIndex, this.songName, this.songArtist).serialize(); + } catch (HuaweiPacket.CryptoException e) { + throw new Request.RequestCreationException(e); + } + } +} \ No newline at end of file