mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-28 04:46:51 +01:00
Huawei: Initial P2P service support, Calendar sync support.
This commit is contained in:
parent
ae3615a388
commit
f3aaeb5216
@ -173,6 +173,11 @@ public abstract class HuaweiBRCoordinator extends AbstractBLClassicDeviceCoordin
|
|||||||
return huaweiCoordinator.getContactsSlotCount(device);
|
return huaweiCoordinator.getContactsSlotCount(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCalendarEvents() {
|
||||||
|
return huaweiCoordinator.supportsCalendarEvents();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsActivityDataFetching() {
|
public boolean supportsActivityDataFetching() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -88,12 +88,10 @@ public class HuaweiCoordinator {
|
|||||||
)));
|
)));
|
||||||
if (key.equals("maxContactsCount"))
|
if (key.equals("maxContactsCount"))
|
||||||
this.maxContactsCount = getCapabilitiesSharedPreferences().getInt(key, 0);
|
this.maxContactsCount = getCapabilitiesSharedPreferences().getInt(key, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private SharedPreferences getCapabilitiesSharedPreferences() {
|
private SharedPreferences getCapabilitiesSharedPreferences() {
|
||||||
return GBApplication.getContext().getSharedPreferences("huawei_coordinator_capatilities" + parent.getDeviceType().name(), Context.MODE_PRIVATE);
|
return GBApplication.getContext().getSharedPreferences("huawei_coordinator_capatilities" + parent.getDeviceType().name(), Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
@ -257,6 +255,11 @@ public class HuaweiCoordinator {
|
|||||||
dateTime.add(R.xml.devicesettings_timeformat);
|
dateTime.add(R.xml.devicesettings_timeformat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Calendar
|
||||||
|
if( supportsP2PService() && supportsCalendar()) {
|
||||||
|
deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CALENDAR, R.xml.devicesettings_sync_calendar);
|
||||||
|
}
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
if (supportsWearLocation(device))
|
if (supportsWearLocation(device))
|
||||||
deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DISPLAY, R.xml.devicesettings_wearlocation);
|
deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DISPLAY, R.xml.devicesettings_wearlocation);
|
||||||
@ -324,6 +327,10 @@ public class HuaweiCoordinator {
|
|||||||
return supportsCommandForService(0x03, 0x1);
|
return supportsCommandForService(0x03, 0x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean supportsCalendarEvents() {
|
||||||
|
return supportsP2PService() && supportsCalendar();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean supportsAcceptAgreement() {
|
public boolean supportsAcceptAgreement() {
|
||||||
return supportsCommandForService(0x01, 0x30);
|
return supportsCommandForService(0x01, 0x30);
|
||||||
}
|
}
|
||||||
@ -520,6 +527,22 @@ public class HuaweiCoordinator {
|
|||||||
return supportsCommandForService(0x32, 0x01);
|
return supportsCommandForService(0x32, 0x01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean supportsP2PService() {
|
||||||
|
return supportsCommandForService(0x34, 0x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsExternalCalendarService() {
|
||||||
|
if (supportsExpandCapability())
|
||||||
|
return supportsExpandCapability(184);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsCalendar() {
|
||||||
|
if (supportsExpandCapability())
|
||||||
|
return supportsExpandCapability(171) || supportsExpandCapability(184);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean supportsMultiDevice() {
|
public boolean supportsMultiDevice() {
|
||||||
if (supportsExpandCapability())
|
if (supportsExpandCapability())
|
||||||
return supportsExpandCapability(109);
|
return supportsExpandCapability(109);
|
||||||
|
@ -182,6 +182,11 @@ public abstract class HuaweiLECoordinator extends AbstractBLEDeviceCoordinator i
|
|||||||
return huaweiCoordinator.getContactsSlotCount(device);
|
return huaweiCoordinator.getContactsSlotCount(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCalendarEvents() {
|
||||||
|
return huaweiCoordinator.supportsCalendarEvents();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsActivityDataFetching() {
|
public boolean supportsActivityDataFetching() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -38,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Contacts;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService0A;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService0A;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService2C;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService2C;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
|
||||||
@ -231,6 +232,9 @@ public class HuaweiPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class SerializeException extends Exception {
|
public static class SerializeException extends Exception {
|
||||||
|
public SerializeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
public SerializeException(String message, Exception e) {
|
public SerializeException(String message, Exception e) {
|
||||||
super(message, e);
|
super(message, e);
|
||||||
}
|
}
|
||||||
@ -652,6 +656,14 @@ public class HuaweiPacket {
|
|||||||
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
case P2P.id:
|
||||||
|
switch (this.commandId) {
|
||||||
|
case P2P.P2PCommand.id:
|
||||||
|
return new P2P.P2PCommand.Response(paramsProvider).fromPacket(this);
|
||||||
|
default:
|
||||||
|
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
||||||
|
return this;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
|
||||||
return this;
|
return this;
|
||||||
@ -777,14 +789,14 @@ public class HuaweiPacket {
|
|||||||
return retv;
|
return retv;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<byte[]> serializeFileChunk(byte[] fileChunk, int uploadPosition, short unitSize, byte fileId) {
|
public List<byte[]> serializeFileChunk(byte[] fileChunk, int uploadPosition, short unitSize, byte fileId, boolean isEncrypted) throws SerializeException {
|
||||||
List<byte[]> retv = new ArrayList<>();
|
List<byte[]> retv = new ArrayList<>();
|
||||||
int headerLength = 5; // Magic + (short)(bodyLength + 1) + 0x00
|
int headerLength = 5; // Magic + (short)(bodyLength + 1) + 0x00
|
||||||
int sliceHeaderLenght =7;
|
int sliceHeaderLength = 6;
|
||||||
|
|
||||||
int footerLength = 2; //CRC16
|
int footerLength = 2; //CRC16
|
||||||
|
|
||||||
int packetCount = (int) Math.ceil(((double) fileChunk.length ) / (double) unitSize);
|
int packetCount = (int) Math.ceil(((double) fileChunk.length) / (double) unitSize);
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(fileChunk);
|
ByteBuffer buffer = ByteBuffer.wrap(fileChunk);
|
||||||
|
|
||||||
@ -793,7 +805,34 @@ public class HuaweiPacket {
|
|||||||
for (int i = 0; i < packetCount; i++) {
|
for (int i = 0; i < packetCount; i++) {
|
||||||
|
|
||||||
short contentSize = (short) Math.min(unitSize, buffer.remaining());
|
short contentSize = (short) Math.min(unitSize, buffer.remaining());
|
||||||
short packetSize = (short)(contentSize + headerLength + sliceHeaderLenght + footerLength);
|
|
||||||
|
ByteBuffer payload = ByteBuffer.allocate(contentSize + sliceHeaderLength);
|
||||||
|
payload.put(fileId); // Slice
|
||||||
|
payload.put((byte)i); // Flag
|
||||||
|
payload.putInt(sliceStart);
|
||||||
|
|
||||||
|
byte[] packetContent = new byte[contentSize];
|
||||||
|
buffer.get(packetContent);
|
||||||
|
payload.put(packetContent); // Packet databyte[] packetContent = new byte[contentSize];
|
||||||
|
|
||||||
|
|
||||||
|
byte[] new_payload = null;
|
||||||
|
if(isEncrypted) {
|
||||||
|
try {
|
||||||
|
HuaweiTLV encryptedTlv = HuaweiTLV.encryptRaw(this.paramsProvider, payload.array());
|
||||||
|
new_payload = encryptedTlv.serialize();
|
||||||
|
} catch (HuaweiCrypto.CryptoException e) {
|
||||||
|
throw new HuaweiPacket.SerializeException("Error to encrypt TLV");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_payload = payload.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_payload == null) {
|
||||||
|
throw new HuaweiPacket.SerializeException("new payload is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
short packetSize = (short)(new_payload.length + sliceHeaderLength + footerLength);
|
||||||
ByteBuffer packet = ByteBuffer.allocate(packetSize);
|
ByteBuffer packet = ByteBuffer.allocate(packetSize);
|
||||||
|
|
||||||
int start = packet.position();
|
int start = packet.position();
|
||||||
@ -804,18 +843,11 @@ public class HuaweiPacket {
|
|||||||
packet.put(this.serviceId);
|
packet.put(this.serviceId);
|
||||||
packet.put(this.commandId);
|
packet.put(this.commandId);
|
||||||
|
|
||||||
packet.put(fileId); // Slice
|
packet.put(new_payload);
|
||||||
packet.put((byte)i); // Flag
|
|
||||||
packet.putInt(sliceStart);
|
|
||||||
|
|
||||||
byte[] packetContent = new byte[contentSize];
|
|
||||||
buffer.get(packetContent);
|
|
||||||
packet.put(packetContent); // Packet databyte[] packetContent = new byte[contentSize];
|
|
||||||
|
|
||||||
int length = packet.position() - start;
|
int length = packet.position() - start;
|
||||||
if (length != packetSize - footerLength) {
|
if (length != packetSize - footerLength) {
|
||||||
// TODO: exception?
|
throw new HuaweiPacket.SerializeException(String.format(GBApplication.getLanguage(), "Packet lengths don't match! %d != %d", length, packetSize + headerLength));
|
||||||
LOG.error(String.format(GBApplication.getLanguage(), "Packet lengths don't match! %d != %d", length, packetSize + headerLength));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] complete = new byte[length];
|
byte[] complete = new byte[length];
|
||||||
|
@ -300,13 +300,12 @@ public class HuaweiTLV {
|
|||||||
return msg.substring(0, msg.length() - 3);
|
return msg.substring(0, msg.length() - 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HuaweiTLV encrypt(ParamsProvider paramsProvider) throws CryptoException {
|
public static HuaweiTLV encryptRaw(ParamsProvider paramsProvider, byte[] data) throws CryptoException {
|
||||||
byte[] serializedTLV = serialize();
|
|
||||||
byte[] key = paramsProvider.getSecretKey();
|
byte[] key = paramsProvider.getSecretKey();
|
||||||
byte[] nonce = paramsProvider.getIv();
|
byte[] nonce = paramsProvider.getIv();
|
||||||
byte[] encryptedTLV = HuaweiCrypto.encrypt(
|
byte[] encryptedTLV = HuaweiCrypto.encrypt(
|
||||||
paramsProvider.getEncryptMethod() == 0x01 || paramsProvider.getDeviceSupportType() == 0x04,
|
paramsProvider.getEncryptMethod() == 0x01 || paramsProvider.getDeviceSupportType() == 0x04,
|
||||||
serializedTLV,
|
data,
|
||||||
key,
|
key,
|
||||||
nonce);
|
nonce);
|
||||||
return new HuaweiTLV()
|
return new HuaweiTLV()
|
||||||
@ -315,6 +314,11 @@ public class HuaweiTLV {
|
|||||||
.put(CryptoTags.cipherText, encryptedTLV);
|
.put(CryptoTags.cipherText, encryptedTLV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HuaweiTLV encrypt(ParamsProvider paramsProvider) throws CryptoException {
|
||||||
|
byte[] serializedTLV = serialize();
|
||||||
|
return encryptRaw(paramsProvider, serializedTLV);
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] decryptRaw(ParamsProvider paramsProvider) throws CryptoException, HuaweiPacket.MissingTagException {
|
public byte[] decryptRaw(ParamsProvider paramsProvider) throws CryptoException, HuaweiPacket.MissingTagException {
|
||||||
byte[] key = paramsProvider.getSecretKey();
|
byte[] key = paramsProvider.getSecretKey();
|
||||||
return HuaweiCrypto.decrypt(
|
return HuaweiCrypto.decrypt(
|
||||||
|
@ -30,8 +30,8 @@ public class FileUpload {
|
|||||||
public byte bitmap_enable = 0;
|
public byte bitmap_enable = 0;
|
||||||
public short unit_size = 0;
|
public short unit_size = 0;
|
||||||
public int max_apply_data_size = 0;
|
public int max_apply_data_size = 0;
|
||||||
public short interval =0;
|
public short interval = 0;
|
||||||
public int received_file_size =0;
|
public int received_file_size = 0;
|
||||||
public byte no_encrypt = 0;
|
public byte no_encrypt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,12 +46,18 @@ public class FileUpload {
|
|||||||
|
|
||||||
public static class FileInfoSend {
|
public static class FileInfoSend {
|
||||||
public static final byte id = 0x02;
|
public static final byte id = 0x02;
|
||||||
|
|
||||||
public static class Request extends HuaweiPacket {
|
public static class Request extends HuaweiPacket {
|
||||||
|
|
||||||
public Request(ParamsProvider paramsProvider,
|
public Request(ParamsProvider paramsProvider,
|
||||||
int fileSize,
|
int fileSize,
|
||||||
String fileName,
|
String fileName,
|
||||||
byte fileType) {
|
byte fileType,
|
||||||
|
String srcPackage,
|
||||||
|
String dstPackage,
|
||||||
|
String srcFingerprint,
|
||||||
|
String dstFingerprint
|
||||||
|
) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
this.serviceId = FileUpload.id;
|
this.serviceId = FileUpload.id;
|
||||||
this.commandId = id;
|
this.commandId = id;
|
||||||
@ -68,15 +74,26 @@ public class FileUpload {
|
|||||||
.put(0x06, watchfaceVersion);
|
.put(0x06, watchfaceVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (srcPackage != null && dstPackage != null) {
|
||||||
|
this.tlv.put(0x08, srcPackage)
|
||||||
|
.put(0x09, dstPackage)
|
||||||
|
.put(0x0a)
|
||||||
|
.put(0x0b, srcFingerprint)
|
||||||
|
.put(0x0c, dstFingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.complete = true;
|
this.complete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Response extends HuaweiPacket {
|
public static class Response extends HuaweiPacket {
|
||||||
public int result = 0;
|
public int result = 0;
|
||||||
public Response (ParamsProvider paramsProvider) {
|
|
||||||
|
public Response(ParamsProvider paramsProvider) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||||
this.result = this.tlv.getInteger(0x7f);
|
this.result = this.tlv.getInteger(0x7f);
|
||||||
@ -90,8 +107,8 @@ public class FileUpload {
|
|||||||
public static class Request extends HuaweiPacket {
|
public static class Request extends HuaweiPacket {
|
||||||
|
|
||||||
public Request(ParamsProvider paramsProvider,
|
public Request(ParamsProvider paramsProvider,
|
||||||
byte[] hash,
|
byte[] hash,
|
||||||
byte fileId) {
|
byte fileId) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
this.serviceId = FileUpload.id;
|
this.serviceId = FileUpload.id;
|
||||||
this.commandId = id;
|
this.commandId = id;
|
||||||
@ -104,8 +121,9 @@ public class FileUpload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Response extends HuaweiPacket {
|
public static class Response extends HuaweiPacket {
|
||||||
public byte fileId =0;
|
public byte fileId = 0;
|
||||||
public Response (ParamsProvider paramsProvider) {
|
|
||||||
|
public Response(ParamsProvider paramsProvider) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +137,7 @@ public class FileUpload {
|
|||||||
|
|
||||||
public static class FileUploadConsultAck {
|
public static class FileUploadConsultAck {
|
||||||
public static final byte id = 0x04;
|
public static final byte id = 0x04;
|
||||||
|
|
||||||
public static class Request extends HuaweiPacket {
|
public static class Request extends HuaweiPacket {
|
||||||
public Request(ParamsProvider paramsProvider, byte noEncryption, byte fileId) {
|
public Request(ParamsProvider paramsProvider, byte noEncryption, byte fileId) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
@ -128,7 +147,7 @@ public class FileUpload {
|
|||||||
.put(0x7f, 0x000186A0) //ok
|
.put(0x7f, 0x000186A0) //ok
|
||||||
.put(0x01, fileId);
|
.put(0x01, fileId);
|
||||||
if (noEncryption == 1)
|
if (noEncryption == 1)
|
||||||
this.tlv.put(0x09, (byte)0x01); // need on devices which generally encrypted, but files
|
this.tlv.put(0x09, (byte) 0x01); // need on devices which generally encrypted, but files
|
||||||
this.complete = true;
|
this.complete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,22 +155,23 @@ public class FileUpload {
|
|||||||
public static class Response extends HuaweiPacket {
|
public static class Response extends HuaweiPacket {
|
||||||
|
|
||||||
public FileUploadParams fileUploadParams = new FileUploadParams();
|
public FileUploadParams fileUploadParams = new FileUploadParams();
|
||||||
public Response (ParamsProvider paramsProvider) {
|
|
||||||
|
public Response(ParamsProvider paramsProvider) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||||
this.fileUploadParams.file_id = this.tlv.getByte(0x01);
|
this.fileUploadParams.file_id = this.tlv.getByte(0x01);
|
||||||
this.fileUploadParams.protocolVersion = this.tlv.getString(0x02);
|
this.fileUploadParams.protocolVersion = this.tlv.getString(0x02);
|
||||||
this.fileUploadParams.app_wait_time = this.tlv.getShort(0x03);
|
this.fileUploadParams.app_wait_time = this.tlv.getShort(0x03);
|
||||||
this.fileUploadParams.bitmap_enable = this.tlv.getByte(0x04);
|
this.fileUploadParams.bitmap_enable = this.tlv.getByte(0x04);
|
||||||
this.fileUploadParams.unit_size = this.tlv.getShort(0x05);
|
this.fileUploadParams.unit_size = this.tlv.getShort(0x05);
|
||||||
this.fileUploadParams.max_apply_data_size = this.tlv.getInteger(0x06);
|
this.fileUploadParams.max_apply_data_size = this.tlv.getInteger(0x06);
|
||||||
this.fileUploadParams.interval = this.tlv.getShort(0x07);
|
this.fileUploadParams.interval = this.tlv.getShort(0x07);
|
||||||
this.fileUploadParams.received_file_size = this.tlv.getInteger(0x08);
|
this.fileUploadParams.received_file_size = this.tlv.getInteger(0x08);
|
||||||
if (this.tlv.contains(0x09)) // optional for older devices
|
if (this.tlv.contains(0x09)) // optional for older devices
|
||||||
this.fileUploadParams.no_encrypt = this.tlv.getByte(0x09);
|
this.fileUploadParams.no_encrypt = this.tlv.getByte(0x09);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,12 +181,14 @@ public class FileUpload {
|
|||||||
|
|
||||||
public int bytesUploaded = 0;
|
public int bytesUploaded = 0;
|
||||||
public int nextchunkSize = 0;
|
public int nextchunkSize = 0;
|
||||||
|
|
||||||
public FileNextChunkParams(ParamsProvider paramsProvider) {
|
public FileNextChunkParams(ParamsProvider paramsProvider) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
this.serviceId = FileUpload.id;
|
this.serviceId = FileUpload.id;
|
||||||
this.commandId = id;
|
this.commandId = id;
|
||||||
this.complete = true;
|
this.complete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||||
this.bytesUploaded = this.tlv.getInteger(0x02);
|
this.bytesUploaded = this.tlv.getInteger(0x02);
|
||||||
@ -203,7 +225,8 @@ public class FileUpload {
|
|||||||
|
|
||||||
public static class Response extends HuaweiPacket {
|
public static class Response extends HuaweiPacket {
|
||||||
byte status = 0;
|
byte status = 0;
|
||||||
public Response (ParamsProvider paramsProvider) {
|
|
||||||
|
public Response(ParamsProvider paramsProvider) {
|
||||||
super(paramsProvider);
|
super(paramsProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
|
||||||
|
|
||||||
|
public class P2P {
|
||||||
|
public static final byte id = 0x34;
|
||||||
|
|
||||||
|
public static class P2PCommand {
|
||||||
|
public static final byte id = 0x01;
|
||||||
|
|
||||||
|
public static class Request extends HuaweiPacket {
|
||||||
|
|
||||||
|
|
||||||
|
public Request(ParamsProvider paramsProvider,
|
||||||
|
byte cmdId,
|
||||||
|
short sequenceId,
|
||||||
|
String srcPackage,
|
||||||
|
String dstPackage,
|
||||||
|
String srcFingerprint,
|
||||||
|
String dstFingerprint,
|
||||||
|
byte[] sendData,
|
||||||
|
int sendCode) {
|
||||||
|
super(paramsProvider);
|
||||||
|
this.serviceId = P2P.id;
|
||||||
|
this.commandId = id;
|
||||||
|
|
||||||
|
this.tlv = new HuaweiTLV()
|
||||||
|
.put(0x01, cmdId)
|
||||||
|
.put(0x02, sequenceId)
|
||||||
|
.put(0x03, srcPackage)
|
||||||
|
.put(0x04, dstPackage);
|
||||||
|
if(cmdId == 0x2) {
|
||||||
|
this.tlv.put(0x05, srcFingerprint);
|
||||||
|
this.tlv.put(0x06, dstFingerprint);
|
||||||
|
}
|
||||||
|
if(sendData != null && sendData.length > 0)
|
||||||
|
this.tlv.put(0x07, sendData);
|
||||||
|
if(cmdId == 0x3) {
|
||||||
|
this.tlv.put(0x08, sendCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Response extends HuaweiPacket {
|
||||||
|
public byte cmdId;
|
||||||
|
public short sequenceId;
|
||||||
|
public String srcPackage;
|
||||||
|
public String dstPackage;
|
||||||
|
public String srcFingerprint = null;
|
||||||
|
public String dstFingerprint = null;
|
||||||
|
public byte[] respData = null;
|
||||||
|
public int respCode = 0;
|
||||||
|
public Response (ParamsProvider paramsProvider) {
|
||||||
|
super(paramsProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||||
|
cmdId = this.tlv.getByte(0x01);
|
||||||
|
sequenceId = this.tlv.getShort(0x02);
|
||||||
|
srcPackage = this.tlv.getString(0x03);
|
||||||
|
dstPackage = this.tlv.getString(0x04);
|
||||||
|
if(this.tlv.contains(0x05))
|
||||||
|
srcFingerprint = this.tlv.getString(0x05);
|
||||||
|
if(this.tlv.contains(0x06))
|
||||||
|
dstFingerprint = this.tlv.getString(0x06);
|
||||||
|
if(this.tlv.contains(0x07))
|
||||||
|
respData = this.tlv.getBytes(0x07);
|
||||||
|
// NOTE: P2P service uses different data types in responseCode(TLV 0x08).
|
||||||
|
// It sends byte from wearable but send integer to wearable
|
||||||
|
// So we have a change that other device can send different type.
|
||||||
|
if(this.tlv.contains(0x08))
|
||||||
|
respCode = this.tlv.getByte(0x08);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -56,6 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Menstrual;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Menstrual;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||||
@ -121,6 +122,7 @@ public class AsynchronousResponse {
|
|||||||
handleWatchface(response);
|
handleWatchface(response);
|
||||||
handleCameraRemote(response);
|
handleCameraRemote(response);
|
||||||
handleApp(response);
|
handleApp(response);
|
||||||
|
handleP2p(response);
|
||||||
} catch (Request.ResponseParseException e) {
|
} catch (Request.ResponseParseException e) {
|
||||||
LOG.error("Response parse exception", e);
|
LOG.error("Response parse exception", e);
|
||||||
}
|
}
|
||||||
@ -423,64 +425,91 @@ public class AsynchronousResponse {
|
|||||||
if (response.commandId == FileUpload.FileInfoSend.id) {
|
if (response.commandId == FileUpload.FileInfoSend.id) {
|
||||||
if (!(response instanceof FileUpload.FileInfoSend.Response))
|
if (!(response instanceof FileUpload.FileInfoSend.Response))
|
||||||
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileInfoSend.Response.class);
|
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileInfoSend.Response.class);
|
||||||
FileUpload.FileInfoSend.Response resp = (FileUpload.FileInfoSend.Response) response;
|
if(support.huaweiUploadManager.getFileUploadInfo() == null) {
|
||||||
if (resp.result == 140004) {
|
LOG.error("Upload file info received but no file to upload");
|
||||||
LOG.error("Too many watchfaces installed");
|
} else {
|
||||||
support.handleGBDeviceEvent(new GBDeviceEventDisplayMessage(support.getContext().getString(R.string.cannot_upload_watchface_too_many_watchfaces_installed), Toast.LENGTH_LONG, GB.ERROR));
|
FileUpload.FileInfoSend.Response resp = (FileUpload.FileInfoSend.Response) response;
|
||||||
} else if (resp.result == 140009) {
|
if (resp.result != 100000) {
|
||||||
LOG.error("Insufficient space for upload");
|
if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) {
|
||||||
support.handleGBDeviceEvent(new GBDeviceEventDisplayMessage(support.getContext().getString(R.string.insufficient_space_for_upload), Toast.LENGTH_LONG, GB.ERROR));
|
support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onError(resp.result);
|
||||||
|
} else {
|
||||||
|
LOG.error("Upload file info error without callback: {}", resp.result);
|
||||||
|
}
|
||||||
|
//Cleanup
|
||||||
|
support.huaweiUploadManager.setFileUploadInfo(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (response.commandId == FileUpload.FileHashSend.id) {
|
} else if (response.commandId == FileUpload.FileHashSend.id) {
|
||||||
if (!(response instanceof FileUpload.FileHashSend.Response))
|
if (!(response instanceof FileUpload.FileHashSend.Response))
|
||||||
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileHashSend.Response.class);
|
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileHashSend.Response.class);
|
||||||
FileUpload.FileHashSend.Response resp = (FileUpload.FileHashSend.Response) response;
|
if(support.huaweiUploadManager.getFileUploadInfo() == null) {
|
||||||
support.huaweiUploadManager.setFileId(resp.fileId);
|
LOG.error("Upload file hash requested but no file to upload");
|
||||||
try {
|
} else {
|
||||||
SendFileUploadHash sendFileUploadHash = new SendFileUploadHash(support, support.huaweiUploadManager);
|
FileUpload.FileHashSend.Response resp = (FileUpload.FileHashSend.Response) response;
|
||||||
sendFileUploadHash.doPerform();
|
support.huaweiUploadManager.getFileUploadInfo().setFileId(resp.fileId);
|
||||||
} catch (IOException e) {
|
try {
|
||||||
LOG.error("Could not send fileupload hash request", e);
|
SendFileUploadHash sendFileUploadHash = new SendFileUploadHash(support, support.huaweiUploadManager);
|
||||||
|
sendFileUploadHash.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Could not send file upload hash request", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (response.commandId == FileUpload.FileUploadConsultAck.id) {
|
} else if (response.commandId == FileUpload.FileUploadConsultAck.id) {
|
||||||
if (!(response instanceof FileUpload.FileUploadConsultAck.Response))
|
if (!(response instanceof FileUpload.FileUploadConsultAck.Response))
|
||||||
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileUploadConsultAck.Response.class);
|
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileUploadConsultAck.Response.class);
|
||||||
FileUpload.FileUploadConsultAck.Response resp = (FileUpload.FileUploadConsultAck.Response) response;
|
if(support.huaweiUploadManager.getFileUploadInfo() == null) {
|
||||||
|
LOG.error("Upload file ask requested but no file to upload");
|
||||||
support.huaweiUploadManager.setFileUploadParams(resp.fileUploadParams);
|
} else {
|
||||||
|
FileUpload.FileUploadConsultAck.Response resp = (FileUpload.FileUploadConsultAck.Response) response;
|
||||||
try {
|
support.huaweiUploadManager.getFileUploadInfo().setFileUploadParams(resp.fileUploadParams);
|
||||||
support.huaweiUploadManager.setDeviceBusy();
|
try {
|
||||||
SendFileUploadAck sendFileUploadAck = new SendFileUploadAck(support,
|
if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) {
|
||||||
resp.fileUploadParams.no_encrypt, support.huaweiUploadManager.getFileId());
|
support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onUploadStart();
|
||||||
sendFileUploadAck.doPerform();
|
}
|
||||||
} catch (IOException e) {
|
SendFileUploadAck sendFileUploadAck = new SendFileUploadAck(support,
|
||||||
LOG.error("Could not send fileupload ack request", e);
|
resp.fileUploadParams.no_encrypt, support.huaweiUploadManager.getFileUploadInfo().getFileId());
|
||||||
}
|
sendFileUploadAck.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Could not send file upload ack request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (response.commandId == FileUpload.FileNextChunkParams.id) {
|
} else if (response.commandId == FileUpload.FileNextChunkParams.id) {
|
||||||
if (!(response instanceof FileUpload.FileNextChunkParams))
|
if (!(response instanceof FileUpload.FileNextChunkParams))
|
||||||
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileNextChunkParams.class);
|
throw new Request.ResponseTypeMismatchException(response, FileUpload.FileNextChunkParams.class);
|
||||||
FileUpload.FileNextChunkParams resp = (FileUpload.FileNextChunkParams) response;
|
if(support.huaweiUploadManager.getFileUploadInfo() == null) {
|
||||||
support.huaweiUploadManager.setUploadChunkSize(resp.nextchunkSize);
|
LOG.error("Upload file next chunk requested but no file to upload");
|
||||||
support.huaweiUploadManager.setCurrentUploadPosition(resp.bytesUploaded);
|
} else {
|
||||||
int progress = Math.round(((float)resp.bytesUploaded / (float)support.huaweiUploadManager.getFileSize())* 100);
|
FileUpload.FileNextChunkParams resp = (FileUpload.FileNextChunkParams) response;
|
||||||
support.onUploadProgress(R.string.updatefirmwareoperation_update_in_progress, progress, true);
|
support.huaweiUploadManager.getFileUploadInfo().setUploadChunkSize(resp.nextchunkSize);
|
||||||
|
support.huaweiUploadManager.getFileUploadInfo().setCurrentUploadPosition(resp.bytesUploaded);
|
||||||
try {
|
int progress = Math.round(((float) resp.bytesUploaded / (float) support.huaweiUploadManager.getFileUploadInfo().getFileSize()) * 100);
|
||||||
SendFileUploadChunk sendFileUploadChunk = new SendFileUploadChunk(support, support.huaweiUploadManager);
|
try {
|
||||||
sendFileUploadChunk.doPerform();
|
if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) {
|
||||||
} catch (IOException e) {
|
support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onUploadProgress(progress);
|
||||||
LOG.error("Could not send fileupload next chunk request", e);
|
}
|
||||||
}
|
SendFileUploadChunk sendFileUploadChunk = new SendFileUploadChunk(support, support.huaweiUploadManager);
|
||||||
|
sendFileUploadChunk.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Could not send fileupload next chunk request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (response.commandId == FileUpload.FileUploadResult.id) {
|
} else if (response.commandId == FileUpload.FileUploadResult.id) {
|
||||||
try {
|
if(support.huaweiUploadManager.getFileUploadInfo() == null) {
|
||||||
support.huaweiUploadManager.unsetDeviceBusy();
|
LOG.error("Upload file result requested but no file to upload");
|
||||||
support.onUploadProgress(R.string.updatefirmwareoperation_update_complete, 100, false);
|
} else {
|
||||||
SendFileUploadComplete sendFileUploadComplete = new SendFileUploadComplete(this.support, support.huaweiUploadManager.getFileId());
|
try {
|
||||||
sendFileUploadComplete.doPerform();
|
byte fileId = support.huaweiUploadManager.getFileUploadInfo().getFileId();
|
||||||
} catch (IOException e) {
|
if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) {
|
||||||
LOG.error("Could not send fileupload result request", e);
|
support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onUploadComplete();
|
||||||
}
|
}
|
||||||
|
//Cleanup
|
||||||
|
support.huaweiUploadManager.setFileUploadInfo(null);
|
||||||
|
SendFileUploadComplete sendFileUploadComplete = new SendFileUploadComplete(this.support, fileId);
|
||||||
|
sendFileUploadComplete.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Could not send file upload result request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -523,6 +552,18 @@ public class AsynchronousResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleP2p(HuaweiPacket response) throws Request.ResponseParseException {
|
||||||
|
if (response.serviceId == P2P.id && response.commandId == P2P.P2PCommand.id) {
|
||||||
|
if (!(response instanceof P2P.P2PCommand.Response))
|
||||||
|
throw new Request.ResponseTypeMismatchException(response, P2P.P2PCommand.class);
|
||||||
|
try {
|
||||||
|
this.support.getHuaweiP2PManager().handlePacket((P2P.P2PCommand.Response) response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Error in P2P service", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleWeatherCheck(HuaweiPacket response) {
|
private void handleWeatherCheck(HuaweiPacket response) {
|
||||||
if (response.serviceId == Weather.id && response.commandId == 0x04) {
|
if (response.serviceId == Weather.id && response.commandId == 0x04) {
|
||||||
support.huaweiWeatherManager.handleAsyncMessage(response);
|
support.huaweiWeatherManager.handleAsyncMessage(response);
|
||||||
|
@ -30,6 +30,7 @@ import java.util.UUID;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
@ -177,6 +178,17 @@ public class HuaweiBRSupport extends AbstractBTBRDeviceSupport {
|
|||||||
supportProvider.onSetContacts(contacts);
|
supportProvider.onSetContacts(contacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddCalendarEvent(final CalendarEventSpec calendarEventSpec) {
|
||||||
|
supportProvider.onAddCalendarEvent(calendarEventSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteCalendarEvent(final byte type, long id) {
|
||||||
|
supportProvider.onDeleteCalendarEvent(type, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
supportProvider.dispose();
|
supportProvider.dispose();
|
||||||
|
@ -33,6 +33,7 @@ import java.util.UUID;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
@ -184,6 +185,16 @@ public class HuaweiLESupport extends AbstractBTLEDeviceSupport {
|
|||||||
supportProvider.onSetContacts(contacts);
|
supportProvider.onSetContacts(contacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddCalendarEvent(final CalendarEventSpec calendarEventSpec) {
|
||||||
|
supportProvider.onAddCalendarEvent(calendarEventSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteCalendarEvent(final byte type, long id) {
|
||||||
|
supportProvider.onDeleteCalendarEvent(type, id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
supportProvider.dispose();
|
supportProvider.dispose();
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiBaseP2PService;
|
||||||
|
|
||||||
|
public class HuaweiP2PManager {
|
||||||
|
private final Logger LOG = LoggerFactory.getLogger(HuaweiP2PManager.class);
|
||||||
|
|
||||||
|
private final HuaweiSupportProvider support;
|
||||||
|
|
||||||
|
private final List<HuaweiBaseP2PService> registeredServices;
|
||||||
|
|
||||||
|
private Short sequence = 1;
|
||||||
|
|
||||||
|
public synchronized Short getNextSequence() {
|
||||||
|
return sequence++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HuaweiP2PManager(HuaweiSupportProvider support) {
|
||||||
|
this.support = support;
|
||||||
|
this.registeredServices = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HuaweiSupportProvider getSupportProvider() {
|
||||||
|
return support;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerService(HuaweiBaseP2PService service) {
|
||||||
|
for (HuaweiBaseP2PService svr : registeredServices) {
|
||||||
|
if (svr.getModule().equals(service.getModule())) {
|
||||||
|
LOG.error("P2P Service already registered, unregister: {}", service.getModule());
|
||||||
|
svr.unregister();
|
||||||
|
registeredServices.remove(svr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registeredServices.add(service);
|
||||||
|
service.registered();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HuaweiBaseP2PService getRegisteredService(String module) {
|
||||||
|
for (HuaweiBaseP2PService svr : registeredServices) {
|
||||||
|
if (svr.getModule().equals(module)) {
|
||||||
|
return svr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterAllService() {
|
||||||
|
for (HuaweiBaseP2PService svr : registeredServices) {
|
||||||
|
svr.unregister();
|
||||||
|
}
|
||||||
|
registeredServices.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void handlePacket(P2P.P2PCommand.Response packet) {
|
||||||
|
LOG.info("P2P Service message: Src: {} Dst: {} Seq: {}", packet.srcPackage, packet.dstPackage, packet.sequenceId);
|
||||||
|
for (HuaweiBaseP2PService service : registeredServices) {
|
||||||
|
if (service.getPackage().equals(packet.srcPackage)) {
|
||||||
|
service.handlePacket(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -81,6 +81,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
@ -90,6 +91,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p.HuaweiP2PCalendarService;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.AcceptAgreementsRequest;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.AcceptAgreementsRequest;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetAppInfoParams;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetAppInfoParams;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetContactsCount;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetContactsCount;
|
||||||
@ -211,6 +213,9 @@ public class HuaweiSupportProvider {
|
|||||||
|
|
||||||
protected HuaweiWeatherManager huaweiWeatherManager = new HuaweiWeatherManager(this);
|
protected HuaweiWeatherManager huaweiWeatherManager = new HuaweiWeatherManager(this);
|
||||||
|
|
||||||
|
//TODO: we need only one instance of manager and all it services.
|
||||||
|
protected HuaweiP2PManager huaweiP2PManager = new HuaweiP2PManager(this);
|
||||||
|
|
||||||
public HuaweiCoordinatorSupplier getCoordinator() {
|
public HuaweiCoordinatorSupplier getCoordinator() {
|
||||||
return ((HuaweiCoordinatorSupplier) this.gbDevice.getDeviceCoordinator());
|
return ((HuaweiCoordinatorSupplier) this.gbDevice.getDeviceCoordinator());
|
||||||
}
|
}
|
||||||
@ -218,6 +223,11 @@ public class HuaweiSupportProvider {
|
|||||||
public HuaweiCoordinator getHuaweiCoordinator() {
|
public HuaweiCoordinator getHuaweiCoordinator() {
|
||||||
return getCoordinator().getHuaweiCoordinator();
|
return getCoordinator().getHuaweiCoordinator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HuaweiUploadManager getUploadManager() {
|
||||||
|
return huaweiUploadManager;
|
||||||
|
}
|
||||||
|
|
||||||
public HuaweiWatchfaceManager getHuaweiWatchfaceManager() {
|
public HuaweiWatchfaceManager getHuaweiWatchfaceManager() {
|
||||||
return huaweiWatchfaceManager;
|
return huaweiWatchfaceManager;
|
||||||
}
|
}
|
||||||
@ -225,6 +235,11 @@ public class HuaweiSupportProvider {
|
|||||||
public HuaweiAppManager getHuaweiAppManager() {
|
public HuaweiAppManager getHuaweiAppManager() {
|
||||||
return huaweiAppManager;
|
return huaweiAppManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HuaweiP2PManager getHuaweiP2PManager() {
|
||||||
|
return huaweiP2PManager;
|
||||||
|
}
|
||||||
|
|
||||||
public HuaweiSupportProvider(HuaweiBRSupport support) {
|
public HuaweiSupportProvider(HuaweiBRSupport support) {
|
||||||
this.brSupport = support;
|
this.brSupport = support;
|
||||||
}
|
}
|
||||||
@ -563,6 +578,7 @@ public class HuaweiSupportProvider {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
huaweiP2PManager.unregisterAllService();
|
||||||
stopBatteryRunnerDelayed();
|
stopBatteryRunnerDelayed();
|
||||||
GetBatteryLevelRequest batteryLevelReq = new GetBatteryLevelRequest(this);
|
GetBatteryLevelRequest batteryLevelReq = new GetBatteryLevelRequest(this);
|
||||||
batteryLevelReq.setFinalizeReq(new RequestCallback() {
|
batteryLevelReq.setFinalizeReq(new RequestCallback() {
|
||||||
@ -755,6 +771,15 @@ public class HuaweiSupportProvider {
|
|||||||
public void call() {
|
public void call() {
|
||||||
gbDevice.setState(GBDevice.State.INITIALIZED);
|
gbDevice.setState(GBDevice.State.INITIALIZED);
|
||||||
gbDevice.sendDeviceUpdateIntent(getContext(), GBDevice.DeviceUpdateSubject.DEVICE_STATE);
|
gbDevice.sendDeviceUpdateIntent(getContext(), GBDevice.DeviceUpdateSubject.DEVICE_STATE);
|
||||||
|
|
||||||
|
if(getHuaweiCoordinator().supportsP2PService()) {
|
||||||
|
if(getHuaweiCoordinator().supportsCalendar()) {
|
||||||
|
if (HuaweiP2PCalendarService.getRegisteredInstance(huaweiP2PManager) == null) {
|
||||||
|
HuaweiP2PCalendarService calendarService = new HuaweiP2PCalendarService(huaweiP2PManager);
|
||||||
|
calendarService.register();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1004,6 +1029,9 @@ public class HuaweiSupportProvider {
|
|||||||
case ActivityUser.PREF_USER_DATE_OF_BIRTH:
|
case ActivityUser.PREF_USER_DATE_OF_BIRTH:
|
||||||
sendUserInfo();
|
sendUserInfo();
|
||||||
break;
|
break;
|
||||||
|
case DeviceSettingsPreferenceConst.PREF_SYNC_CALENDAR:
|
||||||
|
HuaweiP2PCalendarService.getRegisteredInstance(huaweiP2PManager).restartSynchronization();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO: Use translatable string
|
// TODO: Use translatable string
|
||||||
@ -1815,13 +1843,47 @@ public class HuaweiSupportProvider {
|
|||||||
public void onInstallApp(Uri uri) {
|
public void onInstallApp(Uri uri) {
|
||||||
LOG.info("enter onAppInstall uri: "+uri);
|
LOG.info("enter onAppInstall uri: "+uri);
|
||||||
HuaweiFwHelper huaweiFwHelper = new HuaweiFwHelper(uri, getContext());
|
HuaweiFwHelper huaweiFwHelper = new HuaweiFwHelper(uri, getContext());
|
||||||
huaweiUploadManager.setBytes(huaweiFwHelper.getBytes());
|
|
||||||
huaweiUploadManager.setFileType(huaweiFwHelper.getFileType());
|
HuaweiUploadManager.FileUploadInfo fileInfo = new HuaweiUploadManager.FileUploadInfo();
|
||||||
|
|
||||||
|
fileInfo.setFileType(huaweiFwHelper.getFileType());
|
||||||
if (huaweiFwHelper.isWatchface()) {
|
if (huaweiFwHelper.isWatchface()) {
|
||||||
huaweiUploadManager.setFileName(huaweiWatchfaceManager.getRandomName());
|
fileInfo.setFileName(huaweiWatchfaceManager.getRandomName());
|
||||||
} else {
|
} else {
|
||||||
huaweiUploadManager.setFileName(huaweiFwHelper.getFileName());
|
fileInfo.setFileName(huaweiFwHelper.getFileName());
|
||||||
}
|
}
|
||||||
|
fileInfo.setBytes(huaweiFwHelper.getBytes());
|
||||||
|
|
||||||
|
fileInfo.setFileUploadCallback(new HuaweiUploadManager.FileUploadCallback() {
|
||||||
|
@Override
|
||||||
|
public void onUploadStart() {
|
||||||
|
HuaweiSupportProvider.this.huaweiUploadManager.setDeviceBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUploadProgress(int progress) {
|
||||||
|
HuaweiSupportProvider.this.onUploadProgress(R.string.updatefirmwareoperation_update_in_progress, progress, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUploadComplete() {
|
||||||
|
HuaweiSupportProvider.this.huaweiUploadManager.unsetDeviceBusy();
|
||||||
|
HuaweiSupportProvider.this.onUploadProgress(R.string.updatefirmwareoperation_update_complete, 100, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(int code) {
|
||||||
|
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 == 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
huaweiUploadManager.setFileUploadInfo(fileInfo);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SendFileUploadInfo sendFileUploadInfo = new SendFileUploadInfo(this, huaweiUploadManager);
|
SendFileUploadInfo sendFileUploadInfo = new SendFileUploadInfo(this, huaweiUploadManager);
|
||||||
@ -1949,6 +2011,20 @@ public class HuaweiSupportProvider {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onAddCalendarEvent(final CalendarEventSpec calendarEventSpec) {
|
||||||
|
HuaweiP2PCalendarService service = HuaweiP2PCalendarService.getRegisteredInstance(huaweiP2PManager);
|
||||||
|
if(service != null) {
|
||||||
|
service.onAddCalendarEvent(calendarEventSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDeleteCalendarEvent(final byte type, long id) {
|
||||||
|
HuaweiP2PCalendarService service = HuaweiP2PCalendarService.getRegisteredInstance(huaweiP2PManager);
|
||||||
|
if(service != null) {
|
||||||
|
service.onDeleteCalendarEvent(type, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean startBatteryRunnerDelayed() {
|
public boolean startBatteryRunnerDelayed() {
|
||||||
int interval_minutes = GBApplication.getDevicePrefs(gbDevice).getBatteryPollingIntervalMinutes();
|
int interval_minutes = GBApplication.getDevicePrefs(gbDevice).getBatteryPollingIntervalMinutes();
|
||||||
int interval = interval_minutes * 60 * 1000;
|
int interval = interval_minutes * 60 * 1000;
|
||||||
@ -1965,6 +2041,7 @@ public class HuaweiSupportProvider {
|
|||||||
public void dispose() {
|
public void dispose() {
|
||||||
stopBatteryRunnerDelayed();
|
stopBatteryRunnerDelayed();
|
||||||
huaweiFileDownloadManager.dispose();
|
huaweiFileDownloadManager.dispose();
|
||||||
|
huaweiP2PManager.unregisterAllService();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean downloadTruSleepData(int start, int end) {
|
public boolean downloadTruSleepData(int start, int end) {
|
||||||
|
@ -31,109 +31,165 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|||||||
|
|
||||||
public class HuaweiUploadManager {
|
public class HuaweiUploadManager {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(HuaweiUploadManager.class);
|
private static final Logger LOG = LoggerFactory.getLogger(HuaweiUploadManager.class);
|
||||||
|
|
||||||
|
public interface FileUploadCallback {
|
||||||
|
void onUploadStart();
|
||||||
|
void onUploadProgress(int progress);
|
||||||
|
void onUploadComplete();
|
||||||
|
void onError(int code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FileUploadInfo {
|
||||||
|
private byte[] fileBin;
|
||||||
|
private byte[] fileSHA256;
|
||||||
|
private byte fileType = 1; // 1 - watchface, 2 - music, 3 - png for background , 7 - app
|
||||||
|
private int fileSize = 0;
|
||||||
|
private byte fileId = 0; // get on incoming (2803)
|
||||||
|
|
||||||
|
private String fileName = ""; //FIXME generate random name
|
||||||
|
|
||||||
|
private String srcPackage = null;
|
||||||
|
private String dstPackage = null;
|
||||||
|
private String srcFingerprint = null;
|
||||||
|
private String dstFingerprint = null;
|
||||||
|
private boolean isEncrypted = false;
|
||||||
|
|
||||||
|
private int currentUploadPosition = 0;
|
||||||
|
private int uploadChunkSize = 0;
|
||||||
|
|
||||||
|
private FileUploadCallback fileUploadCallback = null;
|
||||||
|
|
||||||
|
//ack values set from 28 4 response
|
||||||
|
private FileUploadParams fileUploadParams = null;
|
||||||
|
|
||||||
|
|
||||||
|
public FileUploadCallback getFileUploadCallback() {
|
||||||
|
return fileUploadCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileUploadCallback(FileUploadCallback fileUploadCallback) {
|
||||||
|
this.fileUploadCallback = fileUploadCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileUploadParams(FileUploadParams params) {
|
||||||
|
this.fileUploadParams = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getUnitSize() {
|
||||||
|
return fileUploadParams.unit_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(byte[] uploadArray) {
|
||||||
|
|
||||||
|
this.fileSize = uploadArray.length;
|
||||||
|
this.fileBin = uploadArray;
|
||||||
|
|
||||||
|
try {
|
||||||
|
MessageDigest m = MessageDigest.getInstance("SHA256");
|
||||||
|
m.update(fileBin, 0, fileBin.length);
|
||||||
|
fileSHA256 = m.digest();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
LOG.error("Digest algorithm not found.", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUploadPosition = 0;
|
||||||
|
uploadChunkSize = 0;
|
||||||
|
|
||||||
|
LOG.info("File ready for upload, SHA256: "+ GB.hexdump(fileSHA256) + " fileName: " + fileName + " filetype: ", fileType);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFileSize() {
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return this.fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileName(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getFileType() {
|
||||||
|
return this.fileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileType(byte fileType) {
|
||||||
|
this.fileType = fileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getFileId() {
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileId(byte fileId) {
|
||||||
|
this.fileId = fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSrcPackage() { return srcPackage; }
|
||||||
|
|
||||||
|
public void setSrcPackage(String srcPackage) { this.srcPackage = srcPackage; }
|
||||||
|
|
||||||
|
public String getDstPackage() { return dstPackage; }
|
||||||
|
|
||||||
|
public void setDstPackage(String dstPackage) { this.dstPackage = dstPackage; }
|
||||||
|
|
||||||
|
public String getSrcFingerprint() { return srcFingerprint; }
|
||||||
|
|
||||||
|
public void setSrcFingerprint(String srcFingerprint) { this.srcFingerprint = srcFingerprint; }
|
||||||
|
|
||||||
|
public String getDstFingerprint() { return dstFingerprint;}
|
||||||
|
|
||||||
|
public void setDstFingerprint(String dstFingerprint) { this.dstFingerprint = dstFingerprint;}
|
||||||
|
|
||||||
|
public boolean isEncrypted() { return isEncrypted; }
|
||||||
|
|
||||||
|
public void setEncrypted(boolean encrypted) { isEncrypted = encrypted; }
|
||||||
|
|
||||||
|
public byte[] getFileSHA256() {
|
||||||
|
return fileSHA256;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUploadChunkSize(int chunkSize) {
|
||||||
|
uploadChunkSize = chunkSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentUploadPosition (int pos) {
|
||||||
|
currentUploadPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentUploadPosition() {
|
||||||
|
return currentUploadPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getCurrentChunk() {
|
||||||
|
byte[] ret = new byte[uploadChunkSize];
|
||||||
|
System.arraycopy(fileBin, currentUploadPosition, ret, 0, uploadChunkSize);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final HuaweiSupportProvider support;
|
private final HuaweiSupportProvider support;
|
||||||
byte[] fileBin;
|
|
||||||
byte[] fileSHA256;
|
|
||||||
byte fileType = 1; // 1 - watchface, 2 - music, 3 - png for background , 7 - app
|
|
||||||
int fileSize = 0;
|
|
||||||
byte fileId = 0; // get on incoming (2803)
|
|
||||||
|
|
||||||
int currentUploadPosition = 0;
|
|
||||||
int uploadChunkSize =0;
|
|
||||||
|
|
||||||
String fileName = ""; //FIXME generate random name
|
|
||||||
|
|
||||||
//ack values set from 28 4 response
|
|
||||||
FileUploadParams fileUploadParams;
|
|
||||||
|
|
||||||
|
FileUploadInfo fileUploadInfo = null;
|
||||||
|
|
||||||
public HuaweiUploadManager(HuaweiSupportProvider support) {
|
public HuaweiUploadManager(HuaweiSupportProvider support) {
|
||||||
this.support=support;
|
this.support=support;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBytes(byte[] uploadArray) {
|
public FileUploadInfo getFileUploadInfo() {
|
||||||
|
return fileUploadInfo;
|
||||||
this.fileSize = uploadArray.length;
|
|
||||||
this.fileBin = uploadArray;
|
|
||||||
|
|
||||||
try {
|
|
||||||
MessageDigest m = MessageDigest.getInstance("SHA256");
|
|
||||||
m.update(fileBin, 0, fileBin.length);
|
|
||||||
fileSHA256 = m.digest();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
LOG.error("Digest alghoritm not found.", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentUploadPosition = 0;
|
|
||||||
uploadChunkSize = 0;
|
|
||||||
|
|
||||||
LOG.info("File ready for upload, SHA256: "+ GB.hexdump(fileSHA256) + " fileName: " + fileName + " filetype: ", fileType);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFileSize() {
|
public void setFileUploadInfo(FileUploadInfo fileUploadInfo) {
|
||||||
return fileSize;
|
this.fileUploadInfo = fileUploadInfo;
|
||||||
}
|
|
||||||
|
|
||||||
public String getFileName() {
|
|
||||||
return this.fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFileName(String fileName) {
|
|
||||||
this.fileName = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getFileType() {
|
|
||||||
return this.fileType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFileType(byte fileType) {
|
|
||||||
this.fileType = fileType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getFileId() {
|
|
||||||
return fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFileId(byte fileId) {
|
|
||||||
this.fileId = fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getFileSHA256() {
|
|
||||||
return fileSHA256;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUploadChunkSize(int chunkSize) {
|
|
||||||
uploadChunkSize = chunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentUploadPosition (int pos) {
|
|
||||||
currentUploadPosition = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCurrentUploadPosition() {
|
|
||||||
return currentUploadPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCurrentChunk() {
|
|
||||||
byte[] ret = new byte[uploadChunkSize];
|
|
||||||
System.arraycopy(fileBin, currentUploadPosition, ret, 0, uploadChunkSize);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFileUploadParams(FileUploadParams params) {
|
|
||||||
this.fileUploadParams = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public short getUnitSize() {
|
|
||||||
return fileUploadParams.unit_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDeviceBusy() {
|
public void setDeviceBusy() {
|
||||||
final GBDevice device = support.getDevice();
|
final GBDevice device = support.getDevice();
|
||||||
if(fileType == FileUpload.Filetype.watchface) {
|
if(fileUploadInfo != null && fileUploadInfo.fileType == FileUpload.Filetype.watchface) {
|
||||||
device.setBusyTask(support.getContext().getString(R.string.uploading_watchface));
|
device.setBusyTask(support.getContext().getString(R.string.uploading_watchface));
|
||||||
} else {
|
} else {
|
||||||
device.setBusyTask(support.getContext().getString(R.string.updating_firmware));
|
device.setBusyTask(support.getContext().getString(R.string.updating_firmware));
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiP2PManager;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendP2PCommand;
|
||||||
|
|
||||||
|
public abstract class HuaweiBaseP2PService {
|
||||||
|
private final Logger LOG = LoggerFactory.getLogger(HuaweiBaseP2PService.class);
|
||||||
|
|
||||||
|
protected final HuaweiP2PManager manager;
|
||||||
|
|
||||||
|
protected HuaweiBaseP2PService(HuaweiP2PManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register() {
|
||||||
|
manager.registerService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getModule();
|
||||||
|
|
||||||
|
public abstract String getPackage();
|
||||||
|
|
||||||
|
public abstract String getFingerprint();
|
||||||
|
|
||||||
|
public abstract void registered();
|
||||||
|
|
||||||
|
public abstract void unregister();
|
||||||
|
|
||||||
|
public abstract void handleData(byte[] data);
|
||||||
|
|
||||||
|
public String getLocalFingerprint() {
|
||||||
|
return "UniteDeviceManagement";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPingPackage() {
|
||||||
|
return "com.huawei.health";
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<Short, HuaweiP2PCallback> waitPackets = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Short getNextSequence() {
|
||||||
|
return manager.getNextSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendCommand(byte[] sendData, HuaweiP2PCallback callback) {
|
||||||
|
try {
|
||||||
|
short seq = this.getNextSequence();
|
||||||
|
SendP2PCommand test = new SendP2PCommand(this.manager.getSupportProvider(), (byte) 2, seq, this.getModule(), this.getPackage(), this.getLocalFingerprint(), this.getFingerprint(), sendData, 0);
|
||||||
|
if (callback != null) {
|
||||||
|
this.waitPackets.put(seq, callback);
|
||||||
|
}
|
||||||
|
test.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPing(HuaweiP2PCallback callback) {
|
||||||
|
try {
|
||||||
|
short seq = this.getNextSequence();
|
||||||
|
SendP2PCommand test = new SendP2PCommand(this.manager.getSupportProvider(), (byte) 1, seq, this.getPingPackage(), this.getPackage(), null, null, null, 0);
|
||||||
|
if (callback != null) {
|
||||||
|
this.waitPackets.put(seq, callback);
|
||||||
|
}
|
||||||
|
test.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendAck(short sequence, String srcPackage, String dstPackage, int code) {
|
||||||
|
try {
|
||||||
|
SendP2PCommand test = new SendP2PCommand(this.manager.getSupportProvider(), (byte) 3, sequence, srcPackage, dstPackage, null, null, null, code);
|
||||||
|
test.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePacket(P2P.P2PCommand.Response packet) {
|
||||||
|
LOG.info("HuaweiP2PCalendarService handlePacket: {} Code: {}", packet.cmdId, packet.respCode);
|
||||||
|
if (waitPackets.containsKey(packet.sequenceId)) {
|
||||||
|
LOG.info("HuaweiP2PCalendarService handlePacket find handler");
|
||||||
|
HuaweiP2PCallback handle = waitPackets.remove(packet.sequenceId);
|
||||||
|
handle.onResponse(packet.respCode, packet.respData);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (packet.cmdId == 1) { //Ping
|
||||||
|
sendAck(packet.sequenceId, packet.dstPackage, packet.srcPackage, 0xca);
|
||||||
|
} else if (packet.cmdId == 2) {
|
||||||
|
handleData(packet.respData);
|
||||||
|
sendAck(packet.sequenceId, packet.dstPackage, packet.srcPackage, 0xca);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,441 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p;
|
||||||
|
|
||||||
|
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SYNC_CALENDAR;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiP2PManager;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiUploadManager;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadInfo;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
|
||||||
|
|
||||||
|
public class HuaweiP2PCalendarService extends HuaweiBaseP2PService {
|
||||||
|
private final Logger LOG = LoggerFactory.getLogger(HuaweiP2PCalendarService.class);
|
||||||
|
|
||||||
|
public static final String MODULE = "hw.unitedevice.calendarapp";
|
||||||
|
|
||||||
|
private final AtomicBoolean isRegistered = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
private final Runnable syncCallback = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
sendCalendarCmd((byte) 0x01, (byte) 0x01, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private List<CalendarEvent> lastCalendarEvents = null;
|
||||||
|
|
||||||
|
public HuaweiP2PCalendarService(HuaweiP2PManager manager) {
|
||||||
|
super(manager);
|
||||||
|
LOG.info("HuaweiP2PCalendarService");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HuaweiP2PCalendarService getRegisteredInstance(HuaweiP2PManager manager) {
|
||||||
|
return (HuaweiP2PCalendarService) manager.getRegisteredService(HuaweiP2PCalendarService.MODULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModule() {
|
||||||
|
return HuaweiP2PCalendarService.MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPackage() {
|
||||||
|
if (manager.getSupportProvider().getHuaweiCoordinator().supportsExternalCalendarService())
|
||||||
|
return "com.huawei.ohos.calendar";
|
||||||
|
return "in.huawei.calendar";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFingerprint() {
|
||||||
|
if (manager.getSupportProvider().getHuaweiCoordinator().supportsExternalCalendarService())
|
||||||
|
return "com.huawei.ohos.calendar_BCgpfcWNSKWgvxsSILxooQZyAmKYsFQnMTibnfrKQqK9M0ABtXH+GbsOscsnVvVc5qIDiFEyEOYMSF7gJ7Vb5Mc=";
|
||||||
|
return "SystemApp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registered() {
|
||||||
|
isRegistered.set(true);
|
||||||
|
startSynchronization();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregister() {
|
||||||
|
isRegistered.set(false);
|
||||||
|
handler.removeCallbacks(syncCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startSynchronization() {
|
||||||
|
sendCalendarCmd((byte) 0x02, (byte) 0x01, null); // download calendar request but it does not work on my device
|
||||||
|
sendCalendarCmd((byte) 0x01, (byte) 0x01, null); // send sync upload request
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restartSynchronization() {
|
||||||
|
if (isRegistered.get()) {
|
||||||
|
scheduleUpdate(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleUpdate(long delay) {
|
||||||
|
handler.removeCallbacks(syncCallback);
|
||||||
|
if (isRegistered.get()) {
|
||||||
|
handler.postDelayed(syncCallback, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendCalendarCmd(byte command, byte commandData, HuaweiP2PCallback callback) {
|
||||||
|
sendPing(new HuaweiP2PCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(int code, byte[] data) {
|
||||||
|
if ((byte) code != (byte) 0xca)
|
||||||
|
return;
|
||||||
|
// NOTE: basically this is TLV with one tag 0x1, But I have no reasons to create additional classes for this.
|
||||||
|
sendCommand(new byte[]{command, 0x01, 0x01, commandData}, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAddCalendarEvent(final CalendarEventSpec calendarEventSpec) {
|
||||||
|
LOG.info("onAddCalendarEvent {}", calendarEventSpec.id);
|
||||||
|
scheduleUpdate(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDeleteCalendarEvent(final byte type, long id) {
|
||||||
|
LOG.info("onDeleteCalendarEvent {} {}", type, id);
|
||||||
|
scheduleUpdate(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String truncateToHexBytes(String str, int count) {
|
||||||
|
if(str == null)
|
||||||
|
return "";
|
||||||
|
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
|
||||||
|
if (bytes.length > count) {
|
||||||
|
int len = (int)Math.ceil(count/2.0) + 1;
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(bytes, 0, len - 3);
|
||||||
|
CharBuffer cb = CharBuffer.allocate(len);
|
||||||
|
CharsetDecoder cd = StandardCharsets.UTF_8.newDecoder();
|
||||||
|
cd.onMalformedInput(CodingErrorAction.IGNORE);
|
||||||
|
cd.decode(buffer, cb, true);
|
||||||
|
cd.flush(cb);
|
||||||
|
return new String(cb.array(), 0, cb.position()) + "...";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getFileCalendarName() {
|
||||||
|
long millis = System.currentTimeMillis();
|
||||||
|
return String.format(Locale.ROOT, "calendar_data_%d.json", millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonObject calendarEventToJson(CalendarEvent calendarEvent) {
|
||||||
|
JsonObject ret = new JsonObject();
|
||||||
|
|
||||||
|
// NOTE: Calendar contain reminders already in required format. But GB reformat them.
|
||||||
|
// So we need to reformat them back.
|
||||||
|
StringBuilder reminders = new StringBuilder();
|
||||||
|
for (long rem : calendarEvent.getRemindersAbsoluteTs()) {
|
||||||
|
reminders.append(String.valueOf((calendarEvent.getBegin() - rem) / 60 / 1000L)).append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
String rrule = calendarEvent.getRrule();
|
||||||
|
if(rrule == null)
|
||||||
|
rrule = "";
|
||||||
|
|
||||||
|
String eventUUID = String.valueOf(calendarEvent.getId()) + "_" + calendarEvent.getBegin();
|
||||||
|
|
||||||
|
ret.addProperty("account_name", truncateToHexBytes(calendarEvent.getCalAccountName(), 64));
|
||||||
|
ret.addProperty("account_type", truncateToHexBytes(calendarEvent.getCalAccountType(), 64));
|
||||||
|
ret.addProperty("all_day", calendarEvent.isAllDay() ? 1 : 0);
|
||||||
|
ret.addProperty("calendar_color", calendarEvent.getColor());
|
||||||
|
ret.addProperty("calendar_displayName", truncateToHexBytes(calendarEvent.getCalName(), 64));
|
||||||
|
ret.addProperty("calendar_id", calendarEvent.getCalendarId());
|
||||||
|
ret.addProperty("description", truncateToHexBytes(calendarEvent.getDescription(), 512));
|
||||||
|
ret.addProperty("dtend", calendarEvent.getEnd());
|
||||||
|
ret.addProperty("dtstart", calendarEvent.getBegin());
|
||||||
|
ret.addProperty("event_id", String.valueOf(calendarEvent.getId()));
|
||||||
|
ret.addProperty("event_location", truncateToHexBytes(calendarEvent.getLocation(), 256));
|
||||||
|
ret.addProperty("event_uuid", eventUUID);
|
||||||
|
ret.addProperty("has_alarm", (reminders.length() == 0) ? 0 : 1);
|
||||||
|
ret.addProperty("minutes", reminders.toString());
|
||||||
|
ret.addProperty("operation", 1); // 1 - add, 2 - delete
|
||||||
|
ret.addProperty("rrule", rrule);
|
||||||
|
// TODO: Retrieve from CalendarContract.CalendarAlerts, field state
|
||||||
|
// TODO: see handleData function command ID 3 for details
|
||||||
|
ret.addProperty("state", -1);
|
||||||
|
ret.addProperty("title", truncateToHexBytes(calendarEvent.getTitle(), 500));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonObject calendarDeletedEventToJson(CalendarEvent calendarEvent) {
|
||||||
|
JsonObject ret = new JsonObject();
|
||||||
|
|
||||||
|
String eventUUID = String.valueOf(calendarEvent.getId()) + "_" + calendarEvent.getBegin();
|
||||||
|
|
||||||
|
ret.addProperty("account_name", "");
|
||||||
|
ret.addProperty("account_type", "");
|
||||||
|
ret.addProperty("all_day", 0);
|
||||||
|
ret.addProperty("calendar_color", 0);
|
||||||
|
ret.addProperty("calendar_displayName", "");
|
||||||
|
ret.addProperty("calendar_id", "");
|
||||||
|
ret.addProperty("description", "");
|
||||||
|
ret.addProperty("dtend", 0);
|
||||||
|
ret.addProperty("dtstart", 0);
|
||||||
|
ret.addProperty("event_id", String.valueOf(calendarEvent.getId()));
|
||||||
|
ret.addProperty("event_location", "");
|
||||||
|
ret.addProperty("event_uuid", eventUUID);
|
||||||
|
ret.addProperty("has_alarm", 0);
|
||||||
|
ret.addProperty("minutes", "");
|
||||||
|
ret.addProperty("operation", 2); // 1 - add, 2 - delete
|
||||||
|
ret.addProperty("rrule", "");
|
||||||
|
ret.addProperty("state", 0);
|
||||||
|
ret.addProperty("title", "");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonArray getFullCalendarData() {
|
||||||
|
final CalendarManager upcomingEvents = new CalendarManager(manager.getSupportProvider().getContext(), manager.getSupportProvider().getDevice().getAddress());
|
||||||
|
final List<CalendarEvent> calendarEvents = upcomingEvents.getCalendarEventList();
|
||||||
|
|
||||||
|
JsonArray events = new JsonArray();
|
||||||
|
for (final CalendarEvent calendarEvent : calendarEvents) {
|
||||||
|
events.add(calendarEventToJson(calendarEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCalendarEvents = calendarEvents;
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonArray getUpdateCalendarData() {
|
||||||
|
final CalendarManager upcomingEvents = new CalendarManager(manager.getSupportProvider().getContext(), manager.getSupportProvider().getDevice().getAddress());
|
||||||
|
final List<CalendarEvent> calendarEvents = upcomingEvents.getCalendarEventList();
|
||||||
|
|
||||||
|
List<CalendarEvent> newEvents = new ArrayList<>(calendarEvents);
|
||||||
|
newEvents.removeAll(lastCalendarEvents);
|
||||||
|
|
||||||
|
List<CalendarEvent> removedEvents = new ArrayList<>(lastCalendarEvents);
|
||||||
|
removedEvents.removeAll(calendarEvents);
|
||||||
|
|
||||||
|
JsonArray events = new JsonArray();
|
||||||
|
for (final CalendarEvent calendarEvent : newEvents) {
|
||||||
|
events.add(calendarEventToJson(calendarEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final CalendarEvent calendarEvent : removedEvents) {
|
||||||
|
events.add(calendarDeletedEventToJson(calendarEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCalendarEvents = calendarEvents;
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getCalendarFileContent(String majorVersion, short minorVersion, JsonArray scheduleList) {
|
||||||
|
JsonObject syncData = new JsonObject();
|
||||||
|
syncData.addProperty("major", majorVersion);
|
||||||
|
syncData.addProperty("minor", minorVersion);
|
||||||
|
syncData.add("scheduleList", scheduleList);
|
||||||
|
|
||||||
|
String data = new Gson().toJson(syncData);
|
||||||
|
LOG.info(data);
|
||||||
|
|
||||||
|
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
|
||||||
|
ByteBuffer sendData = ByteBuffer.allocate(dataBytes.length + 8); // 8 is data header
|
||||||
|
// NOTE: minor version is short in response but in this case it writes as integer
|
||||||
|
sendData.putInt(minorVersion);
|
||||||
|
sendData.putInt(dataBytes.length);
|
||||||
|
sendData.put(dataBytes);
|
||||||
|
|
||||||
|
return sendData.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendCalendarFile(String majorVersion, short minorVersion, short scheduleCount) {
|
||||||
|
LOG.info("Send calendar file upload info");
|
||||||
|
HuaweiUploadManager huaweiUploadManager = this.manager.getSupportProvider().getUploadManager();
|
||||||
|
|
||||||
|
JsonArray calendarData;
|
||||||
|
if (majorVersion == null || majorVersion.isEmpty() || lastCalendarEvents == null || minorVersion == 0) {
|
||||||
|
calendarData = getFullCalendarData();
|
||||||
|
if(calendarData.isEmpty()) {
|
||||||
|
if(minorVersion == 0 && !(majorVersion == null || majorVersion.isEmpty())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
minorVersion = 0;
|
||||||
|
} else {
|
||||||
|
minorVersion++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
calendarData = getUpdateCalendarData();
|
||||||
|
if (calendarData.isEmpty())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (majorVersion == null || majorVersion.isEmpty()) {
|
||||||
|
majorVersion = new String(this.manager.getSupportProvider().getAndroidId(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = getCalendarFileContent(majorVersion, minorVersion, calendarData);
|
||||||
|
|
||||||
|
HuaweiUploadManager.FileUploadInfo fileInfo = new HuaweiUploadManager.FileUploadInfo();
|
||||||
|
|
||||||
|
fileInfo.setFileType((byte) 7);
|
||||||
|
fileInfo.setFileName(getFileCalendarName());
|
||||||
|
fileInfo.setBytes(data);
|
||||||
|
fileInfo.setSrcPackage(this.getModule());
|
||||||
|
fileInfo.setDstPackage(this.getPackage());
|
||||||
|
fileInfo.setSrcFingerprint(this.getLocalFingerprint());
|
||||||
|
fileInfo.setDstFingerprint(this.getFingerprint());
|
||||||
|
fileInfo.setEncrypted(true);
|
||||||
|
|
||||||
|
fileInfo.setFileUploadCallback(new HuaweiUploadManager.FileUploadCallback() {
|
||||||
|
@Override
|
||||||
|
public void onUploadStart() {
|
||||||
|
// TODO: set device as busy in this case. But maybe exists another way to do this. Currently user see text on device card.
|
||||||
|
// Also text should be changed
|
||||||
|
manager.getSupportProvider().getDevice().setBusyTask(manager.getSupportProvider().getContext().getString(R.string.updating_firmware));
|
||||||
|
manager.getSupportProvider().getDevice().sendDeviceUpdateIntent(manager.getSupportProvider().getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUploadProgress(int progress) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUploadComplete() {
|
||||||
|
if (manager.getSupportProvider().getDevice().isBusy()) {
|
||||||
|
manager.getSupportProvider().getDevice().unsetBusyTask();
|
||||||
|
manager.getSupportProvider().getDevice().sendDeviceUpdateIntent(manager.getSupportProvider().getContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(int code) {
|
||||||
|
// TODO: maybe we should retry resend on error, at least 3 times.
|
||||||
|
// currently I don't understand the mandatory of this action because file sends always successfully,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
huaweiUploadManager.setFileUploadInfo(fileInfo);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SendFileUploadInfo sendFileUploadInfo = new SendFileUploadInfo(this.manager.getSupportProvider(), huaweiUploadManager);
|
||||||
|
sendFileUploadInfo.doPerform();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Failed to send file upload info", e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleData(byte[] data) {
|
||||||
|
try {
|
||||||
|
byte commandId = data[0];
|
||||||
|
if (commandId == 1) {
|
||||||
|
HuaweiTLV tlv = new HuaweiTLV();
|
||||||
|
tlv.parse(data, 1, data.length - 1);
|
||||||
|
byte operateMode = -1;
|
||||||
|
String majorVersion = null;
|
||||||
|
short minorVersion = -1;
|
||||||
|
short scheduleCount = -1;
|
||||||
|
|
||||||
|
if (tlv.contains(0x1))
|
||||||
|
operateMode = tlv.getByte(0x1);
|
||||||
|
if (tlv.contains(0x2))
|
||||||
|
majorVersion = tlv.getString(0x2).trim();
|
||||||
|
if (tlv.contains(0x3))
|
||||||
|
minorVersion = tlv.getShort(0x3);
|
||||||
|
if (tlv.contains(0x4))
|
||||||
|
scheduleCount = tlv.getShort(0x4);
|
||||||
|
|
||||||
|
LOG.info("Operate mode: {} Major: {} Minor: {} Schedule Count: {}", operateMode, majorVersion, minorVersion, scheduleCount);
|
||||||
|
|
||||||
|
// TODO: scheduleCount can be a max number of events to send, but I am not sure. Ignore it for now.
|
||||||
|
// NOTE: device can initiate calendar sync. So we need to check and answer properly.
|
||||||
|
final boolean syncEnabled = GBApplication.getDeviceSpecificSharedPrefs(manager.getSupportProvider().getDevice().getAddress()).getBoolean(PREF_SYNC_CALENDAR, false);
|
||||||
|
if(!syncEnabled) {
|
||||||
|
sendCalendarCmd((byte) 0x01, (byte) 0x07, null); //sync disabled
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operateMode != -1 && majorVersion != null && minorVersion != -1 && scheduleCount != -1) {
|
||||||
|
if (operateMode == 3) {
|
||||||
|
sendCalendarCmd((byte) 0x01, (byte) 0x03, null);
|
||||||
|
}
|
||||||
|
if (operateMode == 2 || operateMode == 3) {
|
||||||
|
|
||||||
|
//TODO:
|
||||||
|
//sendCalendarCmd((byte) 0x01, (byte) 0x06, null); //no permissions
|
||||||
|
|
||||||
|
//external calendar synchronization only supported on Harmony devices. I don't know how to deal with this.
|
||||||
|
if (!manager.getSupportProvider().getHuaweiCoordinator().supportsExternalCalendarService()) {
|
||||||
|
if (!sendCalendarFile(majorVersion, minorVersion, scheduleCount)) {
|
||||||
|
sendCalendarCmd((byte) 0x01, (byte) 0x04, null); //No sync required
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (commandId == 3) {
|
||||||
|
HuaweiTLV tlv = new HuaweiTLV();
|
||||||
|
tlv.parse(data, 1, data.length - 1);
|
||||||
|
//TODO: wearable sends this command if calendar event status changed.
|
||||||
|
// For example one of the quick action buttons on the reminders notification wes pressed.
|
||||||
|
// There are two buttons:
|
||||||
|
// Snooze - status 0
|
||||||
|
// Close - status 2
|
||||||
|
// It should be send to Android or stored to local GB database. And should be used during next synchronization.
|
||||||
|
// Should be saved to CalendarContract.CalendarAlerts, column "state".
|
||||||
|
// CalendarContract.CalendarAlertsColumns.STATE
|
||||||
|
// Values:
|
||||||
|
// STATE_DISMISSED = 2
|
||||||
|
// STATE_FIRED = 1
|
||||||
|
// STATE_SCHEDULED = 0
|
||||||
|
// Currently GB does not support CALENDAR_WRITE permission so it is not possible.
|
||||||
|
// Additional research required.
|
||||||
|
LOG.info("calendarRequest");
|
||||||
|
if (tlv.contains(0x1))
|
||||||
|
LOG.info("eventId: {}", tlv.getString(0x1).trim());
|
||||||
|
if (tlv.contains(0x2))
|
||||||
|
LOG.info("calendarId: {}", tlv.getString(0x2).trim());
|
||||||
|
if (tlv.contains(0x3))
|
||||||
|
LOG.info("accountName: {}", tlv.getString(0x3).trim());
|
||||||
|
if (tlv.contains(0x4))
|
||||||
|
LOG.info("accountType: {}", tlv.getString(0x4).trim());
|
||||||
|
if (tlv.contains(0x5))
|
||||||
|
LOG.info("state: {}", tlv.getByte(0x5)); //state: 0 - snooze, 2 - close. Quick actions on watch
|
||||||
|
if (tlv.contains(0x6))
|
||||||
|
LOG.info("eventUuid: {}", tlv.getString(0x6).trim());
|
||||||
|
} else {
|
||||||
|
LOG.info("Unknown command");
|
||||||
|
}
|
||||||
|
} catch (HuaweiPacket.MissingTagException e) {
|
||||||
|
LOG.error("P2P handle packet: tag is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.p2p;
|
||||||
|
|
||||||
|
public interface HuaweiP2PCallback {
|
||||||
|
void onResponse(int code, byte[] data);
|
||||||
|
}
|
@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiUploadManager;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiUploadManager;
|
||||||
@ -36,11 +37,16 @@ public class SendFileUploadChunk extends Request {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||||
return new FileUpload.FileNextChunkSend(this.paramsProvider).serializeFileChunk(
|
try {
|
||||||
huaweiUploadManager.getCurrentChunk(),
|
return new FileUpload.FileNextChunkSend(this.paramsProvider).serializeFileChunk(
|
||||||
huaweiUploadManager.getCurrentUploadPosition(),
|
huaweiUploadManager.getFileUploadInfo().getCurrentChunk(),
|
||||||
huaweiUploadManager.getUnitSize(),
|
huaweiUploadManager.getFileUploadInfo().getCurrentUploadPosition(),
|
||||||
huaweiUploadManager.getFileId()
|
huaweiUploadManager.getFileUploadInfo().getUnitSize(),
|
||||||
);
|
huaweiUploadManager.getFileUploadInfo().getFileId(),
|
||||||
|
huaweiUploadManager.getFileUploadInfo().isEncrypted()
|
||||||
|
);
|
||||||
|
} catch(HuaweiPacket.SerializeException e) {
|
||||||
|
throw new RequestCreationException(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,10 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiUploadM
|
|||||||
|
|
||||||
public class SendFileUploadHash extends Request{
|
public class SendFileUploadHash extends Request{
|
||||||
HuaweiUploadManager huaweiUploadManager;
|
HuaweiUploadManager huaweiUploadManager;
|
||||||
private byte fileId;
|
|
||||||
public SendFileUploadHash(HuaweiSupportProvider support,
|
public SendFileUploadHash(HuaweiSupportProvider support,
|
||||||
HuaweiUploadManager huaweiUploadManager) {
|
HuaweiUploadManager huaweiUploadManager) {
|
||||||
super(support);
|
super(support);
|
||||||
this.huaweiUploadManager = huaweiUploadManager;
|
this.huaweiUploadManager = huaweiUploadManager;
|
||||||
this.fileId = fileId;
|
|
||||||
this.serviceId = FileUpload.id;
|
this.serviceId = FileUpload.id;
|
||||||
this.commandId = FileUpload.FileHashSend.id;
|
this.commandId = FileUpload.FileHashSend.id;
|
||||||
this.addToResponse = false;
|
this.addToResponse = false;
|
||||||
@ -42,8 +40,8 @@ public class SendFileUploadHash extends Request{
|
|||||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||||
try {
|
try {
|
||||||
return new FileUpload.FileHashSend.Request(this.paramsProvider,
|
return new FileUpload.FileHashSend.Request(this.paramsProvider,
|
||||||
huaweiUploadManager.getFileSHA256(),
|
huaweiUploadManager.getFileUploadInfo().getFileSHA256(),
|
||||||
huaweiUploadManager.getFileId()
|
huaweiUploadManager.getFileUploadInfo().getFileId()
|
||||||
).serialize();
|
).serialize();
|
||||||
} catch (HuaweiPacket.CryptoException e) {
|
} catch (HuaweiPacket.CryptoException e) {
|
||||||
throw new RequestCreationException(e);
|
throw new RequestCreationException(e);
|
||||||
|
@ -33,16 +33,19 @@ public class SendFileUploadInfo extends Request{
|
|||||||
this.serviceId = FileUpload.id;
|
this.serviceId = FileUpload.id;
|
||||||
this.commandId = FileUpload.FileInfoSend.id;
|
this.commandId = FileUpload.FileInfoSend.id;
|
||||||
this.addToResponse = false;
|
this.addToResponse = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<byte[]> createRequest() throws RequestCreationException {
|
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||||
try {
|
try {
|
||||||
return new FileUpload.FileInfoSend.Request(this.paramsProvider,
|
return new FileUpload.FileInfoSend.Request(this.paramsProvider,
|
||||||
huaweiUploadManager.getFileSize(),
|
huaweiUploadManager.getFileUploadInfo().getFileSize(),
|
||||||
huaweiUploadManager.getFileName(),
|
huaweiUploadManager.getFileUploadInfo().getFileName(),
|
||||||
huaweiUploadManager.getFileType()
|
huaweiUploadManager.getFileUploadInfo().getFileType(),
|
||||||
|
huaweiUploadManager.getFileUploadInfo().getSrcPackage(),
|
||||||
|
huaweiUploadManager.getFileUploadInfo().getDstPackage(),
|
||||||
|
huaweiUploadManager.getFileUploadInfo().getSrcFingerprint(),
|
||||||
|
huaweiUploadManager.getFileUploadInfo().getDstFingerprint()
|
||||||
).serialize();
|
).serialize();
|
||||||
} catch (HuaweiPacket.CryptoException e) {
|
} catch (HuaweiPacket.CryptoException e) {
|
||||||
throw new RequestCreationException(e);
|
throw new RequestCreationException(e);
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.AccountRelated;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||||
|
|
||||||
|
public class SendP2PCommand extends Request {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SendAccountRequest.class);
|
||||||
|
|
||||||
|
private byte cmdId;
|
||||||
|
private short sequenceId;
|
||||||
|
private String srcPackage;
|
||||||
|
private String dstPackage;
|
||||||
|
private String srcFingerprint = null;
|
||||||
|
private String dstFingerprint = null;
|
||||||
|
private byte[] sendData = null;
|
||||||
|
private int sendCode = 0;
|
||||||
|
|
||||||
|
public SendP2PCommand(HuaweiSupportProvider support,
|
||||||
|
byte cmdId,
|
||||||
|
short sequenceId,
|
||||||
|
String srcPackage,
|
||||||
|
String dstPackage,
|
||||||
|
String srcFingerprint,
|
||||||
|
String dstFingerprint,
|
||||||
|
byte[] sendData,
|
||||||
|
int sendCode) {
|
||||||
|
super(support);
|
||||||
|
this.serviceId = P2P.id;
|
||||||
|
this.commandId = P2P.P2PCommand.id;
|
||||||
|
|
||||||
|
this.cmdId = cmdId;
|
||||||
|
this.sequenceId = sequenceId;
|
||||||
|
this.srcPackage = srcPackage;
|
||||||
|
this.dstPackage = dstPackage;
|
||||||
|
this.srcFingerprint = srcFingerprint;
|
||||||
|
this.dstFingerprint = dstFingerprint;
|
||||||
|
this.sendData = sendData;
|
||||||
|
this.sendCode = sendCode;
|
||||||
|
this.addToResponse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<byte[]> createRequest() throws RequestCreationException {
|
||||||
|
try {
|
||||||
|
return new P2P.P2PCommand.Request(paramsProvider, this.cmdId, this.sequenceId, this.srcPackage, this.dstPackage, this.srcFingerprint, this.dstFingerprint, this.sendData, this.sendCode).serialize();
|
||||||
|
} catch (HuaweiPacket.CryptoException e) {
|
||||||
|
throw new RequestCreationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,12 +29,15 @@ public class CalendarEvent {
|
|||||||
private final String location;
|
private final String location;
|
||||||
private final String calName;
|
private final String calName;
|
||||||
private final String calAccountName;
|
private final String calAccountName;
|
||||||
|
private final String calAccountType;
|
||||||
|
private final String calendarId;
|
||||||
private final String organizer;
|
private final String organizer;
|
||||||
private final int color;
|
private final int color;
|
||||||
private final boolean allDay;
|
private final boolean allDay;
|
||||||
|
private final String rrule;
|
||||||
private List<Long> remindersAbsoluteTs = new ArrayList<>();
|
private List<Long> remindersAbsoluteTs = new ArrayList<>();
|
||||||
|
|
||||||
public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName, String calAccountName, int color, boolean allDay, String organizer) {
|
public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName, String calAccountName, int color, boolean allDay, String organizer, String calAccountType, String calendarId, String rrule) {
|
||||||
this.begin = begin;
|
this.begin = begin;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -46,6 +49,9 @@ public class CalendarEvent {
|
|||||||
this.color = color;
|
this.color = color;
|
||||||
this.allDay = allDay;
|
this.allDay = allDay;
|
||||||
this.organizer = organizer;
|
this.organizer = organizer;
|
||||||
|
this.calAccountType = calAccountType;
|
||||||
|
this.calendarId = calendarId;
|
||||||
|
this.rrule = rrule;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getRemindersAbsoluteTs() {
|
public List<Long> getRemindersAbsoluteTs() {
|
||||||
@ -125,6 +131,18 @@ public class CalendarEvent {
|
|||||||
return allDay;
|
return allDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getCalAccountType() {
|
||||||
|
return calAccountType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCalendarId() {
|
||||||
|
return calendarId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRrule() {
|
||||||
|
return rrule;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (other instanceof CalendarEvent) {
|
if (other instanceof CalendarEvent) {
|
||||||
@ -140,7 +158,10 @@ public class CalendarEvent {
|
|||||||
(this.getColor() == e.getColor()) &&
|
(this.getColor() == e.getColor()) &&
|
||||||
(this.isAllDay() == e.isAllDay()) &&
|
(this.isAllDay() == e.isAllDay()) &&
|
||||||
Objects.equals(this.getOrganizer(), e.getOrganizer()) &&
|
Objects.equals(this.getOrganizer(), e.getOrganizer()) &&
|
||||||
Objects.equals(this.getRemindersAbsoluteTs(), e.getRemindersAbsoluteTs());
|
Objects.equals(this.getRemindersAbsoluteTs(), e.getRemindersAbsoluteTs()) &&
|
||||||
|
Objects.equals(this.getCalAccountType(), e.getCalAccountType()) &&
|
||||||
|
Objects.equals(this.getCalendarId(), e.getCalendarId()) &&
|
||||||
|
Objects.equals(this.getRrule(), e.getRrule());
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -160,6 +181,9 @@ public class CalendarEvent {
|
|||||||
result = 31 * result + Boolean.valueOf(allDay).hashCode();
|
result = 31 * result + Boolean.valueOf(allDay).hashCode();
|
||||||
result = 31 * result + Objects.hash(organizer);
|
result = 31 * result + Objects.hash(organizer);
|
||||||
result = 31 * result + Objects.hash(remindersAbsoluteTs);
|
result = 31 * result + Objects.hash(remindersAbsoluteTs);
|
||||||
|
result = 31 * result + Objects.hash(calAccountType);
|
||||||
|
result = 31 * result + Objects.hash(calendarId);
|
||||||
|
result = 31 * result + Objects.hash(rrule);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,10 @@ public class CalendarManager {
|
|||||||
CalendarContract.Calendars.ACCOUNT_NAME,
|
CalendarContract.Calendars.ACCOUNT_NAME,
|
||||||
Instances.CALENDAR_COLOR,
|
Instances.CALENDAR_COLOR,
|
||||||
Instances.ALL_DAY,
|
Instances.ALL_DAY,
|
||||||
Instances.EVENT_ID //needed for reminders
|
Instances.EVENT_ID, //needed for reminders
|
||||||
|
CalendarContract.Calendars.ACCOUNT_TYPE,
|
||||||
|
Instances.CALENDAR_ID,
|
||||||
|
Instances.RRULE
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int lookahead_days = 7;
|
private static final int lookahead_days = 7;
|
||||||
@ -126,6 +129,7 @@ public class CalendarManager {
|
|||||||
time.parse(evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.DURATION)));
|
time.parse(evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.DURATION)));
|
||||||
end = start + time.toMillis(false);
|
end = start + time.toMillis(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
CalendarEvent calEvent = new CalendarEvent(
|
CalendarEvent calEvent = new CalendarEvent(
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
@ -137,10 +141,12 @@ public class CalendarManager {
|
|||||||
evtCursor.getString(evtCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_NAME)),
|
evtCursor.getString(evtCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_NAME)),
|
||||||
evtCursor.getInt(evtCursor.getColumnIndexOrThrow(Instances.CALENDAR_COLOR)),
|
evtCursor.getInt(evtCursor.getColumnIndexOrThrow(Instances.CALENDAR_COLOR)),
|
||||||
!evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.ALL_DAY)).equals("0"),
|
!evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.ALL_DAY)).equals("0"),
|
||||||
evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.ORGANIZER))
|
evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.ORGANIZER)),
|
||||||
|
evtCursor.getString(evtCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_TYPE)),
|
||||||
|
evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.CALENDAR_ID)),
|
||||||
|
evtCursor.getString(evtCursor.getColumnIndexOrThrow(Instances.RRULE))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// Query reminders for this event
|
// Query reminders for this event
|
||||||
final Cursor reminderCursor = mContext.getContentResolver().query(
|
final Cursor reminderCursor = mContext.getContentResolver().query(
|
||||||
CalendarContract.Reminders.CONTENT_URI,
|
CalendarContract.Reminders.CONTENT_URI,
|
||||||
|
Loading…
Reference in New Issue
Block a user