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 442d644fe..c6f21498e 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 @@ -169,6 +169,7 @@ public class FossilWatchAdapter extends WatchAdapter { requestQueue.clear(); } log("characteristic write failed: " + status); + GB.toast(fossilRequest.getName() + " characteristic write failed: " + status, Toast.LENGTH_SHORT, GB.ERROR); fossilRequest = null; queueNextRequest(); @@ -583,7 +584,7 @@ public class FossilWatchAdapter extends WatchAdapter { requestFinished = fossilRequest.isFinished(); } catch (RuntimeException e) { if (characteristic.getUuid().toString().equals("3dda0005-957f-7d4a-34a6-74696673696d")) { - GB.log("authentication failed", GB.ERROR, null); + GB.log("authentication failed", GB.ERROR, e); // setDeviceState(GBDevice.State.AUTHENTICATION_REQUIRED); }else { GB.log("error", GB.ERROR, e); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java index ee7dfedcb..3ba3ee631 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java @@ -110,6 +110,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.application.ApplicationInformation; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.application.ApplicationsListRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.async.ConfirmAppStatusRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication.CheckDeviceNeedsConfirmationRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication.ConfirmOnDeviceRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication.VerifyPrivateKeyRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.buttons.ButtonConfiguration; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.buttons.ButtonConfigurationPutRequest; @@ -211,9 +213,44 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { private void initializeAfterAuthentication(boolean authenticated) { queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZING)); - if (!authenticated) + if (!authenticated) { GB.toast(getContext().getString(R.string.fossil_hr_auth_failed), Toast.LENGTH_LONG, GB.ERROR); + initializeAfterWatchConfirmation(false); + return; + } + boolean shouldAuthenticateOnWatch = getDeviceSpecificPreferences().getBoolean("enable_on_device_confirmation", true); + if(!shouldAuthenticateOnWatch){ + GB.toast("Skipping on-device confirmation", Toast.LENGTH_SHORT, GB.INFO); + initializeAfterWatchConfirmation(false); + return; + } + confirmOnWatch(); + } + private void confirmOnWatch() { + queueWrite(new CheckDeviceNeedsConfirmationRequest() { + @Override + public void onResult(boolean needsConfirmation) { + GB.log("needs confirmation: " + needsConfirmation, GB.INFO, null); + if(needsConfirmation){ + GB.toast("please confirm on device.", Toast.LENGTH_SHORT, GB.INFO); + queueWrite( new ConfirmOnDeviceRequest(){ + @Override + public void onResult(boolean confirmationSuccess) { + if(!confirmationSuccess){ + GB.toast("connection unconfirmed on watch, unauthenticated mode", Toast.LENGTH_LONG, GB.ERROR); + } + initializeAfterWatchConfirmation(confirmationSuccess); + } + }, true); + }else{ + initializeAfterWatchConfirmation(true); + } + } + }); + } + + private void initializeAfterWatchConfirmation(boolean authenticated){ setNotificationConfigurations(); setQuickRepliesConfiguration(); @@ -1224,22 +1261,23 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { @Override public void onTestNewFunction() { + queueWrite(new FossilRequest() { + @Override + public boolean isFinished() { + return true; + } - try{ - JSONObject data = new JSONObject() - .put("push", new JSONObject() - .put("set", new JSONObject() - .put("widgetCustom0._.config.upper_text", "0 up") - .put("widgetCustom0._.config.lower_text", "0 low") + @Override + public byte[] getStartSequence() { + return new byte[]{0x01, 0x07}; + } - .put("widgetCustom1._.config.upper_text", "1 up") - .put("widgetCustom1._.config.lower_text", "1 low") - ) - ); - queueWrite(new JsonPutRequest(data, this)); - }catch (Exception e) { - e.printStackTrace(); - } + @Override + public UUID getRequestUUID() { + return UUID.fromString("3dda0005-957f-7d4a-34a6-74696673696d"); + } + }); + queueWrite(new ConfirmOnDeviceRequest()); } public byte[] getSecretKey() throws IllegalAccessException { @@ -1483,6 +1521,14 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { } } + @Override + public void onFindDevice(boolean start) { + super.onFindDevice(start); + if(start){ + queueWrite(new ConfirmOnDeviceRequest()); + } + } + private void handleDeleteNotification(byte[] value) { ByteBuffer buffer = ByteBuffer.wrap(value); buffer.order(ByteOrder.LITTLE_ENDIAN); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/AuthenticationRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/AuthenticationRequest.java new file mode 100644 index 000000000..94a38b062 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/AuthenticationRequest.java @@ -0,0 +1,12 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication; + +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest; + +public abstract class AuthenticationRequest extends FossilRequest { + @Override + public UUID getRequestUUID() { + return UUID.fromString("3dda0005-957f-7d4a-34a6-74696673696d"); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/CheckDeviceNeedsConfirmationRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/CheckDeviceNeedsConfirmationRequest.java new file mode 100644 index 000000000..38d49f949 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/CheckDeviceNeedsConfirmationRequest.java @@ -0,0 +1,34 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication; + +import android.bluetooth.BluetoothGattCharacteristic; + +public abstract class CheckDeviceNeedsConfirmationRequest extends AuthenticationRequest { + private boolean isFinished = false; + @Override + public byte[] getStartSequence() { + return new byte[]{0x01, 0x07}; + } + + @Override + public void handleResponse(BluetoothGattCharacteristic characteristic) { + if(!characteristic.getUuid().equals(getRequestUUID())){ + throw new RuntimeException("wrong characteristic responded to authentication"); + } + byte[] value = characteristic.getValue(); + if(value.length != 3){ + throw new RuntimeException("wrong authentication response length"); + } + if(value[0] != 0x03 || value[1] != 0x07){ + throw new RuntimeException("wrong authentication response bytes"); + } + this.onResult(value[2] == 0x00); + this.isFinished = true; + } + + public abstract void onResult(boolean needsConfirmation); + + @Override + public boolean isFinished() { + return isFinished; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/ConfirmOnDeviceRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/ConfirmOnDeviceRequest.java new file mode 100644 index 000000000..fc9012b78 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/ConfirmOnDeviceRequest.java @@ -0,0 +1,35 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication; + +import android.bluetooth.BluetoothGattCharacteristic; + +public class ConfirmOnDeviceRequest extends AuthenticationRequest { + protected boolean isFinished = false; + + @Override + public byte[] getStartSequence() { + return new byte[]{0x02, 0x06, 0x30, 0x75, 0x00, 0x00, 0x00}; + } + + @Override + public void handleResponse(BluetoothGattCharacteristic characteristic) { + if(!characteristic.getUuid().equals(getRequestUUID())){ + throw new RuntimeException("wrong characteristic responded to authentication"); + } + byte[] value = characteristic.getValue(); + if(value.length != 4){ + throw new RuntimeException("wrong authentication response length"); + } + if(value[0] != 0x03 || value[1] != 0x06 || value[2] != 0x00){ + throw new RuntimeException("wrong authentication response bytes"); + } + this.onResult(value[3] == 0x01); + this.isFinished = true; + } + + public void onResult(boolean confirmationSuccess){}; + + @Override + public boolean isFinished() { + return isFinished; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java index 78865ea2d..abbe36730 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java @@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.foss import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode; -public class VerifyPrivateKeyRequest extends FossilRequest { +public class VerifyPrivateKeyRequest extends AuthenticationRequest { private final FossilHRWatchAdapter adapter; private byte[] key, randomPhoneNumber; private boolean isFinished = false; @@ -132,9 +132,4 @@ public class VerifyPrivateKeyRequest extends FossilRequest { return buffer.array(); } - - @Override - public UUID getRequestUUID() { - return UUID.fromString("3dda0005-957f-7d4a-34a6-74696673696d"); - } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 248e01fed..4bf0c53a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1392,5 +1392,7 @@ Update timeout in minutes: Hide text on timeout: Show circle on timeout: + Enable on-device pairing confirmation + On-device pairing confirmations can get annoying. Disabling them might lose you functionality. diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr.xml index 3fcafff70..5332ca6fd 100644 --- a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml +++ b/app/src/main/res/xml/devicesettings_fossilhybridhr.xml @@ -124,6 +124,12 @@ android:targetClass="nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.FileManagementActivity" /> + +