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

Huawei: Contacts uploading support

This commit is contained in:
Me7c7 2024-09-06 10:58:49 +03:00 committed by José Rebelo
parent 287fdd87d8
commit fc450882cb
10 changed files with 301 additions and 8 deletions

View File

@ -169,6 +169,11 @@ public abstract class HuaweiBRCoordinator extends AbstractBLClassicDeviceCoordin
return huaweiCoordinator.getAlarmSlotCount(device);
}
@Override
public int getContactsSlotCount(GBDevice device) {
return huaweiCoordinator.getContactsSlotCount(device);
}
@Override
public boolean supportsActivityDataFetching() {
return true;

View File

@ -65,6 +65,8 @@ public class HuaweiCoordinator {
private boolean transactionCrypted=true;
private int maxContactsCount = 0;
public HuaweiCoordinator(HuaweiCoordinatorSupplier parent) {
this.parent = parent;
for (String key : getCapabilitiesSharedPreferences().getAll().keySet()) {
@ -84,6 +86,9 @@ public class HuaweiCoordinator {
key,
GB.hexdump(Notifications.defaultConstraints)
)));
if (key.equals("maxContactsCount"))
this.maxContactsCount = getCapabilitiesSharedPreferences().getInt(key, 0);
}
}
}
@ -121,6 +126,11 @@ public class HuaweiCoordinator {
getCapabilitiesSharedPreferences().edit().putString("notificationConstraints", GB.hexdump(constraints.array())).apply();
}
public void saveMaxContactsCount(int maxContactsCount) {
this.maxContactsCount = maxContactsCount;
getCapabilitiesSharedPreferences().edit().putInt("maxContactsCount", maxContactsCount).apply();
}
public void addCommandsForService(int service, byte[] commands) {
if (!commandsPerService.containsKey(service)) {
saveCommandsForService(service, commands);
@ -232,6 +242,11 @@ public class HuaweiCoordinator {
if (supportsCameraRemote())
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_camera_remote);
//Contacts
if (getContactsSlotCount(device) > 0) {
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_contacts);
}
// Time
if (supportsDateFormat()) {
final List<Integer> dateTime = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DATE_TIME);
@ -297,6 +312,10 @@ public class HuaweiCoordinator {
return supportsCommandForService(0x01, 0x29) && CameraActivity.supportsCamera();
}
public boolean supportsContacts() {
return supportsCommandForService(0x03, 0x1);
}
public boolean supportsAcceptAgreement() {
return supportsCommandForService(0x01, 0x30);
}
@ -550,6 +569,10 @@ public class HuaweiCoordinator {
return alarmCount;
}
public int getContactsSlotCount(GBDevice device) {
return supportsContacts()?maxContactsCount:0;
}
public void setTransactionCrypted(boolean crypted) {
this.transactionCrypted = crypted;
}

View File

@ -175,6 +175,11 @@ public abstract class HuaweiLECoordinator extends AbstractBLEDeviceCoordinator i
return huaweiCoordinator.getAlarmSlotCount(device);
}
@Override
public int getContactsSlotCount(GBDevice device) {
return huaweiCoordinator.getContactsSlotCount(device);
}
@Override
public boolean supportsActivityDataFetching() {
return true;

View File

@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.AccountRelate
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Calls;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.CameraRemote;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Contacts;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService0A;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileDownloadService2C;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime;
@ -476,6 +477,15 @@ public class HuaweiPacket {
default:
return this;
}
case Contacts.id:
switch (this.commandId) {
case Contacts.ContactsSet.id:
return new Contacts.ContactsSet.Response(paramsProvider).fromPacket(this);
case Contacts.ContactsCount.id:
return new Contacts.ContactsCount.Response(paramsProvider).fromPacket(this);
}
this.isEncrypted = this.attemptDecrypt(); // Helps with debugging
return this;
case Calls.id:
if (this.commandId == Calls.AnswerCallResponse.id)
return new Calls.AnswerCallResponse(paramsProvider).fromPacket(this);

View File

@ -0,0 +1,93 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets;
import java.util.ArrayList;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
public class Contacts {
public static final byte id = 0x03;
public static class ContactsSet {
public static final byte id = 0x01;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, ArrayList<? extends Contact> contacts, int maxCount) {
super(paramsProvider);
this.serviceId = Contacts.id;
this.commandId = id;
HuaweiTLV contacts_tlv = new HuaweiTLV();
for(int i = 0; i < maxCount; i++) {
HuaweiTLV contact= new HuaweiTLV()
.put(0x03, (byte) (i + 1));
if(i < contacts.size()) {
contact.put(0x4, contacts.get(i).getName())
.put(0x85, new HuaweiTLV().put(0x86, new HuaweiTLV().put(0x7, "Mobile").put(0x8, contacts.get(i).getNumber())));
}
contacts_tlv.put(0x82, contact);
}
this.tlv = new HuaweiTLV()
.put(0x81, contacts_tlv);
this.complete = true;
this.isEncrypted = true;
}
}
public static class Response extends HuaweiPacket {
public boolean isOk;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Contacts.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
isOk = this.tlv.getInteger(0x7f) == 0x000186A0;
}
}
}
public static class ContactsCount {
public static final byte id = 0x02;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Contacts.id;
this.commandId = id;
this.tlv = new HuaweiTLV()
.put(0x1)
.put(0x2);
this.complete = true;
this.isEncrypted = true;
}
}
public static class Response extends HuaweiPacket {
public int maxCount;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Contacts.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
maxCount = this.tlv.getByte(0x01);
}
}
}
}

View File

@ -24,12 +24,14 @@ import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@ -170,6 +172,11 @@ public class HuaweiBRSupport extends AbstractBTBRDeviceSupport {
supportProvider.onCameraStatusChange(event, filename);
}
@Override
public void onSetContacts(ArrayList<? extends Contact> contacts) {
supportProvider.onSetContacts(contacts);
}
@Override
public void dispose() {
supportProvider.dispose();

View File

@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemo
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@ -178,6 +179,11 @@ public class HuaweiLESupport extends AbstractBTLEDeviceSupport {
supportProvider.onCameraStatusChange(event, filename);
}
@Override
public void onSetContacts(ArrayList<? extends Contact> contacts) {
supportProvider.onSetContacts(contacts);
}
@Override
public void dispose() {
supportProvider.dispose();

View File

@ -85,6 +85,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@ -94,6 +95,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
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.GetContactsCount;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetEventAlarmList;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetGpsParameterRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetNotificationConstraintsRequest;
@ -105,6 +107,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Send
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendGpsAndTimeToDeviceRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendGpsDataRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendSetContactsRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWeatherCurrentRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotifyHeartRateCapabilityRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendNotifyRestHeartRateCapabilityRequest;
@ -791,6 +794,7 @@ public class HuaweiSupportProvider {
if (getHuaweiCoordinator().supportsActivityReminder()) {
setActivityReminder();
}
if (getHuaweiCoordinator().supportsTruSleep()) {
setTrusleep();
}
@ -803,20 +807,55 @@ public class HuaweiSupportProvider {
getNotificationConstraintsReq.doPerform();
}
if (getHuaweiCoordinator().supportsWatchfaceParams()) {
GetWatchfaceParams getWatchfaceParams = new GetWatchfaceParams(this);
getWatchfaceParams.doPerform();
}
if (getHuaweiCoordinator().supportsCameraRemote() && GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREF_CAMERA_REMOTE, false)) {
SendCameraRemoteSetupEvent sendCameraRemoteSetupEvent = new SendCameraRemoteSetupEvent(this, CameraRemote.CameraRemoteSetup.Request.Event.ENABLE_CAMERA);
sendCameraRemoteSetupEvent.doPerform();
}
if (getHuaweiCoordinator().supportsAppParams()) {
GetAppInfoParams getAppInfoParams = new GetAppInfoParams(this);
getAppInfoParams.doPerform();
// FIXME: Limit number of simultaneous commands
// Huawei watch, for example the Watch GT4 has limited buffer for incoming commands.
// So sending a lot of command broke connection or cause that watch does not answer to requests.
// To avoid this issue I perform some commands in the chain, but we should limit number of simultaneous commands
RequestCallback contactsCallback = new RequestCallback() {
@Override
public void call() {
if (getHuaweiCoordinator().supportsContacts()) {
GetContactsCount getContactsCount = new GetContactsCount(HuaweiSupportProvider.this);
try {
getContactsCount.doPerform();
} catch (IOException e) {
LOG.error("Error perform contacts count request", e);
}
}
}
};
RequestCallback appsCallback= new RequestCallback() {
@Override
public void call() {
if (getHuaweiCoordinator().supportsAppParams()) {
GetAppInfoParams getAppInfoParams = new GetAppInfoParams(HuaweiSupportProvider.this);
getAppInfoParams.setFinalizeReq(contactsCallback);
try {
getAppInfoParams.doPerform();
} catch (IOException e) {
LOG.error("Error perform app info request", e);
}
} else {
contactsCallback.call();
}
}
};
if (getHuaweiCoordinator().supportsWatchfaceParams()) {
GetWatchfaceParams getWatchfaceParams = new GetWatchfaceParams(this);
getWatchfaceParams.setFinalizeReq(appsCallback);
getWatchfaceParams.doPerform();
} else {
appsCallback.call();
}
} catch (IOException e) {
GB.toast(getContext(), "Initialize dynamic services of Huawei device failed", Toast.LENGTH_SHORT, GB.ERROR,
e);
@ -2103,6 +2142,22 @@ public class HuaweiSupportProvider {
}
}
public void onSetContacts(ArrayList<? extends Contact> contacts) {
SendSetContactsRequest sendSetContactsRequest = new SendSetContactsRequest(
this,
contacts,
this.getHuaweiCoordinator().getContactsSlotCount(getDevice())
);
try {
sendSetContactsRequest.doPerform();
} catch (IOException e) {
// TODO: Use translatable string
GB.toast(context, "Failed to set contacts", Toast.LENGTH_SHORT, GB.ERROR, e);
LOG.error("Failed to send set contacts request", e);
}
}
public boolean startBatteryRunnerDelayed() {
int interval_minutes = GBApplication.getDevicePrefs(gbDevice).getBatteryPollingIntervalMinutes();
int interval = interval_minutes * 60 * 1000;

View File

@ -0,0 +1,40 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Contacts;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class GetContactsCount extends Request {
private static final Logger LOG = LoggerFactory.getLogger(GetContactsCount.class);
public GetContactsCount(HuaweiSupportProvider support) {
super(support);
this.serviceId = Contacts.id;
this.commandId = Contacts.ContactsCount.id;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
return new Contacts.ContactsCount.Request(paramsProvider).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
@Override
protected void processResponse() throws ResponseParseException {
LOG.debug("handle contacts count");
if (!(receivedPacket instanceof Contacts.ContactsCount.Response))
throw new ResponseTypeMismatchException(receivedPacket, Contacts.ContactsCount.Response.class);
int count = ((Contacts.ContactsCount.Response) receivedPacket).maxCount;
this.supportProvider.getHuaweiCoordinator().saveMaxContactsCount(count);
}
}

View File

@ -0,0 +1,49 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Contacts;
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
public class SendSetContactsRequest extends Request {
private static final Logger LOG = LoggerFactory.getLogger(SendSetContactsRequest.class);
private final ArrayList<? extends Contact> contacts;
int maxCount;
public SendSetContactsRequest(HuaweiSupportProvider support,ArrayList<? extends Contact> contacts, int maxCount) {
super(support);
this.serviceId = Contacts.id;
this.commandId = Contacts.ContactsSet.id;
this.contacts = contacts;
this.maxCount = maxCount;
}
@Override
protected List<byte[]> createRequest() throws RequestCreationException {
try {
return new Contacts.ContactsSet.Request(paramsProvider, this.contacts, this.maxCount).serialize();
} catch (HuaweiPacket.CryptoException e) {
throw new RequestCreationException(e);
}
}
@Override
protected void processResponse() {
if (receivedPacket instanceof Contacts.ContactsSet.Response) {
if (((Contacts.ContactsSet.Response) receivedPacket).isOk) {
LOG.debug("Contacts set");
} else {
LOG.warn("Error set contacts");
}
} else {
LOG.error("Set Contacts response is not of type ContactsSet response");
}
}
}