mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-01 14:32:54 +01:00
Fossil HR: added infrastructure to display on-device confirmations (#2451)
This PR aims to add the on-device connection confirmation for the Fossil HR. This seems mandatory, since, at least on my watch, without said confirmation certain files like the configuration cannot be accesses, e.g. the time on the watch cannot be set etc. The mystery yet to be solved is how to get the watch to not ask for a confirmation on every new connection or reconnection attempt, since that can get very annoying. Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2451 Co-authored-by: dakhnod <dakhnod@noreply.codeberg.org> Co-committed-by: dakhnod <dakhnod@noreply.codeberg.org>
This commit is contained in:
parent
7d552ce41f
commit
4981aacb30
@ -169,6 +169,7 @@ public class FossilWatchAdapter extends WatchAdapter {
|
|||||||
requestQueue.clear();
|
requestQueue.clear();
|
||||||
}
|
}
|
||||||
log("characteristic write failed: " + status);
|
log("characteristic write failed: " + status);
|
||||||
|
GB.toast(fossilRequest.getName() + " characteristic write failed: " + status, Toast.LENGTH_SHORT, GB.ERROR);
|
||||||
fossilRequest = null;
|
fossilRequest = null;
|
||||||
|
|
||||||
queueNextRequest();
|
queueNextRequest();
|
||||||
@ -583,7 +584,7 @@ public class FossilWatchAdapter extends WatchAdapter {
|
|||||||
requestFinished = fossilRequest.isFinished();
|
requestFinished = fossilRequest.isFinished();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
if (characteristic.getUuid().toString().equals("3dda0005-957f-7d4a-34a6-74696673696d")) {
|
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);
|
// setDeviceState(GBDevice.State.AUTHENTICATION_REQUIRED);
|
||||||
}else {
|
}else {
|
||||||
GB.log("error", GB.ERROR, e);
|
GB.log("error", GB.ERROR, e);
|
||||||
|
@ -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.ApplicationInformation;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.application.ApplicationsListRequest;
|
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.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.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.ButtonConfiguration;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.buttons.ButtonConfigurationPutRequest;
|
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) {
|
private void initializeAfterAuthentication(boolean authenticated) {
|
||||||
queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZING));
|
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);
|
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();
|
setNotificationConfigurations();
|
||||||
setQuickRepliesConfiguration();
|
setQuickRepliesConfiguration();
|
||||||
|
|
||||||
@ -1224,22 +1261,23 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTestNewFunction() {
|
public void onTestNewFunction() {
|
||||||
|
queueWrite(new FossilRequest() {
|
||||||
try{
|
@Override
|
||||||
JSONObject data = new JSONObject()
|
public boolean isFinished() {
|
||||||
.put("push", new JSONObject()
|
return true;
|
||||||
.put("set", new JSONObject()
|
|
||||||
.put("widgetCustom0._.config.upper_text", "0 up")
|
|
||||||
.put("widgetCustom0._.config.lower_text", "0 low")
|
|
||||||
|
|
||||||
.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 byte[] getStartSequence() {
|
||||||
|
return new byte[]{0x01, 0x07};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getRequestUUID() {
|
||||||
|
return UUID.fromString("3dda0005-957f-7d4a-34a6-74696673696d");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
queueWrite(new ConfirmOnDeviceRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getSecretKey() throws IllegalAccessException {
|
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) {
|
private void handleDeleteNotification(byte[] value) {
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(value);
|
ByteBuffer buffer = ByteBuffer.wrap(value);
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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.FossilRequest;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode;
|
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 final FossilHRWatchAdapter adapter;
|
||||||
private byte[] key, randomPhoneNumber;
|
private byte[] key, randomPhoneNumber;
|
||||||
private boolean isFinished = false;
|
private boolean isFinished = false;
|
||||||
@ -132,9 +132,4 @@ public class VerifyPrivateKeyRequest extends FossilRequest {
|
|||||||
|
|
||||||
return buffer.array();
|
return buffer.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID getRequestUUID() {
|
|
||||||
return UUID.fromString("3dda0005-957f-7d4a-34a6-74696673696d");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1392,5 +1392,7 @@
|
|||||||
<string name="watchface_dialog_widget_update_timeout">Update timeout in minutes:</string>
|
<string name="watchface_dialog_widget_update_timeout">Update timeout in minutes:</string>
|
||||||
<string name="watchface_dialog_widget_timeout_hide_text">Hide text on timeout:</string>
|
<string name="watchface_dialog_widget_timeout_hide_text">Hide text on timeout:</string>
|
||||||
<string name="watchface_dialog_widget_timeout_show_circle">Show circle on timeout:</string>
|
<string name="watchface_dialog_widget_timeout_show_circle">Show circle on timeout:</string>
|
||||||
|
<string name="qhybrid_title_on_device_confirmation">Enable on-device pairing confirmation</string>
|
||||||
|
<string name="qhybrid_summary_on_device_confirmation">On-device pairing confirmations can get annoying. Disabling them might lose you functionality.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -124,6 +124,12 @@
|
|||||||
android:targetClass="nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.FileManagementActivity" />
|
android:targetClass="nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.FileManagementActivity" />
|
||||||
</Preference>
|
</Preference>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:key="enable_on_device_confirmation"
|
||||||
|
android:title="@string/qhybrid_title_on_device_confirmation"
|
||||||
|
android:summary="@string/qhybrid_summary_on_device_confirmation" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen>
|
</androidx.preference.PreferenceScreen>
|
||||||
|
Loading…
Reference in New Issue
Block a user