mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Zepp OS: Extract services: user info, silent mode, find device
This commit is contained in:
-38
@@ -22,30 +22,14 @@ public class Huami2021Service {
|
||||
*/
|
||||
public static final short CHUNKED2021_ENDPOINT_WEATHER = 0x000e;
|
||||
public static final short CHUNKED2021_ENDPOINT_CONNECTION = 0x0015;
|
||||
public static final short CHUNKED2021_ENDPOINT_USER_INFO = 0x0017;
|
||||
public static final short CHUNKED2021_ENDPOINT_STEPS = 0x0016;
|
||||
public static final short CHUNKED2021_ENDPOINT_VIBRATION_PATTERNS = 0x0018;
|
||||
public static final short CHUNKED2021_ENDPOINT_WORKOUT = 0x0019;
|
||||
public static final short CHUNKED2021_ENDPOINT_FIND_DEVICE = 0x001a;
|
||||
public static final short CHUNKED2021_ENDPOINT_HEARTRATE = 0x001d;
|
||||
public static final short CHUNKED2021_ENDPOINT_BATTERY = 0x0029;
|
||||
public static final short CHUNKED2021_ENDPOINT_SILENT_MODE = 0x003b;
|
||||
public static final short CHUNKED2021_ENDPOINT_AUTH = 0x0082;
|
||||
public static final short CHUNKED2021_ENDPOINT_COMPAT = 0x0090;
|
||||
|
||||
/**
|
||||
* Find Device, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_FIND_DEVICE}.
|
||||
*/
|
||||
public static final byte FIND_BAND_START = 0x03;
|
||||
public static final byte FIND_BAND_ACK = 0x04;
|
||||
public static final byte FIND_BAND_STOP_FROM_PHONE = 0x06;
|
||||
public static final byte FIND_BAND_STOP_FROM_BAND = 0x07;
|
||||
public static final byte FIND_PHONE_START = 0x11;
|
||||
public static final byte FIND_PHONE_ACK = 0x12;
|
||||
public static final byte FIND_PHONE_STOP_FROM_BAND = 0x13;
|
||||
public static final byte FIND_PHONE_STOP_FROM_PHONE = 0x14;
|
||||
public static final byte FIND_PHONE_MODE = 0x15;
|
||||
|
||||
/**
|
||||
* Steps, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_STEPS}.
|
||||
*/
|
||||
@@ -67,22 +51,6 @@ public class Huami2021Service {
|
||||
public static final byte BATTERY_REQUEST = 0x03;
|
||||
public static final byte BATTERY_REPLY = 0x04;
|
||||
|
||||
/**
|
||||
* Silent Mode, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_SILENT_MODE}.
|
||||
*/
|
||||
public static final byte SILENT_MODE_CMD_CAPABILITIES_REQUEST = 0x01;
|
||||
public static final byte SILENT_MODE_CMD_CAPABILITIES_RESPONSE = 0x02;
|
||||
// Notify silent mode, from phone
|
||||
public static final byte SILENT_MODE_CMD_NOTIFY_BAND = 0x03;
|
||||
public static final byte SILENT_MODE_CMD_NOTIFY_BAND_ACK = 0x04;
|
||||
// Query silent mode on phone, from band
|
||||
public static final byte SILENT_MODE_CMD_QUERY = 0x05;
|
||||
public static final byte SILENT_MODE_CMD_REPLY = 0x06;
|
||||
// Set silent mode on phone, from band
|
||||
// After this, phone sends ACK + NOTIFY
|
||||
public static final byte SILENT_MODE_CMD_SET = 0x07;
|
||||
public static final byte SILENT_MODE_CMD_ACK = 0x08;
|
||||
|
||||
/**
|
||||
* Connection, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_CONNECTION}.
|
||||
*/
|
||||
@@ -120,12 +88,6 @@ public class Huami2021Service {
|
||||
public static final byte WEATHER_CMD_SET_DEFAULT_LOCATION = 0x09;
|
||||
public static final byte WEATHER_CMD_DEFAULT_LOCATION_ACK = 0x0a;
|
||||
|
||||
/**
|
||||
* User Info, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_USER_INFO}.
|
||||
*/
|
||||
public static final byte USER_INFO_CMD_SET = 0x01;
|
||||
public static final byte USER_INFO_CMD_SET_ACK = 0x02;
|
||||
|
||||
/**
|
||||
* Raw sensor control.
|
||||
*/
|
||||
|
||||
+1
-24
@@ -95,7 +95,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLift;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLiftSensitivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.DisconnectNotificationSetting;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppos.ZeppOsCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
|
||||
@@ -1649,29 +1648,7 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
|
||||
}
|
||||
|
||||
protected int getFindDeviceInterval() {
|
||||
final VibrationProfile findBand = HuamiCoordinator.getVibrationProfile(
|
||||
getDevice().getAddress(),
|
||||
HuamiVibrationPatternNotificationType.FIND_BAND,
|
||||
supportsDeviceDefaultVibrationProfiles()
|
||||
);
|
||||
int findDeviceInterval = 0;
|
||||
|
||||
if (findBand != null) {
|
||||
// It can be null if the device supports continuous find mode
|
||||
// If that's the case, this function shouldn't even have been called
|
||||
for(int len : findBand.getOnOffSequence())
|
||||
findDeviceInterval += len;
|
||||
|
||||
if(findBand.getRepeat() > 0)
|
||||
findDeviceInterval *= findBand.getRepeat();
|
||||
|
||||
if(findDeviceInterval > 10000) // 10 seconds, about as long as Mi Fit allows
|
||||
findDeviceInterval = 10000;
|
||||
} else {
|
||||
findDeviceInterval = 10000;
|
||||
}
|
||||
|
||||
return findDeviceInterval;
|
||||
return HuamiUtils.getFindDeviceInterval(getDevice(), supportsDeviceDefaultVibrationProfiles());
|
||||
}
|
||||
|
||||
protected void sendFindDeviceCommand(boolean start) {
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class HuamiUtils {
|
||||
private HuamiUtils() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
public static int getFindDeviceInterval(final GBDevice device,
|
||||
final boolean supportsDeviceDefaultVibrationProfiles) {
|
||||
final VibrationProfile findBand = HuamiCoordinator.getVibrationProfile(
|
||||
device.getAddress(),
|
||||
HuamiVibrationPatternNotificationType.FIND_BAND,
|
||||
supportsDeviceDefaultVibrationProfiles
|
||||
);
|
||||
int findDeviceInterval = 0;
|
||||
|
||||
if (findBand != null) {
|
||||
// It can be null if the device supports continuous find mode
|
||||
// If that's the case, this function shouldn't even have been called
|
||||
for (int len : findBand.getOnOffSequence())
|
||||
findDeviceInterval += len;
|
||||
|
||||
if (findBand.getRepeat() > 0)
|
||||
findDeviceInterval *= findBand.getRepeat();
|
||||
|
||||
if (findDeviceInterval > 10000) // 10 seconds, about as long as Mi Fit allows
|
||||
findDeviceInterval = 10000;
|
||||
} else {
|
||||
findDeviceInterval = 10000;
|
||||
}
|
||||
|
||||
return findDeviceInterval;
|
||||
}
|
||||
}
|
||||
+12
-222
@@ -19,8 +19,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos;
|
||||
import static org.apache.commons.lang3.ArrayUtils.subarray;
|
||||
import static java.lang.Thread.sleep;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Service.*;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.SUCCESS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_NAME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.fromUint16;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.fromUint8;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.mapTimeZone;
|
||||
@@ -45,7 +43,6 @@ import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -61,7 +58,6 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@@ -85,9 +81,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.loyaltycards.LoyaltyCard;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSilentMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppos.ZeppOsMapsInstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppos.ZeppOsMusicInstallHandler;
|
||||
@@ -138,6 +132,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.service
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsCalendarService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsCannedMessagesService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsDisplayItemsService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsFindDeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsHttpService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsLogsService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsLoyaltyCardService;
|
||||
@@ -153,6 +148,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.service
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsFtpServerService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsMorningUpdatesService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsPhoneService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsSilentModeService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsUserInfoService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsVoiceMemosService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsWatchfaceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services.ZeppOsWifiService;
|
||||
@@ -160,7 +157,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.SilentMode;
|
||||
|
||||
public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferService.Callback {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsSupport.class);
|
||||
@@ -200,6 +196,9 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
private final ZeppOsVoiceMemosService voiceMemosService = new ZeppOsVoiceMemosService(this);
|
||||
private final ZeppOsMapsService mapsService = new ZeppOsMapsService(this, httpService);
|
||||
private final ZeppOsMusicService musicService = new ZeppOsMusicService(this);
|
||||
private final ZeppOsFindDeviceService findDeviceService = new ZeppOsFindDeviceService(this);
|
||||
private final ZeppOsSilentModeService silentModeService = new ZeppOsSilentModeService(this);
|
||||
private final ZeppOsUserInfoService userInfoService = new ZeppOsUserInfoService(this);
|
||||
|
||||
private final Set<Short> mSupportedServices = new HashSet<>();
|
||||
// FIXME: We need to keep track of which services are encrypted for now, since not all of them were yet migrated to a service
|
||||
@@ -231,6 +230,9 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
put(musicService.getEndpoint(), musicService);
|
||||
put(voiceMemosService.getEndpoint(), voiceMemosService);
|
||||
put(mapsService.getEndpoint(), mapsService);
|
||||
put(findDeviceService.getEndpoint(), findDeviceService);
|
||||
put(silentModeService.getEndpoint(), silentModeService);
|
||||
put(userInfoService.getEndpoint(), userInfoService);
|
||||
}};
|
||||
|
||||
public ZeppOsSupport() {
|
||||
@@ -314,55 +316,19 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
voiceMemosService.requestList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void acknowledgeFindPhone() {
|
||||
LOG.info("Acknowledging find phone");
|
||||
|
||||
final byte[] cmd = new byte[]{FIND_PHONE_ACK, SUCCESS};
|
||||
|
||||
writeToChunked2021("ack find phone", CHUNKED2021_ENDPOINT_FIND_DEVICE, cmd, true);
|
||||
}
|
||||
|
||||
protected void stopFindPhone() {
|
||||
LOG.info("Stopping find phone");
|
||||
|
||||
writeToChunked2021("found phone", CHUNKED2021_ENDPOINT_FIND_DEVICE, FIND_PHONE_STOP_FROM_PHONE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFindDevice(final boolean start) {
|
||||
if (getCoordinator().supportsContinuousFindDevice()) {
|
||||
sendFindDeviceCommand(start);
|
||||
} else {
|
||||
// Vibrate band periodically
|
||||
super.onFindDevice(start);
|
||||
}
|
||||
findDeviceService.onFindDevice(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendFindDeviceCommand(boolean start) {
|
||||
final byte findBandCommand = start ? FIND_BAND_START : FIND_BAND_STOP_FROM_PHONE;
|
||||
|
||||
LOG.info("Sending find band {}", start);
|
||||
|
||||
try {
|
||||
final TransactionBuilder builder = performInitialized("find huami 2021");
|
||||
writeToChunked2021(builder, CHUNKED2021_ENDPOINT_FIND_DEVICE, findBandCommand, true);
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
LOG.error("error while sending find Huami 2021 device command", e);
|
||||
}
|
||||
findDeviceService.sendFindDeviceCommand(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFindPhone(final boolean start) {
|
||||
LOG.info("Find phone: {}", start);
|
||||
|
||||
findPhoneStarted = start;
|
||||
|
||||
if (!start) {
|
||||
stopFindPhone();
|
||||
}
|
||||
findDeviceService.onFindPhone(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -504,64 +470,6 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
.write(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ZeppOsSupport setUserInfo(final TransactionBuilder builder) {
|
||||
LOG.info("Attempting to set user info...");
|
||||
|
||||
final Prefs prefs = GBApplication.getPrefs();
|
||||
final Prefs devicePrefs = getDevicePrefs();
|
||||
|
||||
final String alias = prefs.getString(PREF_USER_NAME, null);
|
||||
final ActivityUser activityUser = new ActivityUser();
|
||||
final int height = activityUser.getHeightCm();
|
||||
final int weight = activityUser.getWeightKg();
|
||||
final LocalDate dateOfBirth = activityUser.getDateOfBirth();
|
||||
final int birthYear = dateOfBirth.getYear();
|
||||
final byte birthMonth = (byte) dateOfBirth.getMonthValue();
|
||||
final byte birthDay = (byte) dateOfBirth.getDayOfMonth();
|
||||
final String region = devicePrefs.getString(DeviceSettingsPreferenceConst.PREF_DEVICE_REGION, "unknown");
|
||||
|
||||
if (alias == null || weight == 0 || height == 0 || birthYear == 0) {
|
||||
LOG.warn("Unable to set user info, make sure it is set up");
|
||||
return this;
|
||||
}
|
||||
|
||||
byte genderByte = 2; // other
|
||||
switch (activityUser.getGender()) {
|
||||
case ActivityUser.GENDER_MALE:
|
||||
genderByte = 0;
|
||||
break;
|
||||
case ActivityUser.GENDER_FEMALE:
|
||||
genderByte = 1;
|
||||
}
|
||||
final int userid = alias.hashCode();
|
||||
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
baos.write(USER_INFO_CMD_SET);
|
||||
baos.write(new byte[]{0x4f, 0x07, 0x00, 0x00});
|
||||
baos.write(fromUint16(birthYear));
|
||||
baos.write(birthMonth);
|
||||
baos.write(birthDay);
|
||||
baos.write(genderByte);
|
||||
baos.write(fromUint16(height));
|
||||
baos.write(fromUint16(weight * 200));
|
||||
baos.write(BLETypeConversions.fromUint64(userid));
|
||||
baos.write(region.getBytes(StandardCharsets.UTF_8));
|
||||
baos.write(0);
|
||||
baos.write(0x09); // TODO ?
|
||||
baos.write(alias.getBytes(StandardCharsets.UTF_8));
|
||||
baos.write((byte) 0);
|
||||
|
||||
writeToChunked2021(builder, Huami2021Service.CHUNKED2021_ENDPOINT_USER_INFO, baos.toByteArray(), true);
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to send user info", e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ZeppOsSupport setPassword(final TransactionBuilder builder) {
|
||||
final boolean passwordEnabled = HuamiCoordinator.getPasswordEnabled(gbDevice.getAddress());
|
||||
@@ -1310,10 +1218,6 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
setVibrationPattern(builder, HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX + typeKey);
|
||||
}
|
||||
|
||||
// TODO move these to a service
|
||||
cannedMessagesService.requestCannedMessages(builder);
|
||||
alarmsService.requestAlarms(builder);
|
||||
|
||||
for (AbstractZeppOsService service : mServiceMap.values()) {
|
||||
if (mSupportedServices.contains(service.getEndpoint())) {
|
||||
// Only initialize supported services
|
||||
@@ -1321,11 +1225,6 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
}
|
||||
}
|
||||
|
||||
if (coordinator.supportsBluetoothPhoneCalls(gbDevice)) {
|
||||
phoneService.requestCapabilities(builder);
|
||||
phoneService.requestEnabled(builder);
|
||||
}
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
LOG.error("failed initializing device", e);
|
||||
@@ -1465,18 +1364,12 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
case CHUNKED2021_ENDPOINT_WORKOUT:
|
||||
handle2021Workout(payload);
|
||||
return;
|
||||
case CHUNKED2021_ENDPOINT_FIND_DEVICE:
|
||||
handle2021FindDevice(payload);
|
||||
return;
|
||||
case CHUNKED2021_ENDPOINT_HEARTRATE:
|
||||
handle2021HeartRate(payload);
|
||||
return;
|
||||
case CHUNKED2021_ENDPOINT_CONNECTION:
|
||||
handle2021Connection(payload);
|
||||
return;
|
||||
case CHUNKED2021_ENDPOINT_USER_INFO:
|
||||
handle2021UserInfo(payload);
|
||||
return;
|
||||
case CHUNKED2021_ENDPOINT_STEPS:
|
||||
handle2021Steps(payload);
|
||||
return;
|
||||
@@ -1486,9 +1379,6 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
case CHUNKED2021_ENDPOINT_BATTERY:
|
||||
handle2021Battery(payload);
|
||||
return;
|
||||
case CHUNKED2021_ENDPOINT_SILENT_MODE:
|
||||
handle2021SilentMode(payload);
|
||||
return;
|
||||
default:
|
||||
LOG.warn("Unhandled 2021 payload {}", String.format("0x%04x", type));
|
||||
}
|
||||
@@ -1532,56 +1422,6 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler to schedule the find phone event.
|
||||
*/
|
||||
private final Handler findPhoneHandler = new Handler();
|
||||
private boolean findPhoneStarted;
|
||||
|
||||
protected void handle2021FindDevice(final byte[] payload) {
|
||||
final GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
|
||||
|
||||
switch (payload[0]) {
|
||||
case FIND_BAND_ACK:
|
||||
LOG.info("Band acknowledged find band command");
|
||||
return;
|
||||
case FIND_PHONE_START:
|
||||
LOG.info("Find Phone Start");
|
||||
acknowledgeFindPhone(); // FIXME: Premature, but the band will only send the mode after we ack
|
||||
|
||||
// Delay the find phone start, because we might get the FIND_PHONE_MODE
|
||||
findPhoneHandler.postDelayed(() -> {
|
||||
findPhoneEvent.event = GBDeviceEventFindPhone.Event.START;
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
}, 1500);
|
||||
|
||||
break;
|
||||
case FIND_BAND_STOP_FROM_BAND:
|
||||
LOG.info("Find Band Stop from Band");
|
||||
break;
|
||||
case FIND_PHONE_STOP_FROM_BAND:
|
||||
LOG.info("Find Phone Stop");
|
||||
findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP;
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
break;
|
||||
case FIND_PHONE_MODE:
|
||||
findPhoneHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
final int mode = payload[1] & 0xff; // 0 to only vibrate, 1 to ring
|
||||
LOG.info("Find Phone Mode: {}", mode);
|
||||
if (findPhoneStarted) {
|
||||
// Already started, just change the mode
|
||||
findPhoneEvent.event = mode == 1 ? GBDeviceEventFindPhone.Event.RING : GBDeviceEventFindPhone.Event.VIBRATE;
|
||||
} else {
|
||||
findPhoneEvent.event = mode == 1 ? GBDeviceEventFindPhone.Event.START : GBDeviceEventFindPhone.Event.START_VIBRATE;
|
||||
}
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unexpected find phone byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
}
|
||||
|
||||
protected void handle2021HeartRate(final byte[] payload) {
|
||||
switch (payload[0]) {
|
||||
case HEART_RATE_CMD_REALTIME_ACK:
|
||||
@@ -1636,17 +1476,6 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
LOG.warn("Unexpected connection payload byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
|
||||
protected void handle2021UserInfo(final byte[] payload) {
|
||||
//noinspection SwitchStatementWithTooFewBranches
|
||||
switch (payload[0]) {
|
||||
case USER_INFO_CMD_SET_ACK:
|
||||
LOG.info("Got user info set ack, status = {}", payload[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.warn("Unexpected user info payload byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
|
||||
protected void handle2021Steps(final byte[] payload) {
|
||||
switch (payload[0]) {
|
||||
case STEPS_CMD_REPLY:
|
||||
@@ -1698,45 +1527,6 @@ public class ZeppOsSupport extends HuamiSupport implements ZeppOsFileTransferSer
|
||||
handleGBDeviceEvent(batteryInfo.toDeviceEvent());
|
||||
}
|
||||
|
||||
protected void handle2021SilentMode(final byte[] payload) {
|
||||
switch (payload[0]) {
|
||||
case SILENT_MODE_CMD_NOTIFY_BAND_ACK:
|
||||
LOG.info("Band acknowledged current phone silent mode, status = {}", payload[1]);
|
||||
return;
|
||||
case SILENT_MODE_CMD_QUERY:
|
||||
LOG.info("Got silent mode query from band");
|
||||
sendPhoneSilentMode(SilentMode.isPhoneInSilenceMode(getDevice().getAddress()));
|
||||
return;
|
||||
case SILENT_MODE_CMD_SET:
|
||||
LOG.info("Band setting silent mode = {}", payload[1]);
|
||||
final boolean silentModeEnabled = (payload[1] == 1);
|
||||
ackSilentModeSet();
|
||||
sendPhoneSilentMode(silentModeEnabled);
|
||||
evaluateGBDeviceEvent(new GBDeviceEventSilentMode(silentModeEnabled));
|
||||
return;
|
||||
default:
|
||||
LOG.warn("Unexpected silent mode payload byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
}
|
||||
|
||||
private void ackSilentModeSet() {
|
||||
writeToChunked2021(
|
||||
"ack silent mode set",
|
||||
CHUNKED2021_ENDPOINT_SILENT_MODE,
|
||||
new byte[]{SILENT_MODE_CMD_ACK, 0x01},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private void sendPhoneSilentMode(final boolean enabled) {
|
||||
writeToChunked2021(
|
||||
"send phone silent mode to band",
|
||||
CHUNKED2021_ENDPOINT_SILENT_MODE,
|
||||
new byte[]{SILENT_MODE_CMD_NOTIFY_BAND, bool(enabled)},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileUploadFinish(final boolean success) {
|
||||
LOG.warn("Unexpected file upload finish: {}", success);
|
||||
|
||||
+5
@@ -98,6 +98,11 @@ public class ZeppOsAlarmsService extends AbstractZeppOsService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final TransactionBuilder builder) {
|
||||
requestAlarms(builder);
|
||||
}
|
||||
|
||||
private void requestAlarms() {
|
||||
try {
|
||||
final TransactionBuilder builder = new TransactionBuilder("request alarms");
|
||||
|
||||
+5
@@ -98,6 +98,11 @@ public class ZeppOsCannedMessagesService extends AbstractZeppOsService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final TransactionBuilder builder) {
|
||||
requestCannedMessages(builder);
|
||||
}
|
||||
|
||||
public void setCannedMessages(final CannedMessagesSpec cannedMessagesSpec) {
|
||||
if (cannedMessagesSpec.type != CannedMessagesSpec.TYPE_GENERIC) {
|
||||
LOG.warn("Got unsupported canned messages type: {}", cannedMessagesSpec.type);
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
/* Copyright (C) 2025 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.ZeppOsSupport;
|
||||
|
||||
public class ZeppOsFindDeviceService extends AbstractZeppOsService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsFindDeviceService.class);
|
||||
|
||||
private static final short ENDPOINT = 0x001a;
|
||||
|
||||
public static final byte CMD_CAPABILITIES_REQUEST = 0x01;
|
||||
public static final byte CMD_CAPABILITIES_RESPONSE = 0x02;
|
||||
public static final byte FIND_BAND_START = 0x03;
|
||||
public static final byte FIND_BAND_ACK = 0x04;
|
||||
public static final byte FIND_BAND_STOP_FROM_PHONE = 0x06;
|
||||
public static final byte FIND_BAND_STOP_FROM_BAND = 0x07;
|
||||
public static final byte FIND_PHONE_START = 0x11;
|
||||
public static final byte FIND_PHONE_ACK = 0x12;
|
||||
public static final byte FIND_PHONE_STOP_FROM_BAND = 0x13;
|
||||
public static final byte FIND_PHONE_STOP_FROM_PHONE = 0x14;
|
||||
public static final byte FIND_PHONE_MODE = 0x15;
|
||||
|
||||
private final Handler findWatchHandler = new Handler();
|
||||
private boolean findingWatch = false;
|
||||
|
||||
private final Handler findPhoneHandler = new Handler();
|
||||
private boolean findPhoneStarted;
|
||||
|
||||
public ZeppOsFindDeviceService(final ZeppOsSupport support) {
|
||||
super(support, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getEndpoint() {
|
||||
return ENDPOINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(final byte[] payload) {
|
||||
final GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
|
||||
|
||||
switch (payload[0]) {
|
||||
case CMD_CAPABILITIES_RESPONSE:
|
||||
// mb7: 02:01:01
|
||||
// active2 / gtr4: 02:01:02
|
||||
break;
|
||||
case FIND_BAND_ACK:
|
||||
LOG.info("Band acknowledged find band command");
|
||||
|
||||
final boolean supportsContinuous = getCoordinator().supportsContinuousFindDevice();
|
||||
if (findingWatch && !supportsContinuous) {
|
||||
findWatchHandler.postDelayed(() -> {
|
||||
LOG.debug("Triggering find device vibration");
|
||||
sendFindDeviceCommand(true);
|
||||
}, HuamiUtils.getFindDeviceInterval(getSupport().getDevice(), true));
|
||||
}
|
||||
|
||||
return;
|
||||
case FIND_PHONE_START:
|
||||
LOG.info("Find Phone Start");
|
||||
acknowledgeFindPhone(); // FIXME: Premature, but the band will only send the mode after we ack
|
||||
|
||||
// Delay the find phone start, because we might get the FIND_PHONE_MODE
|
||||
findPhoneHandler.postDelayed(() -> {
|
||||
findPhoneEvent.event = GBDeviceEventFindPhone.Event.START;
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
}, 1500);
|
||||
|
||||
break;
|
||||
case FIND_BAND_STOP_FROM_BAND:
|
||||
LOG.info("Find Band Stop from Band");
|
||||
break;
|
||||
case FIND_PHONE_STOP_FROM_BAND:
|
||||
LOG.info("Find Phone Stop");
|
||||
findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP;
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
break;
|
||||
case FIND_PHONE_MODE:
|
||||
findPhoneHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
final int mode = payload[1] & 0xff; // 0 to only vibrate, 1 to ring
|
||||
LOG.info("Find Phone Mode: {}", mode);
|
||||
if (findPhoneStarted) {
|
||||
// Already started, just change the mode
|
||||
findPhoneEvent.event = mode == 1 ? GBDeviceEventFindPhone.Event.RING : GBDeviceEventFindPhone.Event.VIBRATE;
|
||||
} else {
|
||||
findPhoneEvent.event = mode == 1 ? GBDeviceEventFindPhone.Event.START : GBDeviceEventFindPhone.Event.START_VIBRATE;
|
||||
}
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unexpected find phone byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final TransactionBuilder builder) {
|
||||
write(builder, CMD_CAPABILITIES_REQUEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
findWatchHandler.removeCallbacksAndMessages(null);
|
||||
findPhoneHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
public void onFindDevice(final boolean start) {
|
||||
LOG.debug("onFindDevice {}", start);
|
||||
findWatchHandler.removeCallbacksAndMessages(null);
|
||||
findingWatch = start;
|
||||
sendFindDeviceCommand(start);
|
||||
}
|
||||
|
||||
public void onFindPhone(final boolean start) {
|
||||
LOG.info("Find phone: {}", start);
|
||||
|
||||
findPhoneStarted = start;
|
||||
|
||||
if (!start) {
|
||||
stopFindPhone();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME should be private?
|
||||
public void sendFindDeviceCommand(final boolean start) {
|
||||
final byte findBandCommand = start ? FIND_BAND_START : FIND_BAND_STOP_FROM_PHONE;
|
||||
|
||||
LOG.info("Sending find band {}", start);
|
||||
|
||||
write("find huami 2021", findBandCommand);
|
||||
}
|
||||
|
||||
private void acknowledgeFindPhone() {
|
||||
LOG.info("Acknowledging find phone");
|
||||
|
||||
write("ack find phone", new byte[]{FIND_PHONE_ACK, 0x01 /* success */});
|
||||
}
|
||||
|
||||
private void stopFindPhone() {
|
||||
LOG.info("Stopping find phone");
|
||||
|
||||
write("found phone", FIND_PHONE_STOP_FROM_PHONE);
|
||||
}
|
||||
}
|
||||
+4
@@ -131,6 +131,10 @@ public class ZeppOsPhoneService extends AbstractZeppOsService {
|
||||
|
||||
@Override
|
||||
public void initialize(final TransactionBuilder builder) {
|
||||
if (getCoordinator().supportsBluetoothPhoneCalls(getSupport().getDevice())) {
|
||||
requestCapabilities(builder);
|
||||
requestEnabled(builder);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSupported() {
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/* Copyright (C) 2025 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSilentMode;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.ZeppOsSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.SilentMode;
|
||||
|
||||
public class ZeppOsSilentModeService extends AbstractZeppOsService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsSilentModeService.class);
|
||||
|
||||
private static final short ENDPOINT = 0x003b;
|
||||
|
||||
public static final byte SILENT_MODE_CMD_CAPABILITIES_REQUEST = 0x01;
|
||||
public static final byte SILENT_MODE_CMD_CAPABILITIES_RESPONSE = 0x02;
|
||||
// Notify silent mode, from phone
|
||||
public static final byte SILENT_MODE_CMD_NOTIFY_BAND = 0x03;
|
||||
public static final byte SILENT_MODE_CMD_NOTIFY_BAND_ACK = 0x04;
|
||||
// Query silent mode on phone, from band
|
||||
public static final byte SILENT_MODE_CMD_QUERY = 0x05;
|
||||
public static final byte SILENT_MODE_CMD_REPLY = 0x06;
|
||||
// Set silent mode on phone, from band
|
||||
// After this, phone sends ACK + NOTIFY
|
||||
public static final byte SILENT_MODE_CMD_SET = 0x07;
|
||||
public static final byte SILENT_MODE_CMD_ACK = 0x08;
|
||||
|
||||
public ZeppOsSilentModeService(final ZeppOsSupport support) {
|
||||
super(support, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getEndpoint() {
|
||||
return ENDPOINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(final byte[] payload) {
|
||||
switch (payload[0]) {
|
||||
case SILENT_MODE_CMD_NOTIFY_BAND_ACK:
|
||||
LOG.info("Band acknowledged current phone silent mode, status = {}", payload[1]);
|
||||
return;
|
||||
case SILENT_MODE_CMD_QUERY:
|
||||
LOG.info("Got silent mode query from band");
|
||||
sendPhoneSilentMode(SilentMode.isPhoneInSilenceMode(getSupport().getDevice().getAddress()));
|
||||
return;
|
||||
case SILENT_MODE_CMD_SET:
|
||||
LOG.info("Band setting silent mode = {}", payload[1]);
|
||||
final boolean silentModeEnabled = (payload[1] == 1);
|
||||
ackSilentModeSet();
|
||||
sendPhoneSilentMode(silentModeEnabled);
|
||||
evaluateGBDeviceEvent(new GBDeviceEventSilentMode(silentModeEnabled));
|
||||
return;
|
||||
default:
|
||||
LOG.warn("Unexpected silent mode payload byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
}
|
||||
|
||||
private void ackSilentModeSet() {
|
||||
write("ack silent mode set", new byte[]{SILENT_MODE_CMD_ACK, 0x01});
|
||||
}
|
||||
|
||||
private void sendPhoneSilentMode(final boolean enabled) {
|
||||
write("send phone silent mode to band", new byte[]{SILENT_MODE_CMD_NOTIFY_BAND, bool(enabled)});
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/* Copyright (C) 2025 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.services;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_REGION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_DATE_OF_BIRTH;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GENDER;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_NAME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_WEIGHT_KG;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions.fromUint16;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.AbstractZeppOsService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos.ZeppOsSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class ZeppOsUserInfoService extends AbstractZeppOsService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZeppOsUserInfoService.class);
|
||||
|
||||
private static final short ENDPOINT = 0x0017;
|
||||
|
||||
public static final byte USER_INFO_CMD_SET = 0x01;
|
||||
public static final byte USER_INFO_CMD_SET_ACK = 0x02;
|
||||
|
||||
public ZeppOsUserInfoService(final ZeppOsSupport support) {
|
||||
super(support, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getEndpoint() {
|
||||
return ENDPOINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(final byte[] payload) {
|
||||
//noinspection SwitchStatementWithTooFewBranches
|
||||
switch (payload[0]) {
|
||||
case USER_INFO_CMD_SET_ACK:
|
||||
LOG.info("Got user info set ack, status = {}", payload[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.warn("Unexpected user info payload byte {}", String.format("0x%02x", payload[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final TransactionBuilder builder) {
|
||||
setUserInfo(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSendConfiguration(final String config, final Prefs prefs) {
|
||||
switch (config) {
|
||||
case PREF_USER_DATE_OF_BIRTH:
|
||||
case PREF_USER_NAME:
|
||||
case PREF_USER_WEIGHT_KG:
|
||||
case PREF_USER_HEIGHT_CM:
|
||||
case PREF_USER_GENDER:
|
||||
case PREF_DEVICE_REGION:
|
||||
final TransactionBuilder builder = getSupport().createTransactionBuilder("set user info");
|
||||
setUserInfo(builder);
|
||||
builder.queue(getSupport().getQueue());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setUserInfo(final TransactionBuilder builder) {
|
||||
LOG.info("Attempting to set user info...");
|
||||
|
||||
final Prefs prefs = GBApplication.getPrefs();
|
||||
final Prefs devicePrefs = getDevicePrefs();
|
||||
|
||||
final String alias = prefs.getString(PREF_USER_NAME, null);
|
||||
final ActivityUser activityUser = new ActivityUser();
|
||||
final int height = activityUser.getHeightCm();
|
||||
final int weight = activityUser.getWeightKg();
|
||||
final LocalDate dateOfBirth = activityUser.getDateOfBirth();
|
||||
final int birthYear = dateOfBirth.getYear();
|
||||
final byte birthMonth = (byte) dateOfBirth.getMonthValue();
|
||||
final byte birthDay = (byte) dateOfBirth.getDayOfMonth();
|
||||
final String region = devicePrefs.getString(PREF_DEVICE_REGION, "unknown");
|
||||
|
||||
if (alias == null || weight == 0 || height == 0 || birthYear == 0) {
|
||||
LOG.warn("Unable to set user info, make sure it is set up");
|
||||
return;
|
||||
}
|
||||
|
||||
byte genderByte = 2; // other
|
||||
switch (activityUser.getGender()) {
|
||||
case ActivityUser.GENDER_MALE:
|
||||
genderByte = 0;
|
||||
break;
|
||||
case ActivityUser.GENDER_FEMALE:
|
||||
genderByte = 1;
|
||||
}
|
||||
final int userid = alias.hashCode();
|
||||
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
baos.write(USER_INFO_CMD_SET);
|
||||
baos.write(new byte[]{0x4f, 0x07, 0x00, 0x00});
|
||||
baos.write(fromUint16(birthYear));
|
||||
baos.write(birthMonth);
|
||||
baos.write(birthDay);
|
||||
baos.write(genderByte);
|
||||
baos.write(fromUint16(height));
|
||||
baos.write(fromUint16(weight * 200));
|
||||
baos.write(BLETypeConversions.fromUint64(userid));
|
||||
baos.write(region.getBytes(StandardCharsets.UTF_8));
|
||||
baos.write(0);
|
||||
baos.write(0x09); // TODO ?
|
||||
baos.write(alias.getBytes(StandardCharsets.UTF_8));
|
||||
baos.write((byte) 0);
|
||||
|
||||
write(builder, baos.toByteArray());
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to send user info", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user