diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java
index d0144995d..0cc76021e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java
@@ -1,572 +1,572 @@
-/* Copyright (C) 2018 Andreas Shimokawa, ladbsoft
-
- 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 . */
-package nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch;
-
-import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattCharacteristic;
-import android.content.Context;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.view.KeyEvent;
-import android.widget.Toast;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.UUID;
-
-import nodomain.freeyourgadget.gadgetbridge.GBApplication;
-import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
-import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
-import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
-import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
-import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchSampleProvider;
-import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchService;
-import nodomain.freeyourgadget.gadgetbridge.entities.Device;
-import nodomain.freeyourgadget.gadgetbridge.entities.User;
-import nodomain.freeyourgadget.gadgetbridge.entities.XWatchActivitySample;
-import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
-import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
-import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
-import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
-import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
-import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
-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.btle.actions.SetDeviceStateAction;
-import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo;
-import nodomain.freeyourgadget.gadgetbridge.util.GB;
-
-public class XWatchSupport extends AbstractBTLEDeviceSupport {
- private static final Logger LOG = LoggerFactory.getLogger(XWatchSupport.class);
- private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
- TransactionBuilder builder = null;
- private DeviceInfo mDeviceInfo;
- private byte dayToFetch; //0 = Today; 1 = Yesterday ...
- private byte maxDayToFetch;
- long lastButtonTimestamp;
-
- public XWatchSupport() {
- super(LOG);
-
- addSupportedService(XWatchService.UUID_SERVICE);
- addSupportedService(XWatchService.UUID_WRITE);
- addSupportedService(XWatchService.UUID_NOTIFY);
- }
-
- public static byte[] crcChecksum(byte[] data) {
- byte[] return_data = new byte[(data.length + 1)];
- byte checksum = 0;
-
- for (int i = 0; i < data.length; i++) {
- return_data[i] = data[i];
- checksum += data[i];
- }
- return_data[return_data.length - 1] = checksum;
-
- return return_data;
- }
-
- @Override
- protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
- builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
-
- enableNotifications(builder)
- .setDateTime(builder)
- .setInitialized(builder);
-
- return builder;
- }
-
- /**
- * Last action of initialization sequence. Sets the device to initialized.
- * It is only invoked if all other actions were successfully run, so the device
- * must be initialized, then.
- *
- * @param builder
- */
- private void setInitialized(TransactionBuilder builder) {
- builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZED, getContext()));
- }
-
- @Override
- public boolean useAutoConnect() {
- return true;
- }
-
- @Override
- public boolean connectFirstTime() {
- for (int i = 0; i < 5; i++) {
- if (connect()) {
- return true;
- }
- }
- return false;
- }
-
- private XWatchSupport setDateTime(TransactionBuilder builder) {
- byte[] data;
-
- LOG.debug("Sending current date to the XWatch");
- BluetoothGattCharacteristic deviceData = getCharacteristic(XWatchService.UUID_WRITE);
-
- String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
- String y = time.substring(2, 4);
- String M = time.substring(4, 6);
- String d = time.substring(6, 8);
- String H = time.substring(8, 10);
- String m = time.substring(10, 12);
- String s = time.substring(12, 14);
- System.out.println(y + ":" + M + ":" + d + ":" + H + ":" + m + ":" + time.substring(12, 14));
-
- data = new byte[]{(byte) 1,
- (byte) Integer.parseInt(y, 16),
- (byte) Integer.parseInt(M, 16),
- (byte) Integer.parseInt(d, 16),
- (byte) Integer.parseInt(H, 16),
- (byte) Integer.parseInt(m, 16),
- (byte) Integer.parseInt(s, 16),
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0};
-
- data = crcChecksum(data);
-
- builder.write(deviceData, data);
-
- return this;
- }
-
- private XWatchSupport enableNotifications(TransactionBuilder builder) {
- LOG.debug("Enabling action button");
- BluetoothGattCharacteristic deviceInfo = getCharacteristic(XWatchService.UUID_NOTIFY);
- builder.notify(deviceInfo, true);
- return this;
- }
-
- @Override
- public void onEnableHeartRateSleepSupport(boolean enable) {
- //Not supported
- }
-
- @Override
- public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
- // not supported
- }
-
- @Override
- public void onDeleteCalendarEvent(byte type, long id) {
- // not supported
- }
-
- @Override
- public void onSetAlarms(ArrayList extends Alarm> alarms) {
- //TODO: Implement
- }
-
- @Override
- public void onNotification(NotificationSpec notificationSpec) {
- //TODO: Implement
- }
-
- @Override
- public void onDeleteNotification(int id) {
- //TODO: Implement
- }
-
- @Override
- public void onSetTime() {
- try {
- TransactionBuilder builder = performInitialized("Set date and time");
- setDateTime(builder);
- builder.queue(getQueue());
- } catch (IOException ex) {
- LOG.error("Unable to set time and date on XWatch device", ex);
- }
- }
-
- @Override
- public void onSetCallState(CallSpec callSpec) {
- //TODO: Implement (if necessary)
- }
-
- @Override
- public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
- }
-
- @Override
- public void onSetMusicState(MusicStateSpec stateSpec) {
- // not supported
- }
-
- @Override
- public void onSetMusicInfo(MusicSpec musicSpec) {
- // not supported
- }
-
- @Override
- public void onReboot() {
- //Not supported
- }
-
- @Override
- public void onHeartRateTest() {
- //Not supported
- }
-
- @Override
- public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
- //Not supported
- }
-
- @Override
- public void onFindDevice(boolean start) {
- //TODO: Implement
- }
-
- @Override
- public void onSetConstantVibration(int intensity) {
- //TODO: Implement
- }
-
- @Override
- public void onFetchRecordedData(int dataTypes) {
- try {
- if(builder == null) {
- builder = performInitialized("fetchActivityData");
- }
- requestSummarizedData(builder);
- performConnected(builder.getTransaction());
- } catch (IOException e) {
- GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
- }
- }
-
- @Override
- public void onEnableRealtimeSteps(boolean enable) {
- //Not supported
- }
-
- @Override
- public void onInstallApp(Uri uri) {
- //Not supported
- }
-
- @Override
- public void onAppInfoReq() {
- // not supported
- }
-
- @Override
- public void onAppStart(UUID uuid, boolean start) {
- // not supported
- }
-
- @Override
- public void onAppDelete(UUID uuid) {
- // not supported
- }
-
- @Override
- public void onAppReorder(UUID[] uuids) {
- // not supported
- }
-
- @Override
- public void onScreenshotReq() {
- // not supported
- }
-
- @Override
- public boolean onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- super.onCharacteristicChanged(gatt, characteristic);
-
- UUID characteristicUUID = characteristic.getUuid();
- if (XWatchService.UUID_NOTIFY.equals(characteristicUUID)) {
- byte[] data = characteristic.getValue();
- if (data[0] == XWatchService.COMMAND_ACTIVITY_TOTALS) {
- handleSummarizedData(characteristic.getValue());
- } else if (data[0] == XWatchService.COMMAND_ACTIVITY_DATA) {
- handleDetailedData(characteristic.getValue());
- } else if (data[0] == XWatchService.COMMAND_ACTION_BUTTON) {
- handleButtonPressed(characteristic.getValue());
- } else if (data[0] == XWatchService.COMMAND_CONNECTED) {
- handleDeviceInfo(data, BluetoothGatt.GATT_SUCCESS);
- } else {
- LOG.info("Handled characteristic with unknown data: " + characteristicUUID);
- logMessageContent(characteristic.getValue());
- }
- } else {
- LOG.info("Unhandled characteristic changed: " + characteristicUUID);
- logMessageContent(characteristic.getValue());
- }
- return false;
- }
-
- @Override
- public boolean onCharacteristicRead(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- return super.onCharacteristicChanged(gatt, characteristic);
- //TODO: Implement (if necessary)
- }
-
- @Override
- public boolean onCharacteristicWrite(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- return super.onCharacteristicWrite(gatt, characteristic, status);
- //TODO: Implement (if necessary)
- }
-
- public XWatchActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) {
- XWatchActivitySample sample = new XWatchActivitySample();
- sample.setDevice(device);
- sample.setUser(user);
- sample.setTimestamp(timestampInSeconds);
- sample.setProvider(provider);
-
- return sample;
- }
-
- private void handleDeviceInfo(byte[] value, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- mDeviceInfo = new DeviceInfo(value);
- LOG.warn("Device info: " + mDeviceInfo);
- versionCmd.hwVersion = "1.0";
- versionCmd.fwVersion = "1.0";
- handleGBDeviceEvent(versionCmd);
- }
- }
-
- @Override
- public void onSendConfiguration(String config) {
- // nothing yet
- }
-
- @Override
- public void onTestNewFunction() {
- //Not supported
- }
-
- @Override
- public void onSendWeather(WeatherSpec weatherSpec) {
- //Not supported
- }
-
- private void handleSummarizedData(byte[] value) {
- int daysIntTotal;
- int daysIntPart;
-
- if (value.length != 16) {
- LOG.warn("GOT UNEXPECTED SENSOR DATA WITH LENGTH: " + value.length);
- for (byte b : value) {
- LOG.warn("DATA: " + String.format("0x%4x", b));
- }
- } else {
- daysIntPart = (value[1] & 255) << 24;
- daysIntTotal = daysIntPart;
- daysIntPart = (value[2] & 255) << 16;
- daysIntTotal += daysIntPart;
- daysIntPart = (value[3] & 255) << 8;
- daysIntTotal += daysIntPart;
- daysIntPart = (value[4] & 255);
- daysIntTotal += daysIntPart;
-
- dayToFetch = 0;
- maxDayToFetch = (byte) Integer.bitCount(daysIntTotal);
-
- try {
- requestDetailedData(builder);
- performConnected(builder.getTransaction());
- } catch (IOException e) {
- GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
- }
- }
- }
-
- private void handleDetailedData(byte[] value) {
- int category, intensity, steps = 0;
-
- if (value.length != 16) {
- LOG.warn("GOT UNEXPECTED SENSOR DATA WITH LENGTH: " + value.length);
- for (byte b : value) {
- LOG.warn("DATA: " + String.format("0x%4x", b));
- }
- } else {
- try (DBHandler dbHandler = GBApplication.acquireDB()) {
- XWatchSampleProvider provider = new XWatchSampleProvider(getDevice(), dbHandler.getDaoSession());
- User user = DBHelper.getUser(dbHandler.getDaoSession());
- Device device = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession());
- int timestampInSeconds = 0;
-
- timestampInSeconds = getTimestampFromData(
- value[2],
- value[3],
- value[4],
- value[5]
- );
-
- category = ActivityKind.TYPE_ACTIVITY;
- intensity = (value[7] & 255) + ((value[8] & 255) << 8);
- steps = (value[9] & 255) + ((value[10] & 255) << 8);
-
- XWatchActivitySample sample = createActivitySample(device, user, timestampInSeconds, provider);
- sample.setRawIntensity(intensity);
- sample.setSteps(steps);
- sample.setRawKind(category);
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("sample: " + sample);
- }
-
- provider.addGBActivitySample(sample);
-
- if (value[5] == 95) {
- dayToFetch++;
- if(dayToFetch <= maxDayToFetch) {
- try {
- builder = performInitialized("fetchActivityData");
- requestDetailedData(builder);
- performConnected(builder.getTransaction());
- } catch (IOException e) {
- GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
- }
- }
- }
- } catch (Exception ex) {
- GB.toast(getContext(), ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
- }
- }
- }
-
- private void handleButtonPressed(byte[] value) {
- long currentTimestamp = System.currentTimeMillis();
-
- AudioManager audioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
- if(audioManager.isWiredHeadsetOn()) {
- if (currentTimestamp - lastButtonTimestamp < 1000) {
- if (audioManager.isMusicActive()) {
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
- } else {
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
- }
- } else {
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
- audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
- }
- }
-
- lastButtonTimestamp = currentTimestamp;
- }
-
- @Override
- public void onAppConfiguration(UUID appUuid, String config, Integer id) {
- //Not supported
- }
-
- @Override
- public void onSetHeartRateMeasurementInterval(int seconds) {
- //Not supported
- }
-
- private void requestSummarizedData(TransactionBuilder builder) {
- byte[] fetch = new byte[]{(byte) XWatchService.COMMAND_ACTIVITY_TOTALS,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0};
-
- fetch = XWatchSupport.crcChecksum(fetch);
- builder.write(getCharacteristic(XWatchService.UUID_WRITE), fetch);
- }
-
- private void requestDetailedData(TransactionBuilder builder) {
- byte[] fetch = new byte[]{(byte) XWatchService.COMMAND_ACTIVITY_DATA,
- (byte) dayToFetch,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0,
- (byte) 0};
-
- fetch = XWatchSupport.crcChecksum(fetch);
- builder.write(getCharacteristic(XWatchService.UUID_WRITE), fetch);
- }
-
- private int getTimestampFromData(byte year, byte month, byte day, byte hoursminutes) {
- int timestamp = 0;
- int yearInt, monthInt, dayInt, hoursMinutesInt = 0;
- int hours, minutes = 0;
-
- yearInt = Integer.valueOf(String.format("%02x", year), 16);
- monthInt = Integer.valueOf(String.format("%02x", month), 16);
- dayInt = Integer.valueOf(String.format("%02x", day), 16);
- hoursMinutesInt = Integer.valueOf(String.format("%02x", hoursminutes), 16);
-
- minutes = hoursMinutesInt % 4;
- hours = (hoursMinutesInt - minutes) / 4;
- minutes = minutes * 15;
-
- GregorianCalendar cal = new GregorianCalendar(
- 2000 + yearInt,
- monthInt - 1,
- dayInt,
- hours,
- minutes
- );
-
- timestamp = (int)(cal.getTimeInMillis() / 1000);
-
- return timestamp;
- }
-}
+/* Copyright (C) 2018 Andreas Shimokawa, ladbsoft
+
+ 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 . */
+package nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.content.Context;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.view.KeyEvent;
+import android.widget.Toast;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
+import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
+import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchService;
+import nodomain.freeyourgadget.gadgetbridge.entities.Device;
+import nodomain.freeyourgadget.gadgetbridge.entities.User;
+import nodomain.freeyourgadget.gadgetbridge.entities.XWatchActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
+import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
+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.btle.actions.SetDeviceStateAction;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+
+public class XWatchSupport extends AbstractBTLEDeviceSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(XWatchSupport.class);
+ private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
+ TransactionBuilder builder = null;
+ private DeviceInfo mDeviceInfo;
+ private byte dayToFetch; //0 = Today; 1 = Yesterday ...
+ private byte maxDayToFetch;
+ long lastButtonTimestamp;
+
+ public XWatchSupport() {
+ super(LOG);
+
+ addSupportedService(XWatchService.UUID_SERVICE);
+ addSupportedService(XWatchService.UUID_WRITE);
+ addSupportedService(XWatchService.UUID_NOTIFY);
+ }
+
+ public static byte[] crcChecksum(byte[] data) {
+ byte[] return_data = new byte[(data.length + 1)];
+ byte checksum = 0;
+
+ for (int i = 0; i < data.length; i++) {
+ return_data[i] = data[i];
+ checksum += data[i];
+ }
+ return_data[return_data.length - 1] = checksum;
+
+ return return_data;
+ }
+
+ @Override
+ protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
+ builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext()));
+
+ enableNotifications(builder)
+ .setDateTime(builder)
+ .setInitialized(builder);
+
+ return builder;
+ }
+
+ /**
+ * Last action of initialization sequence. Sets the device to initialized.
+ * It is only invoked if all other actions were successfully run, so the device
+ * must be initialized, then.
+ *
+ * @param builder
+ */
+ private void setInitialized(TransactionBuilder builder) {
+ builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZED, getContext()));
+ }
+
+ @Override
+ public boolean useAutoConnect() {
+ return true;
+ }
+
+ @Override
+ public boolean connectFirstTime() {
+ for (int i = 0; i < 5; i++) {
+ if (connect()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private XWatchSupport setDateTime(TransactionBuilder builder) {
+ byte[] data;
+
+ LOG.debug("Sending current date to the XWatch");
+ BluetoothGattCharacteristic deviceData = getCharacteristic(XWatchService.UUID_WRITE);
+
+ String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+ String y = time.substring(2, 4);
+ String M = time.substring(4, 6);
+ String d = time.substring(6, 8);
+ String H = time.substring(8, 10);
+ String m = time.substring(10, 12);
+ String s = time.substring(12, 14);
+ System.out.println(y + ":" + M + ":" + d + ":" + H + ":" + m + ":" + time.substring(12, 14));
+
+ data = new byte[]{(byte) 1,
+ (byte) Integer.parseInt(y, 16),
+ (byte) Integer.parseInt(M, 16),
+ (byte) Integer.parseInt(d, 16),
+ (byte) Integer.parseInt(H, 16),
+ (byte) Integer.parseInt(m, 16),
+ (byte) Integer.parseInt(s, 16),
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0};
+
+ data = crcChecksum(data);
+
+ builder.write(deviceData, data);
+
+ return this;
+ }
+
+ private XWatchSupport enableNotifications(TransactionBuilder builder) {
+ LOG.debug("Enabling action button");
+ BluetoothGattCharacteristic deviceInfo = getCharacteristic(XWatchService.UUID_NOTIFY);
+ builder.notify(deviceInfo, true);
+ return this;
+ }
+
+ @Override
+ public void onEnableHeartRateSleepSupport(boolean enable) {
+ //Not supported
+ }
+
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ // not supported
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+ // not supported
+ }
+
+ @Override
+ public void onSetAlarms(ArrayList extends Alarm> alarms) {
+ //TODO: Implement
+ }
+
+ @Override
+ public void onNotification(NotificationSpec notificationSpec) {
+ //TODO: Implement
+ }
+
+ @Override
+ public void onDeleteNotification(int id) {
+ //TODO: Implement
+ }
+
+ @Override
+ public void onSetTime() {
+ try {
+ TransactionBuilder builder = performInitialized("Set date and time");
+ setDateTime(builder);
+ builder.queue(getQueue());
+ } catch (IOException ex) {
+ LOG.error("Unable to set time and date on XWatch device", ex);
+ }
+ }
+
+ @Override
+ public void onSetCallState(CallSpec callSpec) {
+ //TODO: Implement (if necessary)
+ }
+
+ @Override
+ public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
+ }
+
+ @Override
+ public void onSetMusicState(MusicStateSpec stateSpec) {
+ // not supported
+ }
+
+ @Override
+ public void onSetMusicInfo(MusicSpec musicSpec) {
+ // not supported
+ }
+
+ @Override
+ public void onReboot() {
+ //Not supported
+ }
+
+ @Override
+ public void onHeartRateTest() {
+ //Not supported
+ }
+
+ @Override
+ public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
+ //Not supported
+ }
+
+ @Override
+ public void onFindDevice(boolean start) {
+ //TODO: Implement
+ }
+
+ @Override
+ public void onSetConstantVibration(int intensity) {
+ //TODO: Implement
+ }
+
+ @Override
+ public void onFetchRecordedData(int dataTypes) {
+ try {
+ if(builder == null) {
+ builder = performInitialized("fetchActivityData");
+ }
+ requestSummarizedData(builder);
+ performConnected(builder.getTransaction());
+ } catch (IOException e) {
+ GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
+ }
+ }
+
+ @Override
+ public void onEnableRealtimeSteps(boolean enable) {
+ //Not supported
+ }
+
+ @Override
+ public void onInstallApp(Uri uri) {
+ //Not supported
+ }
+
+ @Override
+ public void onAppInfoReq() {
+ // not supported
+ }
+
+ @Override
+ public void onAppStart(UUID uuid, boolean start) {
+ // not supported
+ }
+
+ @Override
+ public void onAppDelete(UUID uuid) {
+ // not supported
+ }
+
+ @Override
+ public void onAppReorder(UUID[] uuids) {
+ // not supported
+ }
+
+ @Override
+ public void onScreenshotReq() {
+ // not supported
+ }
+
+ @Override
+ public boolean onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
+ super.onCharacteristicChanged(gatt, characteristic);
+
+ UUID characteristicUUID = characteristic.getUuid();
+ if (XWatchService.UUID_NOTIFY.equals(characteristicUUID)) {
+ byte[] data = characteristic.getValue();
+ if (data[0] == XWatchService.COMMAND_ACTIVITY_TOTALS) {
+ handleSummarizedData(characteristic.getValue());
+ } else if (data[0] == XWatchService.COMMAND_ACTIVITY_DATA) {
+ handleDetailedData(characteristic.getValue());
+ } else if (data[0] == XWatchService.COMMAND_ACTION_BUTTON) {
+ handleButtonPressed(characteristic.getValue());
+ } else if (data[0] == XWatchService.COMMAND_CONNECTED) {
+ handleDeviceInfo(data, BluetoothGatt.GATT_SUCCESS);
+ } else {
+ LOG.info("Handled characteristic with unknown data: " + characteristicUUID);
+ logMessageContent(characteristic.getValue());
+ }
+ } else {
+ LOG.info("Unhandled characteristic changed: " + characteristicUUID);
+ logMessageContent(characteristic.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onCharacteristicRead(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
+ return super.onCharacteristicChanged(gatt, characteristic);
+ //TODO: Implement (if necessary)
+ }
+
+ @Override
+ public boolean onCharacteristicWrite(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
+ return super.onCharacteristicWrite(gatt, characteristic, status);
+ //TODO: Implement (if necessary)
+ }
+
+ public XWatchActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) {
+ XWatchActivitySample sample = new XWatchActivitySample();
+ sample.setDevice(device);
+ sample.setUser(user);
+ sample.setTimestamp(timestampInSeconds);
+ sample.setProvider(provider);
+
+ return sample;
+ }
+
+ private void handleDeviceInfo(byte[] value, int status) {
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ mDeviceInfo = new DeviceInfo(value);
+ LOG.warn("Device info: " + mDeviceInfo);
+ versionCmd.hwVersion = "1.0";
+ versionCmd.fwVersion = "1.0";
+ handleGBDeviceEvent(versionCmd);
+ }
+ }
+
+ @Override
+ public void onSendConfiguration(String config) {
+ // nothing yet
+ }
+
+ @Override
+ public void onTestNewFunction() {
+ //Not supported
+ }
+
+ @Override
+ public void onSendWeather(WeatherSpec weatherSpec) {
+ //Not supported
+ }
+
+ private void handleSummarizedData(byte[] value) {
+ int daysIntTotal;
+ int daysIntPart;
+
+ if (value.length != 16) {
+ LOG.warn("GOT UNEXPECTED SENSOR DATA WITH LENGTH: " + value.length);
+ for (byte b : value) {
+ LOG.warn("DATA: " + String.format("0x%4x", b));
+ }
+ } else {
+ daysIntPart = (value[1] & 255) << 24;
+ daysIntTotal = daysIntPart;
+ daysIntPart = (value[2] & 255) << 16;
+ daysIntTotal += daysIntPart;
+ daysIntPart = (value[3] & 255) << 8;
+ daysIntTotal += daysIntPart;
+ daysIntPart = (value[4] & 255);
+ daysIntTotal += daysIntPart;
+
+ dayToFetch = 0;
+ maxDayToFetch = (byte) Integer.bitCount(daysIntTotal);
+
+ try {
+ requestDetailedData(builder);
+ performConnected(builder.getTransaction());
+ } catch (IOException e) {
+ GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
+ }
+ }
+ }
+
+ private void handleDetailedData(byte[] value) {
+ int category, intensity, steps = 0;
+
+ if (value.length != 16) {
+ LOG.warn("GOT UNEXPECTED SENSOR DATA WITH LENGTH: " + value.length);
+ for (byte b : value) {
+ LOG.warn("DATA: " + String.format("0x%4x", b));
+ }
+ } else {
+ try (DBHandler dbHandler = GBApplication.acquireDB()) {
+ XWatchSampleProvider provider = new XWatchSampleProvider(getDevice(), dbHandler.getDaoSession());
+ User user = DBHelper.getUser(dbHandler.getDaoSession());
+ Device device = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession());
+ int timestampInSeconds = 0;
+
+ timestampInSeconds = getTimestampFromData(
+ value[2],
+ value[3],
+ value[4],
+ value[5]
+ );
+
+ category = ActivityKind.TYPE_ACTIVITY;
+ intensity = (value[7] & 255) + ((value[8] & 255) << 8);
+ steps = (value[9] & 255) + ((value[10] & 255) << 8);
+
+ XWatchActivitySample sample = createActivitySample(device, user, timestampInSeconds, provider);
+ sample.setRawIntensity(intensity);
+ sample.setSteps(steps);
+ sample.setRawKind(category);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("sample: " + sample);
+ }
+
+ provider.addGBActivitySample(sample);
+
+ if (value[5] == 95) {
+ dayToFetch++;
+ if(dayToFetch <= maxDayToFetch) {
+ try {
+ builder = performInitialized("fetchActivityData");
+ requestDetailedData(builder);
+ performConnected(builder.getTransaction());
+ } catch (IOException e) {
+ GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
+ }
+ }
+ }
+ } catch (Exception ex) {
+ GB.toast(getContext(), ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
+ }
+ }
+ }
+
+ private void handleButtonPressed(byte[] value) {
+ long currentTimestamp = System.currentTimeMillis();
+
+ AudioManager audioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
+ if(audioManager.isWiredHeadsetOn()) {
+ if (currentTimestamp - lastButtonTimestamp < 1000) {
+ if (audioManager.isMusicActive()) {
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
+ } else {
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
+ }
+ } else {
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+ audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+ }
+ }
+
+ lastButtonTimestamp = currentTimestamp;
+ }
+
+ @Override
+ public void onAppConfiguration(UUID appUuid, String config, Integer id) {
+ //Not supported
+ }
+
+ @Override
+ public void onSetHeartRateMeasurementInterval(int seconds) {
+ //Not supported
+ }
+
+ private void requestSummarizedData(TransactionBuilder builder) {
+ byte[] fetch = new byte[]{(byte) XWatchService.COMMAND_ACTIVITY_TOTALS,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0};
+
+ fetch = XWatchSupport.crcChecksum(fetch);
+ builder.write(getCharacteristic(XWatchService.UUID_WRITE), fetch);
+ }
+
+ private void requestDetailedData(TransactionBuilder builder) {
+ byte[] fetch = new byte[]{(byte) XWatchService.COMMAND_ACTIVITY_DATA,
+ (byte) dayToFetch,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0};
+
+ fetch = XWatchSupport.crcChecksum(fetch);
+ builder.write(getCharacteristic(XWatchService.UUID_WRITE), fetch);
+ }
+
+ private int getTimestampFromData(byte year, byte month, byte day, byte hoursminutes) {
+ int timestamp = 0;
+ int yearInt, monthInt, dayInt, hoursMinutesInt = 0;
+ int hours, minutes = 0;
+
+ yearInt = Integer.valueOf(String.format("%02x", year), 16);
+ monthInt = Integer.valueOf(String.format("%02x", month), 16);
+ dayInt = Integer.valueOf(String.format("%02x", day), 16);
+ hoursMinutesInt = Integer.valueOf(String.format("%02x", hoursminutes), 16);
+
+ minutes = hoursMinutesInt % 4;
+ hours = (hoursMinutesInt - minutes) / 4;
+ minutes = minutes * 15;
+
+ GregorianCalendar cal = new GregorianCalendar(
+ 2000 + yearInt,
+ monthInt - 1,
+ dayInt,
+ hours,
+ minutes
+ );
+
+ timestamp = (int)(cal.getTimeInMillis() / 1000);
+
+ return timestamp;
+ }
+}