diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridAppChoserActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridAppChoserActivity.java index 2078377c1..c2035c092 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridAppChoserActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridAppChoserActivity.java @@ -123,6 +123,7 @@ public class QHybridAppChoserActivity extends AbstractGBActivity { if(success){ try { helper.saveNotificationConfiguration(config); + LocalBroadcastManager.getInstance(QHybridAppChoserActivity.this).sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_NOTIFICATION_CONFIG_CHANGED)); } catch (GBException e) { e.printStackTrace(); GB.toast("error saving configuration", Toast.LENGTH_SHORT, GB.ERROR, e); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java index fb0ca45a6..772f475ca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java @@ -26,6 +26,7 @@ import java.io.StringWriter; import java.util.HashMap; import java.util.UUID; +import androidx.annotation.RequiresApi; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -294,7 +295,7 @@ public class QHybridSupport extends QHybridBaseSupport { showNotificationsByAllActive(true); } - private void showNotificationsByAllActive(boolean enforceByNotification) { + private void showNotificationsByAllActive(boolean enforceByNotification) { if (!this.useActivityHand) return; double progress = calculateNotificationProgress(); showNotificationCountOnActivityHand(progress); @@ -342,6 +343,13 @@ public class QHybridSupport extends QHybridBaseSupport { } } + @Override + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + super.onMtuChanged(gatt, mtu, status); + if(watchAdapter == null) return; + watchAdapter.onMtuChanged(gatt, mtu, status); + } + private void playNotification(NotificationConfiguration config) { if (config.getMin() == -1 && config.getHour() == -1 && config.getVibration() == PlayNotificationRequest.VibrationType.NO_VIBE) return; @@ -508,6 +516,7 @@ public class QHybridSupport extends QHybridBaseSupport { @Override public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + if(watchAdapter == null) return super.onCharacteristicChanged(gatt, characteristic); return watchAdapter.onCharacteristicChanged(gatt, characteristic); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java index 663bd1d64..1c34058ee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java @@ -48,7 +48,7 @@ public abstract class WatchAdapter { public abstract void onFetchActivityData(); public abstract boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); - + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status){}; public String arrayToString(byte[] bytes) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java index 304c8fa50..31fda4c52 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java @@ -2,16 +2,21 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fos import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.os.Build; import android.util.Log; import android.widget.Toast; +import androidx.annotation.RequiresApi; + import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; +import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.PackageConfigHelper; import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilter; @@ -22,11 +27,15 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSuppo import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.WatchAdapter; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Request; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.RequestMtuRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.SetDeviceStateRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationGetRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.connection.SetConnectionParametersRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileCloseRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileDeleteRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileVerifyRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.NotificationFilterPutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayNotificationRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.AnimationRequest; @@ -49,11 +58,13 @@ public class FossilWatchAdapter extends WatchAdapter { public void initialize() { // playPairingAnimation(); // queueWrite(new FileDeleteRequest((short) 0x0200)); - // queueWrite(new ConfigurationGetRequest(this)); -// - // syncNotificationSettings(); - getDeviceSupport().getDevice().setState(GBDevice.State.INITIALIZED); - getDeviceSupport().getDevice().sendDeviceUpdateIntent(getContext()); + queueWrite(new RequestMtuRequest(512)); + queueWrite(new ConfigurationGetRequest(this)); + // queueWrite(new SetConnectionParametersRequest()); + + syncNotificationSettings(); + + queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED)); } @Override @@ -139,14 +150,13 @@ public class FossilWatchAdapter extends WatchAdapter { @Override public void setVibrationStrength(short strength) { + ConfigurationPutRequest.ConfigItem vibrationItem = new ConfigurationPutRequest.VibrationStrengthConfigItem((byte)strength); + + queueWrite( - new ConfigurationPutRequest( - new ConfigurationPutRequest.VibrationStrengthConfigItem( - (byte) strength - ), - this - ) + new ConfigurationPutRequest(new ConfigurationPutRequest.ConfigItem[]{vibrationItem, vibrationItem, vibrationItem}, this) ); + // queueWrite(new FileVerifyRequest((short) 0x0800)); } @Override @@ -179,6 +189,7 @@ public class FossilWatchAdapter extends WatchAdapter { @Override public void onTestNewFunction() { + queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.ConfigItem[0], this)); } @Override @@ -226,7 +237,8 @@ public class FossilWatchAdapter extends WatchAdapter { public void onFetchActivityData() { // queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.ConfigItem[0], this)); - queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.VibrationStrengthConfigItem((byte) 100), this)); + setVibrationStrength((byte) 50); + // queueWrite(new FileCloseRequest((short) 0x0800)); // queueWrite(new ConfigurationGetRequest(this)); } @@ -237,14 +249,20 @@ public class FossilWatchAdapter extends WatchAdapter { handleBackgroundCharacteristic(characteristic); break; } - case "3dda0002-957f-7d4a-34a6-74696673696d": { - break; - } + case "3dda0002-957f-7d4a-34a6-74696673696d": case "3dda0004-957f-7d4a-34a6-74696673696d": case "3dda0003-957f-7d4a-34a6-74696673696d": { if (fossilRequest != null) { boolean requestFinished; try { + if(characteristic.getUuid().toString().equals("3dda0003-957f-7d4a-34a6-74696673696d")){ + byte requestType = (byte)(characteristic.getValue()[0] & 0x0F); + + if(requestType != 0x0A && requestType != fossilRequest.getType()){ + // throw new RuntimeException("Answer type " + requestType + " does not match current request " + fossilRequest.getType()); + } + } + fossilRequest.handleResponse(characteristic); requestFinished = fossilRequest.isFinished(); } catch (RuntimeException e) { @@ -294,14 +312,44 @@ public class FossilWatchAdapter extends WatchAdapter { this.queueWrite(request, false); } + @Override + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + super.onMtuChanged(gatt, mtu, status); + ((RequestMtuRequest)fossilRequest).setFinished(true); + try { + queueWrite(requestQueue.remove(0)); + } catch (IndexOutOfBoundsException e) { + } + } + + //TODO split to multiple methods instead of switch public void queueWrite(Request request, boolean priorise) { - if (request.isBasicRequest()) { + if(request instanceof RequestMtuRequest){ + //TODO mtu on older devices + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + new TransactionBuilder("requestMtu") + .requestMtu(512) + .queue(getDeviceSupport().getQueue()); + + this.fossilRequest = (FossilRequest) request; + } + return; + }else if(request instanceof SetDeviceStateRequest){ + log("setting device state: " + ((SetDeviceStateRequest)request).getDeviceState()); + getDeviceSupport().getDevice().setState(((SetDeviceStateRequest)request).getDeviceState()); + getDeviceSupport().getDevice().sendDeviceUpdateIntent(getContext()); + try { + queueWrite(requestQueue.remove(0)); + } catch (IndexOutOfBoundsException e) { + } + return; + } else if (request.isBasicRequest()) { try { queueWrite(requestQueue.remove(0)); } catch (IndexOutOfBoundsException e) { } } else { - if (fossilRequest != null) { + if (fossilRequest != null && !fossilRequest.isFinished()) { log("queing request: " + request.getName()); if (priorise) { requestQueue.add(0, request); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/FossilRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/FossilRequest.java index 5a4ba18b6..474b65930 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/FossilRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/FossilRequest.java @@ -7,6 +7,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Req public abstract class FossilRequest extends Request { public abstract boolean isFinished(); + public byte getType(){ + return getStartSequence()[0]; + } @Override public UUID getRequestUUID() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/RequestMtuRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/RequestMtuRequest.java new file mode 100644 index 000000000..1fa05887f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/RequestMtuRequest.java @@ -0,0 +1,28 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil; + +public class RequestMtuRequest extends FossilRequest { + private int mtu; + private boolean finished = false; + + public RequestMtuRequest(int mtu) { + this.mtu = mtu; + } + + public int getMtu() { + return mtu; + } + + @Override + public boolean isFinished() { + return finished; + } + + public void setFinished(boolean finished) { + this.finished = finished; + } + + @Override + public byte[] getStartSequence() { + return new byte[0]; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/SetDeviceStateRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/SetDeviceStateRequest.java new file mode 100644 index 000000000..259c20813 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/SetDeviceStateRequest.java @@ -0,0 +1,25 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public class SetDeviceStateRequest extends FossilRequest { + private GBDevice.State deviceState; + + public SetDeviceStateRequest(GBDevice.State deviceState) { + this.deviceState = deviceState; + } + + public GBDevice.State getDeviceState() { + return deviceState; + } + + @Override + public boolean isFinished() { + return true; + } + + @Override + public byte[] getStartSequence() { + return new byte[0]; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/connection/SetConnectionParametersRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/connection/SetConnectionParametersRequest.java new file mode 100644 index 000000000..1e652f4f2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/connection/SetConnectionParametersRequest.java @@ -0,0 +1,37 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.connection; + +import android.bluetooth.BluetoothGattCharacteristic; + +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest; + +public class SetConnectionParametersRequest extends FossilRequest { + private boolean finished = false; + + @Override + public boolean isFinished() { + return finished; + } + + @Override + public UUID getRequestUUID() { + return UUID.fromString("3dda0002-957f-7d4a-34a6-74696673696d"); + } + + @Override + public void handleResponse(BluetoothGattCharacteristic characteristic) { + super.handleResponse(characteristic); + this.finished = true; + } + + @Override + public byte[] getStartSequence() { + return new byte[]{0x02, 0x09, 0x0C, 0x00, 0x0C, 0x00, 0x2D, 0x00, 0x58, 0x02}; + } + + @Override + public boolean isBasicRequest() { + return false; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java index 3790084a7..ec11b89e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java @@ -66,7 +66,7 @@ public class FileLookupRequest extends FossilRequest { byte status = buffer.get(3); if(status != 0){ - throw new RuntimeException("FileGet error: " + status); + throw new RuntimeException("file lookup error: " + status); } if(this.handle != handle){ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileVerifyRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileVerifyRequest.java new file mode 100644 index 000000000..c3e355838 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileVerifyRequest.java @@ -0,0 +1,75 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file; + +import android.bluetooth.BluetoothGattCharacteristic; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest; + +public class FileVerifyRequest extends FossilRequest { + private boolean isFinished = false; + private short handle; + + public FileVerifyRequest(short fileHandle) { + this.handle = fileHandle; + ByteBuffer buffer = this.createBuffer(); + buffer.putShort(fileHandle); + + this.data = buffer.array(); + } + + public short getHandle() { + return handle; + } + + @Override + public void handleResponse(BluetoothGattCharacteristic characteristic) { + super.handleResponse(characteristic); + + if(!characteristic.getUuid().toString().equals(this.getRequestUUID().toString())){ + throw new RuntimeException("wrong response UUID"); + } + + byte[] value = characteristic.getValue(); + + byte type = (byte)(value[0] & 0x0F); + + if(type == 0x0A) return; + + if(type != 4) throw new RuntimeException("wrong response type"); + + if(value.length != 4) throw new RuntimeException("wrong response length"); + + ByteBuffer buffer = ByteBuffer.wrap(value); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + if(this.handle != buffer.getShort(1)) throw new RuntimeException("wrong response handle"); + + byte status = buffer.get(3); + + if(status != 0) throw new RuntimeException("wrong response status"); + + this.isFinished = true; + + this.onPrepare(); + } + + public void onPrepare(){} + + @Override + public byte[] getStartSequence() { + return new byte[]{4}; + } + + @Override + public int getPayloadLength() { + return 3; + } + + @Override + public boolean isFinished(){ + return this.isFinished; + } + +}