2019-10-07 17:50:32 +02:00
|
|
|
// TODO: GB sometimes fails to connect until a connection with WearFit was made. This must be caused
|
2019-10-08 13:37:25 +02:00
|
|
|
// TODO: by GB, not by makibes hr3 support. Charging the watch or attempting to pair might also
|
|
|
|
// TODO: help. This needs further research.
|
2019-10-07 17:50:32 +02:00
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
// TODO: Where can I view today's steps in GB?
|
2019-10-07 15:28:54 +02:00
|
|
|
// TODO: GB accumulates all step samples, even if they're part of the same day.
|
2019-10-03 12:28:20 +02:00
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
// TODO: All the commands that aren't supported by GB should be added to device specific settings.
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
// TODO: It'd be cool if we could change the language. There's no official way to do so, but the
|
2019-10-06 15:46:15 +02:00
|
|
|
// TODO: watch is sold as chinese/english. Screen on time would be nice too.
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.makibeshr3;
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
import android.bluetooth.BluetoothGatt;
|
2019-10-02 22:57:31 +02:00
|
|
|
import android.bluetooth.BluetoothGattCharacteristic;
|
2019-10-03 11:52:46 +02:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.SharedPreferences;
|
2019-10-02 22:57:31 +02:00
|
|
|
import android.net.Uri;
|
2019-10-03 11:52:46 +02:00
|
|
|
import android.os.Build;
|
2019-10-08 13:37:25 +02:00
|
|
|
import android.os.CountDownTimer;
|
2019-10-07 17:50:32 +02:00
|
|
|
import android.os.Handler;
|
2019-10-03 11:52:46 +02:00
|
|
|
import android.os.VibrationEffect;
|
|
|
|
import android.os.Vibrator;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
import java.util.Date;
|
2019-10-02 22:57:31 +02:00
|
|
|
import java.util.ArrayList;
|
2019-10-07 15:28:54 +02:00
|
|
|
import java.util.Arrays;
|
2019-10-02 22:57:31 +02:00
|
|
|
import java.util.Calendar;
|
2019-10-07 15:28:54 +02:00
|
|
|
import java.util.GregorianCalendar;
|
2019-10-02 22:57:31 +02:00
|
|
|
import java.util.UUID;
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
2019-10-08 13:37:25 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
2019-10-03 11:52:46 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
2019-10-06 15:46:15 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
2019-10-03 11:52:46 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
2019-10-02 22:57:31 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Coordinator;
|
2019-10-03 11:52:46 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3SampleProvider;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.MakibesHR3ActivitySample;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
2019-10-02 22:57:31 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
2019-10-07 15:28:54 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
2019-10-06 15:46:15 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
2019-10-02 22:57:31 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
2019-10-06 15:46:15 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
2019-10-02 22:57:31 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
2019-10-03 11:52:46 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
2019-10-02 22:57:31 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
2019-10-03 11:52:46 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|
|
|
|
|
|
|
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT;
|
2019-10-02 22:57:31 +02:00
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
public class MakibesHR3DeviceSupport extends AbstractBTLEDeviceSupport implements SharedPreferences.OnSharedPreferenceChangeListener {
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(MakibesHR3DeviceSupport.class);
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
private Handler mVibrationHandler = new Handler();
|
2019-10-03 11:52:46 +02:00
|
|
|
private Vibrator mVibrator;
|
|
|
|
|
2019-10-08 13:37:25 +02:00
|
|
|
private CountDownTimer mFetchCountDown = new CountDownTimer(1000, 1000) {
|
|
|
|
@Override
|
|
|
|
public void onTick(long millisUntilFinished) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFinish() {
|
|
|
|
LOG.debug("download finished");
|
|
|
|
GB.updateTransferNotification(null, "", false, 100, getContext());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
private BluetoothGattCharacteristic mControlCharacteristic = null;
|
|
|
|
private BluetoothGattCharacteristic mReportCharacteristic = null;
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
public MakibesHR3DeviceSupport() {
|
|
|
|
super(LOG);
|
|
|
|
|
|
|
|
addSupportedService(MakibesHR3Constants.UUID_SERVICE);
|
|
|
|
}
|
|
|
|
|
2019-10-08 13:37:25 +02:00
|
|
|
/**
|
|
|
|
* Called whenever data is received to postpone the removing of the progress notification.
|
|
|
|
* @param start Start showing the notification
|
|
|
|
*/
|
|
|
|
private void fetch(boolean start) {
|
|
|
|
if (start) {
|
|
|
|
// We don't know how long the watch is going to take to reply. Keep progress at 0.
|
|
|
|
GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, 0, getContext());
|
|
|
|
}
|
|
|
|
|
|
|
|
this.mFetchCountDown.cancel();
|
|
|
|
this.mFetchCountDown.start();
|
|
|
|
}
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
@Override
|
|
|
|
public boolean useAutoConnect() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
public MakibesHR3ActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) {
|
|
|
|
MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample();
|
|
|
|
sample.setDevice(device);
|
|
|
|
sample.setUser(user);
|
|
|
|
sample.setTimestamp(timestampInSeconds);
|
|
|
|
sample.setProvider(provider);
|
|
|
|
|
|
|
|
return sample;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
@Override
|
|
|
|
public void onNotification(NotificationSpec notificationSpec) {
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("onnotificaiton");
|
|
|
|
|
|
|
|
byte sender;
|
|
|
|
|
|
|
|
switch (notificationSpec.type) {
|
|
|
|
case FACEBOOK:
|
|
|
|
case FACEBOOK_MESSENGER:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_FACEBOOK;
|
|
|
|
break;
|
|
|
|
case LINE:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_LINE;
|
|
|
|
break;
|
|
|
|
case TELEGRAM:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_MESSAGE;
|
|
|
|
break;
|
|
|
|
case TWITTER:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_TWITTER;
|
|
|
|
break;
|
|
|
|
case WECHAT:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_WECHAT;
|
|
|
|
break;
|
|
|
|
case WHATSAPP:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_WHATSAPP;
|
|
|
|
break;
|
|
|
|
case KAKAO_TALK:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_KAKOTALK;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
sender = MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_MESSAGE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
String message = "";
|
|
|
|
|
|
|
|
if (notificationSpec.title != null) {
|
|
|
|
message += (notificationSpec.title + ": ");
|
|
|
|
}
|
|
|
|
|
|
|
|
message += notificationSpec.body;
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
this.sendNotification(transactionBuilder,
|
2019-10-07 17:50:32 +02:00
|
|
|
sender, message);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LoggerFactory.getLogger(this.getClass()).error("notification failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDeleteNotification(int id) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetTime() {
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("settime");
|
|
|
|
|
|
|
|
this.setDateTime(transactionBuilder);
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LoggerFactory.getLogger(this.getClass()).error("factory reset failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
|
|
|
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("setalarms");
|
|
|
|
|
|
|
|
for (int i = 0; i < alarms.size(); ++i) {
|
|
|
|
Alarm alarm = alarms.get(i);
|
|
|
|
|
2019-10-03 12:15:19 +02:00
|
|
|
byte repetition = 0x00;
|
|
|
|
|
|
|
|
switch (alarm.getRepetition()) {
|
|
|
|
case Alarm.ALARM_ONCE:
|
|
|
|
repetition = MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_ONE_TIME;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Alarm.ALARM_MON:
|
|
|
|
repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_MONDAY;
|
|
|
|
case Alarm.ALARM_TUE:
|
|
|
|
repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_TUESDAY;
|
|
|
|
case Alarm.ALARM_WED:
|
|
|
|
repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_WEDNESDAY;
|
|
|
|
case Alarm.ALARM_THU:
|
|
|
|
repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_THURSDAY;
|
|
|
|
case Alarm.ALARM_FRI:
|
|
|
|
repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_FRIDAY;
|
|
|
|
case Alarm.ALARM_SAT:
|
|
|
|
repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_SATURDAY;
|
|
|
|
case Alarm.ALARM_SUN:
|
|
|
|
repetition |= MakibesHR3Constants.ARG_SET_ALARM_REMINDER_REPEAT_SUNDAY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOG.warn("invalid alarm repetition " + alarm.getRepetition());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
// Should we use @alarm.getPosition() rather than @i?
|
|
|
|
this.setAlarmReminder(
|
|
|
|
transactionBuilder,
|
|
|
|
i,
|
|
|
|
alarm.getEnabled(),
|
|
|
|
alarm.getHour(),
|
|
|
|
alarm.getMinute(),
|
2019-10-03 12:15:19 +02:00
|
|
|
repetition);
|
2019-10-02 22:57:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LoggerFactory.getLogger(this.getClass()).error("setalarms failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetCallState(CallSpec callSpec) {
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("callstate");
|
|
|
|
LOG.debug("callSpec " + callSpec.command);
|
|
|
|
if (callSpec.command == CallSpec.CALL_INCOMING) {
|
|
|
|
this.sendNotification(transactionBuilder, MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_CALL, callSpec.name);
|
|
|
|
} else {
|
2019-10-07 17:50:32 +02:00
|
|
|
this.sendNotification(transactionBuilder, MakibesHR3Constants.ARG_SEND_NOTIFICATION_SOURCE_STOP_CALL, "");
|
2019-10-02 22:57:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LoggerFactory.getLogger(this.getClass()).error("call state failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetMusicState(MusicStateSpec stateSpec) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onEnableRealtimeSteps(boolean enable) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onInstallApp(Uri uri) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAppInfoReq() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAppStart(UUID uuid, boolean start) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAppDelete(UUID uuid) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAppConfiguration(UUID appUuid, String config, Integer id) {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAppReorder(UUID[] uuids) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFetchRecordedData(int dataTypes) {
|
2019-10-06 15:46:15 +02:00
|
|
|
// what is this?
|
2019-10-02 22:57:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onReset(int flags) {
|
|
|
|
|
|
|
|
if ((flags & GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET) != 0) {
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("reset");
|
|
|
|
this.factoryReset(transactionBuilder);
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LoggerFactory.getLogger(this.getClass()).error("factory reset failed");
|
|
|
|
}
|
|
|
|
} else if ((flags & GBDeviceProtocol.RESET_FLAGS_REBOOT) != 0) {
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("reboot");
|
|
|
|
this.reboot(transactionBuilder);
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LoggerFactory.getLogger(this.getClass()).error("factory reset failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onHeartRateTest() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
2019-10-03 11:52:46 +02:00
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("finddevice");
|
|
|
|
|
|
|
|
this.setEnableRealTimeHeartRate(transactionBuilder, enable);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception e) {
|
|
|
|
LOG.debug("ERROR");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onReverseFindDevice(boolean start) {
|
2019-10-07 17:50:32 +02:00
|
|
|
if (this.mVibrator.hasVibrator()) {
|
|
|
|
final long[] PATTERN = new long[]{
|
|
|
|
100, 100,
|
|
|
|
100, 100,
|
|
|
|
100, 100,
|
|
|
|
500
|
|
|
|
};
|
|
|
|
|
|
|
|
if (start) {
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
this.mVibrator.vibrate(VibrationEffect.createWaveform(PATTERN, 0));
|
|
|
|
} else {
|
|
|
|
this.mVibrator.vibrate(PATTERN, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// In case the connection is closed while we're searching for the device.
|
|
|
|
|
|
|
|
this.mVibrationHandler.postDelayed(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
mVibrator.cancel();
|
|
|
|
}
|
|
|
|
}, 1100 * 6);
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
} else {
|
2019-10-07 17:50:32 +02:00
|
|
|
this.mVibrator.cancel();
|
2019-10-03 11:52:46 +02:00
|
|
|
}
|
|
|
|
} else {
|
2019-10-07 17:50:32 +02:00
|
|
|
// TODO: Alternative handling. Don't use sound, the connection isn't secure.
|
2019-10-03 11:52:46 +02:00
|
|
|
}
|
2019-10-02 22:57:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFindDevice(boolean start) {
|
|
|
|
if (!start) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("finddevice");
|
|
|
|
|
|
|
|
this.findDevice(transactionBuilder);
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception e) {
|
|
|
|
LOG.debug("ERROR");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetConstantVibration(int integer) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onScreenshotReq() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onEnableHeartRateSleepSupport(boolean enable) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSetHeartRateMeasurementInterval(int seconds) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDeleteCalendarEvent(byte type, long id) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSendConfiguration(String config) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onReadConfiguration(String config) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTestNewFunction() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSendWeather(WeatherSpec weatherSpec) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private MakibesHR3DeviceSupport sendUserInfo(TransactionBuilder builder) {
|
2019-10-08 13:37:25 +02:00
|
|
|
this.syncPreferences(builder);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private MakibesHR3DeviceSupport syncPreferences(TransactionBuilder transaction) {
|
|
|
|
|
|
|
|
this.setTimeMode(transaction);
|
|
|
|
this.setDateTime(transaction);
|
|
|
|
// setDayOfWeek(transaction);
|
2019-10-06 15:46:15 +02:00
|
|
|
this.setTimeMode(transaction);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
ActivityUser activityUser = new ActivityUser();
|
|
|
|
|
|
|
|
this.setPersonalInformation(transaction,
|
|
|
|
(byte) Math.round(activityUser.getHeightCm() * 0.43), // Thanks no1f1
|
|
|
|
activityUser.getAge(),
|
|
|
|
activityUser.getHeightCm(),
|
|
|
|
activityUser.getWeightKg(),
|
|
|
|
activityUser.getStepsGoal() / 1000);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
2019-10-08 13:37:25 +02:00
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
// setLanguage(transaction);
|
|
|
|
// setScreenTime(transaction);
|
|
|
|
// setUnit(transaction);
|
|
|
|
// setAllDayHeart(transaction);
|
|
|
|
|
2019-10-08 13:37:25 +02:00
|
|
|
this.fetch(true);
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
|
|
|
LOG.debug(key + " changed");
|
|
|
|
TransactionBuilder transactionBuilder = this.createTransactionBuilder("onSharedPreferenceChanged");
|
|
|
|
|
|
|
|
if (key.equals(PREF_TIMEFORMAT)) {
|
|
|
|
this.setTimeMode(transactionBuilder);
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.performConnected(transactionBuilder.getTransaction());
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LOG.warn(ex.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
@Override
|
|
|
|
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
2019-10-08 13:37:25 +02:00
|
|
|
GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, 0, getContext());
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
gbDevice.setState(GBDevice.State.INITIALIZING);
|
|
|
|
gbDevice.sendDeviceUpdateIntent(getContext());
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
this.mControlCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_CONTROL);
|
|
|
|
this.mReportCharacteristic = getCharacteristic(MakibesHR3Constants.UUID_CHARACTERISTIC_REPORT);
|
2019-10-03 11:52:46 +02:00
|
|
|
|
|
|
|
this.mVibrator = (Vibrator) this.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
builder.notify(this.mReportCharacteristic, true);
|
2019-10-02 22:57:31 +02:00
|
|
|
builder.setGattCallback(this);
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
// Allow modifications
|
2019-10-06 15:46:15 +02:00
|
|
|
builder.write(this.mControlCharacteristic, new byte[]{0x01, 0x00});
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
// Initialize device
|
|
|
|
sendUserInfo(builder); //Sync preferences
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
this.requestFitness(builder);
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
gbDevice.setState(GBDevice.State.INITIALIZED);
|
|
|
|
gbDevice.sendDeviceUpdateIntent(getContext());
|
|
|
|
|
|
|
|
getDevice().setFirmwareVersion("N/A");
|
|
|
|
getDevice().setFirmwareVersion2("N/A");
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
SharedPreferences preferences = GBApplication.getDeviceSpecificSharedPrefs(this.getDevice().getAddress());
|
|
|
|
|
|
|
|
preferences.registerOnSharedPreferenceChangeListener(this);
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
return builder;
|
|
|
|
}
|
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
private void addGBActivitySamples(MakibesHR3ActivitySample[] samples) {
|
2019-10-03 11:52:46 +02:00
|
|
|
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
|
|
|
|
|
|
|
User user = DBHelper.getUser(dbHandler.getDaoSession());
|
|
|
|
Device device = DBHelper.getDevice(this.getDevice(), dbHandler.getDaoSession());
|
|
|
|
|
|
|
|
MakibesHR3SampleProvider provider = new MakibesHR3SampleProvider(this.getDevice(), dbHandler.getDaoSession());
|
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
for (MakibesHR3ActivitySample sample : samples) {
|
|
|
|
sample.setDevice(device);
|
|
|
|
sample.setUser(user);
|
|
|
|
sample.setProvider(provider);
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
sample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
provider.addGBActivitySample(sample);
|
2019-10-03 11:52:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception ex) {
|
2019-10-07 15:28:54 +02:00
|
|
|
// Why is this a toast? The user doesn't care about the error.
|
|
|
|
GB.toast(getContext(), "Error saving samples: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
2019-10-03 11:52:46 +02:00
|
|
|
GB.updateTransferNotification(null, "Data transfer failed", false, 0, getContext());
|
2019-10-07 15:28:54 +02:00
|
|
|
|
|
|
|
LOG.error(ex.getMessage());
|
2019-10-03 11:52:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
private void addGBActivitySample(MakibesHR3ActivitySample sample) {
|
2019-10-07 17:50:32 +02:00
|
|
|
this.addGBActivitySamples(new MakibesHR3ActivitySample[]{sample});
|
2019-10-07 15:28:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Should only be called after the sample has been populated by
|
|
|
|
* {@link MakibesHR3DeviceSupport#addGBActivitySample} or
|
|
|
|
* {@link MakibesHR3DeviceSupport#addGBActivitySamples}
|
2019-10-07 17:50:32 +02:00
|
|
|
*
|
2019-10-07 15:28:54 +02:00
|
|
|
* @param sample
|
|
|
|
*/
|
|
|
|
private void broadcastSample(MakibesHR3ActivitySample sample) {
|
|
|
|
Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES)
|
|
|
|
.putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample)
|
|
|
|
.putExtra(DeviceService.EXTRA_TIMESTAMP, sample.getTimestamp());
|
2019-10-07 17:50:32 +02:00
|
|
|
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
2019-10-07 15:28:54 +02:00
|
|
|
}
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
private void onReceiveFitness(int steps) {
|
|
|
|
LOG.info("steps: " + steps);
|
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample();
|
|
|
|
|
|
|
|
sample.setSteps(steps);
|
|
|
|
sample.setTimestamp((int) (System.currentTimeMillis() / 1000));
|
|
|
|
|
|
|
|
sample.setRawKind(ActivityKind.TYPE_ACTIVITY);
|
|
|
|
this.addGBActivitySample(sample);
|
2019-10-03 11:52:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void onReceiveHeartRate(int heartRate) {
|
|
|
|
LOG.info("heart rate: " + heartRate);
|
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample();
|
|
|
|
|
|
|
|
if (heartRate > 0) {
|
|
|
|
sample.setHeartRate(heartRate);
|
|
|
|
sample.setTimestamp((int) (System.currentTimeMillis() / 1000));
|
|
|
|
sample.setRawKind(ActivityKind.TYPE_ACTIVITY);
|
|
|
|
} else {
|
|
|
|
if (heartRate == MakibesHR3Constants.ARG_HEARTRATE_NO_TARGET) {
|
|
|
|
sample.setRawKind(ActivityKind.TYPE_NOT_WORN);
|
|
|
|
} else if (heartRate == MakibesHR3Constants.ARG_HEARTRATE_NO_READING) {
|
|
|
|
sample.setRawKind(ActivityKind.TYPE_NOT_MEASURED);
|
|
|
|
} else {
|
|
|
|
LOG.warn("invalid heart rate reading: " + heartRate);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.addGBActivitySample(sample);
|
|
|
|
this.broadcastSample(sample);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onReceiveHeartRateSample(int year, int month, int day, int hour, int minute, int heartRate) {
|
|
|
|
LOG.debug("received heart rate sample " + year + "-" + month + "-" + day + " " + hour + ":" + minute + " " + heartRate);
|
|
|
|
|
|
|
|
MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample();
|
|
|
|
|
|
|
|
Calendar calendar = new GregorianCalendar(year, month - 1, day, hour, minute);
|
|
|
|
|
|
|
|
int timeStamp = (int) (calendar.getTimeInMillis() / 1000);
|
|
|
|
|
|
|
|
sample.setHeartRate(heartRate);
|
|
|
|
sample.setTimestamp(timeStamp);
|
|
|
|
|
|
|
|
sample.setRawKind(ActivityKind.TYPE_ACTIVITY);
|
|
|
|
|
|
|
|
this.addGBActivitySample(sample);
|
2019-10-03 11:52:46 +02:00
|
|
|
}
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
/**
|
|
|
|
* The time is the start of the measurement. Each measurement lasts 1h.
|
|
|
|
*/
|
|
|
|
private void onReceiveStepsSample(int year, int month, int day, int hour, int minute, int steps) {
|
|
|
|
LOG.debug("received steps sample " + year + "-" + month + "-" + day + " " + hour + ":" + minute + " " + steps);
|
|
|
|
|
|
|
|
MakibesHR3ActivitySample sample = new MakibesHR3ActivitySample();
|
|
|
|
|
|
|
|
Calendar calendar = new GregorianCalendar(year, month - 1, day, hour + 1, minute);
|
|
|
|
|
|
|
|
int timeStamp = (int) (calendar.getTimeInMillis() / 1000);
|
|
|
|
|
|
|
|
sample.setSteps(steps);
|
|
|
|
sample.setTimestamp(timeStamp);
|
|
|
|
|
|
|
|
sample.setRawKind(ActivityKind.TYPE_ACTIVITY);
|
|
|
|
|
|
|
|
this.addGBActivitySample(sample);
|
|
|
|
}
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
@Override
|
|
|
|
public boolean onCharacteristicChanged(BluetoothGatt gatt,
|
|
|
|
BluetoothGattCharacteristic characteristic) {
|
|
|
|
if (super.onCharacteristicChanged(gatt, characteristic)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] data = characteristic.getValue();
|
|
|
|
if (data.length < 6)
|
|
|
|
return true;
|
|
|
|
|
2019-10-08 13:37:25 +02:00
|
|
|
this.fetch(false);
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
UUID characteristicUuid = characteristic.getUuid();
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
if (characteristicUuid.equals(mReportCharacteristic.getUuid())) {
|
2019-10-03 11:52:46 +02:00
|
|
|
byte[] value = characteristic.getValue();
|
|
|
|
byte[] arguments = new byte[value.length - 6];
|
|
|
|
|
|
|
|
for (int i = 0; i < arguments.length; ++i) {
|
|
|
|
arguments[i] = value[i + 6];
|
|
|
|
}
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
byte[] report = new byte[]{value[4], value[5]};
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-07 15:28:54 +02:00
|
|
|
switch (report[0]) {
|
2019-10-03 11:52:46 +02:00
|
|
|
case MakibesHR3Constants.RPRT_REVERSE_FIND_DEVICE:
|
|
|
|
this.onReverseFindDevice(arguments[0] == 0x01);
|
|
|
|
break;
|
|
|
|
case MakibesHR3Constants.RPRT_HEARTRATE:
|
|
|
|
if (value.length == 7) {
|
|
|
|
this.onReceiveHeartRate((int) arguments[0]);
|
|
|
|
}
|
|
|
|
break;
|
2019-10-06 15:46:15 +02:00
|
|
|
case MakibesHR3Constants.RPRT_BATTERY:
|
|
|
|
if (arguments.length == 2) {
|
|
|
|
GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo();
|
|
|
|
|
|
|
|
batteryInfo.level = (short) arguments[1];
|
|
|
|
batteryInfo.state = ((arguments[0] == 0x01) ? BatteryState.BATTERY_CHARGING : BatteryState.BATTERY_NORMAL);
|
|
|
|
|
|
|
|
this.handleGBDeviceEvent(batteryInfo);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MakibesHR3Constants.RPRT_SOFTWARE:
|
2019-10-03 11:52:46 +02:00
|
|
|
if (arguments.length == 11) {
|
|
|
|
this.getDevice().setFirmwareVersion(((int) arguments[0]) + "." + ((int) arguments[1]));
|
|
|
|
}
|
|
|
|
break;
|
2019-10-07 15:28:54 +02:00
|
|
|
default: // Non-80 reports
|
|
|
|
if (Arrays.equals(report, MakibesHR3Constants.RPRT_FITNESS)) {
|
2019-10-07 17:50:32 +02:00
|
|
|
this.onReceiveFitness(
|
|
|
|
(int) arguments[1] * 0xff + arguments[2]
|
|
|
|
);
|
2019-10-07 15:28:54 +02:00
|
|
|
} else if (Arrays.equals(report, MakibesHR3Constants.RPRT_HEART_RATE_SAMPLE)) {
|
|
|
|
this.onReceiveHeartRateSample(
|
|
|
|
arguments[0] + 2000, arguments[1], arguments[2],
|
|
|
|
arguments[3], arguments[4],
|
|
|
|
arguments[5]);
|
2019-10-07 17:50:32 +02:00
|
|
|
} else if (Arrays.equals(report, MakibesHR3Constants.RPRT_STEPS_SAMPLE)) {
|
|
|
|
this.onReceiveStepsSample(
|
|
|
|
arguments[0] + 2000, arguments[1], arguments[2],
|
|
|
|
arguments[3], 0,
|
|
|
|
(arguments[5] * 0xff) + arguments[6]);
|
2019-10-07 15:28:54 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-10-03 11:52:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
private byte[] craftData(byte[] command, byte[] data) {
|
2019-10-02 22:57:31 +02:00
|
|
|
byte[] result = new byte[MakibesHR3Constants.DATA_TEMPLATE.length + data.length];
|
|
|
|
|
2019-10-02 23:45:19 +02:00
|
|
|
System.arraycopy(MakibesHR3Constants.DATA_TEMPLATE, 0, result, 0, MakibesHR3Constants.DATA_TEMPLATE.length);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
result[MakibesHR3Constants.DATA_ARGUMENT_COUNT_INDEX] = (byte) (data.length + 3);
|
2019-10-06 15:46:15 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < command.length; ++i) {
|
|
|
|
result[MakibesHR3Constants.DATA_COMMAND_INDEX + i] = command[i];
|
|
|
|
}
|
2019-10-02 22:57:31 +02:00
|
|
|
|
2019-10-02 23:45:19 +02:00
|
|
|
System.arraycopy(data, 0, result, 6, data.length);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
private byte[] craftData(byte command, byte[] data) {
|
|
|
|
return this.craftData(new byte[]{command}, data);
|
|
|
|
}
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
private byte[] craftData(byte command) {
|
|
|
|
return this.craftData(command, new byte[]{});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void writeSafe(BluetoothGattCharacteristic characteristic, TransactionBuilder builder, byte[] data) {
|
|
|
|
final int maxMessageLength = 20;
|
|
|
|
|
|
|
|
// For every split, we need 1 byte extra.
|
2019-10-07 17:50:32 +02:00
|
|
|
int extraBytes = 0;
|
|
|
|
|
|
|
|
if (data.length > 20) {
|
|
|
|
extraBytes = (((data.length - maxMessageLength) / maxMessageLength) + 1);
|
|
|
|
}
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
int totalDataLength = (data.length + extraBytes);
|
|
|
|
|
|
|
|
int segmentCount = (((totalDataLength - 1) / maxMessageLength) + 1);
|
|
|
|
|
|
|
|
byte[] indexedData = new byte[totalDataLength];
|
|
|
|
|
|
|
|
int it = 0;
|
|
|
|
int segmentIndex = 0;
|
|
|
|
for (int i = 0; i < data.length; ++i) {
|
|
|
|
if ((i != 0) && ((it % maxMessageLength) == 0)) {
|
|
|
|
indexedData[it++] = (byte) segmentIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
indexedData[it++] = data[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < segmentCount; ++i) {
|
|
|
|
int segmentStart = (i * maxMessageLength);
|
|
|
|
int segmentLength;
|
|
|
|
|
|
|
|
if (i == (segmentCount - 1)) {
|
|
|
|
segmentLength = (indexedData.length - segmentStart);
|
|
|
|
} else {
|
|
|
|
segmentLength = maxMessageLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] segment = new byte[segmentLength];
|
|
|
|
|
2019-10-02 23:45:19 +02:00
|
|
|
System.arraycopy(indexedData, segmentStart, segment, 0, segmentLength);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
builder.write(characteristic, segment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private MakibesHR3DeviceSupport factoryReset(TransactionBuilder transaction) {
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FACTORY_RESET));
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
return this.reboot(transaction);
|
|
|
|
}
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
/**
|
|
|
|
* Ugly because I don't like Date.
|
|
|
|
* All non-zero records after the given times will be returned via
|
|
|
|
* {@link MakibesHR3Constants#RPRT_HEART_RATE_SAMPLE},
|
|
|
|
* {@link MakibesHR3Constants#RPRT_STEPS_SAMPLE},
|
|
|
|
* {@link MakibesHR3Constants#RPRT_FITNESS}
|
|
|
|
*/
|
2019-10-03 11:52:46 +02:00
|
|
|
private MakibesHR3DeviceSupport requestFitness(TransactionBuilder transaction,
|
2019-10-07 17:50:32 +02:00
|
|
|
int yearStepsAfter, int monthStepsAfter, int dayStepsAfter,
|
|
|
|
int hourStepsAfter, int minuteStepsAfter,
|
|
|
|
int yearHeartRateAfter, int monthHeartRateAfter, int dayHeartRateAfter,
|
|
|
|
int hourHeartRateAfter, int minuteHeartRateAfter) {
|
2019-10-03 11:52:46 +02:00
|
|
|
|
2019-10-08 13:37:25 +02:00
|
|
|
this.fetch(true);
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
byte[] data = this.craftData(MakibesHR3Constants.CMD_REQUEST_FITNESS,
|
|
|
|
new byte[]{
|
2019-10-07 17:50:32 +02:00
|
|
|
(byte) 0x00,
|
|
|
|
(byte) (yearStepsAfter - 2000),
|
|
|
|
(byte) monthStepsAfter,
|
|
|
|
(byte) dayStepsAfter,
|
|
|
|
(byte) hourStepsAfter,
|
|
|
|
(byte) minuteStepsAfter,
|
|
|
|
(byte) (yearHeartRateAfter - 2000),
|
|
|
|
(byte) monthHeartRateAfter,
|
|
|
|
(byte) dayHeartRateAfter,
|
|
|
|
(byte) hourHeartRateAfter,
|
|
|
|
(byte) minuteHeartRateAfter
|
2019-10-03 11:52:46 +02:00
|
|
|
});
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic, data);
|
2019-10-03 11:52:46 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-10-07 17:50:32 +02:00
|
|
|
/**
|
|
|
|
* Ugly because I don't like Date.
|
|
|
|
* All non-zero records after the given times will be returned via
|
|
|
|
* {@link MakibesHR3Constants#RPRT_HEART_RATE_SAMPLE},
|
|
|
|
* {@link MakibesHR3Constants#RPRT_STEPS_SAMPLE},
|
|
|
|
* {@link MakibesHR3Constants#RPRT_FITNESS}
|
|
|
|
*/
|
|
|
|
private MakibesHR3DeviceSupport requestFitness(TransactionBuilder transaction) {
|
|
|
|
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
|
|
|
|
|
|
|
MakibesHR3SampleProvider provider = new MakibesHR3SampleProvider(this.getDevice(), dbHandler.getDaoSession());
|
|
|
|
|
|
|
|
MakibesHR3ActivitySample latestSample = provider.getLatestActivitySample();
|
|
|
|
|
|
|
|
if (latestSample == null) {
|
|
|
|
this.requestFitness(transaction,
|
|
|
|
0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0);
|
|
|
|
} else {
|
|
|
|
// getInstance is unnecessary, we're overriding.
|
|
|
|
Calendar calendar = Calendar.getInstance();
|
|
|
|
calendar.setTime(new Date(latestSample.getTimestamp() * 1000l));
|
|
|
|
|
|
|
|
int year = calendar.get(Calendar.YEAR);
|
|
|
|
int month = (calendar.get(Calendar.MONTH) + 1);
|
|
|
|
int day = calendar.get(Calendar.DAY_OF_MONTH);
|
|
|
|
int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
|
|
|
int minute = calendar.get(Calendar.MINUTE);
|
|
|
|
|
|
|
|
this.requestFitness(transaction,
|
|
|
|
year, month, day, hour, minute,
|
|
|
|
year, month, day, hour, minute);
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LOG.error(ex.getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
private MakibesHR3DeviceSupport findDevice(TransactionBuilder transaction) {
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_FIND_DEVICE));
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private MakibesHR3DeviceSupport sendNotification(TransactionBuilder transaction,
|
|
|
|
byte source, String message) {
|
|
|
|
byte[] data = new byte[message.length() + 2];
|
|
|
|
data[0] = source;
|
|
|
|
data[1] = (byte) 0x02;
|
|
|
|
|
|
|
|
for (int i = 0; i < message.length(); ++i) {
|
|
|
|
data[i + 2] = (byte) message.charAt(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.writeSafe(
|
2019-10-06 15:46:15 +02:00
|
|
|
this.mControlCharacteristic,
|
2019-10-02 22:57:31 +02:00
|
|
|
transaction,
|
|
|
|
this.craftData(MakibesHR3Constants.CMD_SEND_NOTIFICATION, data));
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private MakibesHR3DeviceSupport setAlarmReminder(TransactionBuilder transaction,
|
|
|
|
int id, boolean enable, int hour, int minute, byte repeat) {
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic,
|
2019-10-02 22:57:31 +02:00
|
|
|
this.craftData(MakibesHR3Constants.CMD_SET_ALARM_REMINDER, new byte[]{
|
|
|
|
(byte) id,
|
|
|
|
(byte) (enable ? 0x01 : 0x00),
|
|
|
|
(byte) hour,
|
|
|
|
(byte) minute,
|
|
|
|
repeat
|
|
|
|
}));
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
/**
|
|
|
|
* @param transactionBuilder
|
|
|
|
* @param stepLength cm
|
|
|
|
* @param age years
|
|
|
|
* @param height cm
|
|
|
|
* @param weight kg
|
|
|
|
* @param stepGoal kilo
|
|
|
|
*/
|
|
|
|
private MakibesHR3DeviceSupport setPersonalInformation(TransactionBuilder transactionBuilder,
|
|
|
|
int stepLength, int age, int height, int weight, int stepGoal) {
|
|
|
|
byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_PERSONAL_INFORMATION,
|
|
|
|
new byte[]{
|
|
|
|
(byte) stepLength,
|
|
|
|
(byte) age,
|
|
|
|
(byte) height,
|
|
|
|
(byte) weight,
|
|
|
|
MakibesHR3Constants.ARG_SET_PERSONAL_INFORMATION_UNIT_DISTANCE_KILOMETERS,
|
|
|
|
(byte) stepGoal,
|
|
|
|
(byte) 0x5a,
|
|
|
|
(byte) 0x82,
|
|
|
|
(byte) 0x3c,
|
|
|
|
(byte) 0x5a,
|
|
|
|
(byte) 0x28,
|
|
|
|
(byte) 0xb4,
|
|
|
|
(byte) 0x5d,
|
|
|
|
(byte) 0x64,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
transactionBuilder.write(this.mControlCharacteristic, data);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
private MakibesHR3DeviceSupport setTimeMode(TransactionBuilder transaction) {
|
|
|
|
byte value = MakibesHR3Coordinator.getTimeMode(getDevice().getAddress());
|
|
|
|
|
|
|
|
byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_TIMEMODE, new byte[]{value});
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic, data);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-10-03 11:52:46 +02:00
|
|
|
private MakibesHR3DeviceSupport setEnableRealTimeHeartRate(TransactionBuilder transaction, boolean enable) {
|
|
|
|
byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_REAL_TIME_HEART_RATE, new byte[]{(byte) (enable ? 0x01 : 0x00)});
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic, data);
|
2019-10-03 11:52:46 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-10-02 22:57:31 +02:00
|
|
|
private MakibesHR3DeviceSupport setDateTime(TransactionBuilder transaction,
|
|
|
|
int year,
|
|
|
|
int month,
|
|
|
|
int day,
|
|
|
|
int hour,
|
|
|
|
int minute,
|
|
|
|
int second) {
|
|
|
|
|
|
|
|
byte[] data = this.craftData(MakibesHR3Constants.CMD_SET_DATE_TIME,
|
|
|
|
new byte[]{
|
|
|
|
(byte) 0x00,
|
2019-10-06 15:46:15 +02:00
|
|
|
(byte) ((year & 0xff00) >> 8),
|
2019-10-02 22:57:31 +02:00
|
|
|
(byte) (year & 0x00ff),
|
|
|
|
(byte) month,
|
|
|
|
(byte) day,
|
|
|
|
(byte) hour,
|
|
|
|
(byte) minute,
|
|
|
|
(byte) second
|
|
|
|
});
|
|
|
|
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic, data);
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private MakibesHR3DeviceSupport setDateTime(TransactionBuilder transaction) {
|
|
|
|
|
|
|
|
Calendar calendar = Calendar.getInstance();
|
|
|
|
|
|
|
|
return this.setDateTime(transaction,
|
|
|
|
calendar.get(Calendar.YEAR),
|
|
|
|
calendar.get(Calendar.MONTH) + 1,
|
|
|
|
calendar.get(Calendar.DAY_OF_MONTH),
|
|
|
|
calendar.get(Calendar.HOUR_OF_DAY),
|
|
|
|
calendar.get(Calendar.MINUTE),
|
|
|
|
calendar.get(Calendar.SECOND)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private MakibesHR3DeviceSupport reboot(TransactionBuilder transaction) {
|
2019-10-06 15:46:15 +02:00
|
|
|
transaction.write(this.mControlCharacteristic, this.craftData(MakibesHR3Constants.CMD_REBOOT));
|
2019-10-02 22:57:31 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|